diff --git a/crates/ruff_linter/resources/test/fixtures/flake8_gettext/INT001.py b/crates/ruff_linter/resources/test/fixtures/flake8_gettext/INT001.py index 703ad7df4b..240213f485 100644 --- a/crates/ruff_linter/resources/test/fixtures/flake8_gettext/INT001.py +++ b/crates/ruff_linter/resources/test/fixtures/flake8_gettext/INT001.py @@ -1 +1,4 @@ _(f"{'value'}") + +# Don't trigger for t-strings +_(t"{'value'}") diff --git a/crates/ruff_linter/resources/test/fixtures/flake8_logging_format/G004.py b/crates/ruff_linter/resources/test/fixtures/flake8_logging_format/G004.py index e339006435..da05aba630 100644 --- a/crates/ruff_linter/resources/test/fixtures/flake8_logging_format/G004.py +++ b/crates/ruff_linter/resources/test/fixtures/flake8_logging_format/G004.py @@ -13,3 +13,7 @@ from logging import info info(f"{name}") info(f"{__name__}") + +# Don't trigger for t-strings +info(t"{name}") +info(t"{__name__}") diff --git a/crates/ruff_linter/resources/test/fixtures/flake8_pytest_style/PT011.py b/crates/ruff_linter/resources/test/fixtures/flake8_pytest_style/PT011.py index 368cea6af6..e524935473 100644 --- a/crates/ruff_linter/resources/test/fixtures/flake8_pytest_style/PT011.py +++ b/crates/ruff_linter/resources/test/fixtures/flake8_pytest_style/PT011.py @@ -47,3 +47,7 @@ def test_error_match_is_empty(): with pytest.raises(ValueError, match=f""): raise ValueError("Can't divide 1 by 0") + +def test_ok_t_string_match(): + with pytest.raises(ValueError, match=t""): + raise ValueError("Can't divide 1 by 0") diff --git a/crates/ruff_linter/resources/test/fixtures/flake8_pytest_style/PT016.py b/crates/ruff_linter/resources/test/fixtures/flake8_pytest_style/PT016.py index 288d5d9be6..0a8f6bc8fb 100644 --- a/crates/ruff_linter/resources/test/fixtures/flake8_pytest_style/PT016.py +++ b/crates/ruff_linter/resources/test/fixtures/flake8_pytest_style/PT016.py @@ -23,3 +23,9 @@ def f(): pytest.fail(msg=f"") pytest.fail(reason="") pytest.fail(reason=f"") + +# Skip for t-strings +def g(): + pytest.fail(t"") + pytest.fail(msg=t"") + pytest.fail(reason=t"") diff --git a/crates/ruff_linter/resources/test/fixtures/flake8_pytest_style/PT030.py b/crates/ruff_linter/resources/test/fixtures/flake8_pytest_style/PT030.py index 129f1f0442..2db3885b6d 100644 --- a/crates/ruff_linter/resources/test/fixtures/flake8_pytest_style/PT030.py +++ b/crates/ruff_linter/resources/test/fixtures/flake8_pytest_style/PT030.py @@ -32,3 +32,7 @@ def test_error_match_is_empty(): with pytest.warns(UserWarning, match=f""): pass + +def test_ok_match_t_string(): + with pytest.warns(UserWarning, match=t""): + pass diff --git a/crates/ruff_linter/resources/test/fixtures/flake8_unused_arguments/ARG.py b/crates/ruff_linter/resources/test/fixtures/flake8_unused_arguments/ARG.py index 7cfb3fceee..311409e6ff 100644 --- a/crates/ruff_linter/resources/test/fixtures/flake8_unused_arguments/ARG.py +++ b/crates/ruff_linter/resources/test/fixtures/flake8_unused_arguments/ARG.py @@ -245,3 +245,14 @@ def f(bar: str): class C: def __init__(self, x) -> None: print(locals()) + +### +# Should trigger for t-string here +# even though the corresponding f-string +# does not trigger (since it is common in stubs) +### +class C: + def f(self, x, y): + """Docstring.""" + msg = t"{x}..." + raise NotImplementedError(msg) diff --git a/crates/ruff_linter/resources/test/fixtures/pylint/assert_on_string_literal.py b/crates/ruff_linter/resources/test/fixtures/pylint/assert_on_string_literal.py index 4c572c2767..380b502647 100644 --- a/crates/ruff_linter/resources/test/fixtures/pylint/assert_on_string_literal.py +++ b/crates/ruff_linter/resources/test/fixtures/pylint/assert_on_string_literal.py @@ -22,3 +22,10 @@ assert b"hello" # [assert-on-string-literal] assert "", b"hi" # [assert-on-string-literal] assert "WhyNotHere?", "HereIsOk" # [assert-on-string-literal] assert 12, "ok here" + + +# t-strings are always True even when "empty" +# skip lint in this case +assert t"" +assert t"hey" +assert t"{a}" diff --git a/crates/ruff_linter/resources/test/fixtures/pylint/no_self_use.py b/crates/ruff_linter/resources/test/fixtures/pylint/no_self_use.py index 34c4709505..c246792074 100644 --- a/crates/ruff_linter/resources/test/fixtures/pylint/no_self_use.py +++ b/crates/ruff_linter/resources/test/fixtures/pylint/no_self_use.py @@ -140,3 +140,15 @@ class Foo: def unused_message_2(self, x): msg = "" raise NotImplementedError(x) + +class TPerson: + def developer_greeting(self, name): # [no-self-use] + print(t"Greetings {name}!") + + def greeting_1(self): + print(t"Hello from {self.name} !") + + def tstring(self, x): + msg = t"{x}" + raise NotImplementedError(msg) + diff --git a/crates/ruff_linter/resources/test/fixtures/pylint/single_string_slots.py b/crates/ruff_linter/resources/test/fixtures/pylint/single_string_slots.py index b7a2dac915..58d6381390 100644 --- a/crates/ruff_linter/resources/test/fixtures/pylint/single_string_slots.py +++ b/crates/ruff_linter/resources/test/fixtures/pylint/single_string_slots.py @@ -33,3 +33,11 @@ class Foo: def __init__(self, bar): self.bar = bar + +# This is a type error, out of scope for the rule +class Foo: + __slots__ = t"bar{baz}" + + def __init__(self, bar): + self.bar = bar + diff --git a/crates/ruff_linter/resources/test/fixtures/pyupgrade/UP012.py b/crates/ruff_linter/resources/test/fixtures/pyupgrade/UP012.py index 0029b89e9b..2b7fdfa288 100644 --- a/crates/ruff_linter/resources/test/fixtures/pyupgrade/UP012.py +++ b/crates/ruff_linter/resources/test/fixtures/pyupgrade/UP012.py @@ -84,3 +84,7 @@ def _match_ignore(line): # Not a valid type annotation but this test shouldn't result in a panic. # Refer: https://github.com/astral-sh/ruff/issues/11736 x: '"foo".encode("utf-8")' + +# AttributeError for t-strings so skip lint +(t"foo{bar}").encode("utf-8") +(t"foo{bar}").encode(encoding="utf-8") diff --git a/crates/ruff_linter/resources/test/fixtures/pyupgrade/UP018.py b/crates/ruff_linter/resources/test/fixtures/pyupgrade/UP018.py index a5b7e1d894..86b8d9aebf 100644 --- a/crates/ruff_linter/resources/test/fixtures/pyupgrade/UP018.py +++ b/crates/ruff_linter/resources/test/fixtures/pyupgrade/UP018.py @@ -90,3 +90,7 @@ bool(True)and None int(1)and None float(1.)and None bool(True)and() + + +# t-strings are not native literals +str(t"hey") diff --git a/crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__B018_B018.py.snap b/crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__B018_B018.py.snap index d455292d93..5eee755b4a 100644 --- a/crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__B018_B018.py.snap +++ b/crates/ruff_linter/src/rules/flake8_bugbear/snapshots/ruff_linter__rules__flake8_bugbear__tests__B018_B018.py.snap @@ -1,6 +1,5 @@ --- source: crates/ruff_linter/src/rules/flake8_bugbear/mod.rs -snapshot_kind: text --- B018.py:11:5: B018 Found useless expression. Either assign it to a variable or remove it. | diff --git a/crates/ruff_linter/src/rules/flake8_gettext/snapshots/ruff_linter__rules__flake8_gettext__tests__f-string-in-get-text-func-call_INT001.py.snap b/crates/ruff_linter/src/rules/flake8_gettext/snapshots/ruff_linter__rules__flake8_gettext__tests__f-string-in-get-text-func-call_INT001.py.snap index 90495ebbad..81d2c6b328 100644 --- a/crates/ruff_linter/src/rules/flake8_gettext/snapshots/ruff_linter__rules__flake8_gettext__tests__f-string-in-get-text-func-call_INT001.py.snap +++ b/crates/ruff_linter/src/rules/flake8_gettext/snapshots/ruff_linter__rules__flake8_gettext__tests__f-string-in-get-text-func-call_INT001.py.snap @@ -1,9 +1,10 @@ --- source: crates/ruff_linter/src/rules/flake8_gettext/mod.rs -snapshot_kind: text --- INT001.py:1:3: INT001 f-string is resolved before function call; consider `_("string %s") % arg` | 1 | _(f"{'value'}") | ^^^^^^^^^^^^ INT001 +2 | +3 | # Don't trigger for t-strings | diff --git a/crates/ruff_linter/src/rules/flake8_logging_format/snapshots/ruff_linter__rules__flake8_logging_format__tests__G004.py.snap b/crates/ruff_linter/src/rules/flake8_logging_format/snapshots/ruff_linter__rules__flake8_logging_format__tests__G004.py.snap index 9cd4524dd9..bb51dc65f4 100644 --- a/crates/ruff_linter/src/rules/flake8_logging_format/snapshots/ruff_linter__rules__flake8_logging_format__tests__G004.py.snap +++ b/crates/ruff_linter/src/rules/flake8_logging_format/snapshots/ruff_linter__rules__flake8_logging_format__tests__G004.py.snap @@ -52,4 +52,6 @@ G004.py:15:6: G004 Logging statement uses f-string 14 | info(f"{name}") 15 | info(f"{__name__}") | ^^^^^^^^^^^^^ G004 +16 | +17 | # Don't trigger for t-strings | diff --git a/crates/ruff_linter/src/rules/flake8_pytest_style/snapshots/ruff_linter__rules__flake8_pytest_style__tests__PT016.snap b/crates/ruff_linter/src/rules/flake8_pytest_style/snapshots/ruff_linter__rules__flake8_pytest_style__tests__PT016.snap index ac5c1f3a36..4730ecdeca 100644 --- a/crates/ruff_linter/src/rules/flake8_pytest_style/snapshots/ruff_linter__rules__flake8_pytest_style__tests__PT016.snap +++ b/crates/ruff_linter/src/rules/flake8_pytest_style/snapshots/ruff_linter__rules__flake8_pytest_style__tests__PT016.snap @@ -1,6 +1,5 @@ --- source: crates/ruff_linter/src/rules/flake8_pytest_style/mod.rs -snapshot_kind: text --- PT016.py:19:5: PT016 No message passed to `pytest.fail()` | @@ -67,4 +66,6 @@ PT016.py:25:5: PT016 No message passed to `pytest.fail()` 24 | pytest.fail(reason="") 25 | pytest.fail(reason=f"") | ^^^^^^^^^^^ PT016 +26 | +27 | # Skip for t-strings | diff --git a/crates/ruff_linter/src/rules/flake8_unused_arguments/snapshots/ruff_linter__rules__flake8_unused_arguments__tests__ARG002_ARG.py.snap b/crates/ruff_linter/src/rules/flake8_unused_arguments/snapshots/ruff_linter__rules__flake8_unused_arguments__tests__ARG002_ARG.py.snap index 3e1e19260a..2c7fe70056 100644 --- a/crates/ruff_linter/src/rules/flake8_unused_arguments/snapshots/ruff_linter__rules__flake8_unused_arguments__tests__ARG002_ARG.py.snap +++ b/crates/ruff_linter/src/rules/flake8_unused_arguments/snapshots/ruff_linter__rules__flake8_unused_arguments__tests__ARG002_ARG.py.snap @@ -66,3 +66,13 @@ ARG.py:216:24: ARG002 Unused method argument: `x` | ^ ARG002 217 | print("Hello, world!") | + +ARG.py:255:20: ARG002 Unused method argument: `y` + | +253 | ### +254 | class C: +255 | def f(self, x, y): + | ^ ARG002 +256 | """Docstring.""" +257 | msg = t"{x}..." + | diff --git a/crates/ruff_linter/src/rules/pylint/snapshots/ruff_linter__rules__pylint__tests__PLR6301_no_self_use.py.snap b/crates/ruff_linter/src/rules/pylint/snapshots/ruff_linter__rules__pylint__tests__PLR6301_no_self_use.py.snap index 74818fdb39..4971a0c1fb 100644 --- a/crates/ruff_linter/src/rules/pylint/snapshots/ruff_linter__rules__pylint__tests__PLR6301_no_self_use.py.snap +++ b/crates/ruff_linter/src/rules/pylint/snapshots/ruff_linter__rules__pylint__tests__PLR6301_no_self_use.py.snap @@ -75,3 +75,21 @@ no_self_use.py:140:9: PLR6301 Method `unused_message_2` could be a function, cla 141 | msg = "" 142 | raise NotImplementedError(x) | + +no_self_use.py:145:9: PLR6301 Method `developer_greeting` could be a function, class method, or static method + | +144 | class TPerson: +145 | def developer_greeting(self, name): # [no-self-use] + | ^^^^^^^^^^^^^^^^^^ PLR6301 +146 | print(t"Greetings {name}!") + | + +no_self_use.py:151:9: PLR6301 Method `tstring` could be a function, class method, or static method + | +149 | print(t"Hello from {self.name} !") +150 | +151 | def tstring(self, x): + | ^^^^^^^ PLR6301 +152 | msg = t"{x}" +153 | raise NotImplementedError(msg) + | diff --git a/crates/ruff_linter/src/rules/pyupgrade/snapshots/ruff_linter__rules__pyupgrade__tests__UP012.py.snap b/crates/ruff_linter/src/rules/pyupgrade/snapshots/ruff_linter__rules__pyupgrade__tests__UP012.py.snap index 99b2f05d6e..a3e42b982f 100644 --- a/crates/ruff_linter/src/rules/pyupgrade/snapshots/ruff_linter__rules__pyupgrade__tests__UP012.py.snap +++ b/crates/ruff_linter/src/rules/pyupgrade/snapshots/ruff_linter__rules__pyupgrade__tests__UP012.py.snap @@ -576,6 +576,8 @@ UP012.py:86:5: UP012 [*] Unnecessary call to `encode` as UTF-8 85 | # Refer: https://github.com/astral-sh/ruff/issues/11736 86 | x: '"foo".encode("utf-8")' | ^^^^^^^^^^^^^^^^^^^^^ UP012 +87 | +88 | # AttributeError for t-strings so skip lint | = help: Rewrite as bytes literal @@ -585,3 +587,6 @@ UP012.py:86:5: UP012 [*] Unnecessary call to `encode` as UTF-8 85 85 | # Refer: https://github.com/astral-sh/ruff/issues/11736 86 |-x: '"foo".encode("utf-8")' 86 |+x: 'b"foo"' +87 87 | +88 88 | # AttributeError for t-strings so skip lint +89 89 | (t"foo{bar}").encode("utf-8") diff --git a/crates/ruff_linter/src/rules/pyupgrade/snapshots/ruff_linter__rules__pyupgrade__tests__UP018.py.snap b/crates/ruff_linter/src/rules/pyupgrade/snapshots/ruff_linter__rules__pyupgrade__tests__UP018.py.snap index d57eac6e12..0f6820140c 100644 --- a/crates/ruff_linter/src/rules/pyupgrade/snapshots/ruff_linter__rules__pyupgrade__tests__UP018.py.snap +++ b/crates/ruff_linter/src/rules/pyupgrade/snapshots/ruff_linter__rules__pyupgrade__tests__UP018.py.snap @@ -660,6 +660,7 @@ UP018.py:90:1: UP018 [*] Unnecessary `int` call (rewrite as a literal) 90 |+1 and None 91 91 | float(1.)and None 92 92 | bool(True)and() +93 93 | UP018.py:91:1: UP018 [*] Unnecessary `float` call (rewrite as a literal) | @@ -678,6 +679,8 @@ UP018.py:91:1: UP018 [*] Unnecessary `float` call (rewrite as a literal) 91 |-float(1.)and None 91 |+1. and None 92 92 | bool(True)and() +93 93 | +94 94 | UP018.py:92:1: UP018 [*] Unnecessary `bool` call (rewrite as a literal) | @@ -694,3 +697,6 @@ UP018.py:92:1: UP018 [*] Unnecessary `bool` call (rewrite as a literal) 91 91 | float(1.)and None 92 |-bool(True)and() 92 |+True and() +93 93 | +94 94 | +95 95 | # t-strings are not native literals