diff --git a/crates/ruff_linter/resources/test/fixtures/ruff/RUF052.py b/crates/ruff_linter/resources/test/fixtures/ruff/RUF052.py index dd90a2bfe8..390dbb489e 100644 --- a/crates/ruff_linter/resources/test/fixtures/ruff/RUF052.py +++ b/crates/ruff_linter/resources/test/fixtures/ruff/RUF052.py @@ -129,3 +129,19 @@ def unfixables(): # unfixable because the rename would shadow a variable from the outer function _local = "local4" print(_local) + +def special_calls(): + from typing import TypeVar, ParamSpec, NamedTuple + from enum import Enum + from collections import namedtuple + + _P = ParamSpec("_P") + _T = TypeVar(name="_T", covariant=True, bound=int|str) + _NT = NamedTuple("_NT", [("foo", int)]) + _E = Enum("_E", ["a", "b", "c"]) + _NT2 = namedtuple("_NT2", ['x', 'y', 'z']) + _NT3 = namedtuple(typename="_NT3", field_names=['x', 'y', 'z']) + _DynamicClass = type("_DynamicClass", (), {}) + _NotADynamicClass = type("_NotADynamicClass") + + print(_T, _P, _NT, _E, _NT2, _NT3, _DynamicClass, _NotADynamicClass) diff --git a/crates/ruff_linter/src/renamer.rs b/crates/ruff_linter/src/renamer.rs index 82c6966fd9..9d2c9d6c92 100644 --- a/crates/ruff_linter/src/renamer.rs +++ b/crates/ruff_linter/src/renamer.rs @@ -4,6 +4,7 @@ use anyhow::{anyhow, Result}; use itertools::Itertools; use ruff_diagnostics::Edit; +use ruff_python_ast as ast; use ruff_python_codegen::Stylist; use ruff_python_semantic::{Binding, BindingKind, Scope, ScopeId, SemanticModel}; use ruff_text_size::Ranged; @@ -69,6 +70,11 @@ impl Renamer { /// example, to rename `pandas` to `pd`, we may need to rewrite `import pandas` to /// `import pandas as pd`, rather than `import pd`. /// + /// 1. Check to see if the binding is assigned to a known special call where the first argument + /// must be a string that is the same as the binding's name. For example, + /// `T = TypeVar("_T")` will be rejected by a type checker; only `T = TypeVar("T")` will do. + /// If it *is* one of these calls, we rename the relevant argument as well. + /// /// 1. Rename every reference to the [`Binding`]. For example, renaming the references to the /// `x = 1` binding above would give us: /// @@ -198,6 +204,12 @@ impl Renamer { if let Some(edit) = Renamer::rename_binding(binding, name, target) { edits.push(edit); + if let Some(edit) = + Renamer::fixup_assigned_value(binding, semantic, stylist, name, target) + { + edits.push(edit); + } + // Rename any delayed annotations. if let Some(annotations) = semantic.delayed_annotations(binding_id) { edits.extend(annotations.iter().filter_map(|annotation_id| { @@ -231,6 +243,86 @@ impl Renamer { edits } + /// If the r.h.s. of a call expression is a call expression, + /// we may need to fixup some arguments passed to that call expression. + /// + /// It's impossible to do this entirely rigorously; + /// we only special-case some common standard-library constructors here. + /// + /// For example, in this `TypeVar` definition: + /// ```py + /// from typing import TypeVar + /// + /// _T = TypeVar("_T") + /// ``` + /// + /// If we're renaming it from `_T` to `T`, we want this to be the end result: + /// ```py + /// from typing import TypeVar + /// + /// T = TypeVar("T") + /// ``` + /// + /// Not this, which a type checker will reject: + /// ```py + /// from typing import TypeVar + /// + /// T = TypeVar("_T") + /// ``` + fn fixup_assigned_value( + binding: &Binding, + semantic: &SemanticModel, + stylist: &Stylist, + name: &str, + target: &str, + ) -> Option { + let statement = binding.statement(semantic)?; + + let (ast::Stmt::Assign(ast::StmtAssign { value, .. }) + | ast::Stmt::AnnAssign(ast::StmtAnnAssign { + value: Some(value), .. + })) = statement + else { + return None; + }; + + let ast::ExprCall { + func, arguments, .. + } = value.as_call_expr()?; + + let qualified_name = semantic.resolve_qualified_name(func)?; + + let name_argument = match qualified_name.segments() { + ["collections", "namedtuple"] => arguments.find_argument("typename", 0), + + ["typing" | "typing_extensions", "TypeVar" | "ParamSpec" | "TypeVarTuple" | "NewType" | "TypeAliasType"] => { + arguments.find_argument("name", 0) + } + + ["enum", "Enum" | "IntEnum" | "StrEnum" | "ReprEnum" | "Flag" | "IntFlag"] + | ["typing" | "typing_extensions", "NamedTuple" | "TypedDict"] => { + arguments.find_positional(0) + } + + ["builtins" | "", "type"] if arguments.len() == 3 => arguments.find_positional(0), + + _ => None, + }?; + + let name_argument = name_argument.as_string_literal_expr()?; + + if name_argument.value.to_str() != name { + return None; + } + + let quote = stylist.quote(); + + Some(Edit::range_replacement( + format!("{quote}{target}{quote}"), + name_argument.range(), + )) + } + /// Rename a [`Binding`] reference. fn rename_binding(binding: &Binding, name: &str, target: &str) -> Option { match &binding.kind { diff --git a/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__RUF052_RUF052.py.snap b/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__RUF052_RUF052.py.snap index eef08f0aeb..610a2fc251 100644 --- a/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__RUF052_RUF052.py.snap +++ b/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__RUF052_RUF052.py.snap @@ -148,3 +148,196 @@ RUF052.py:130:9: RUF052 Local dummy variable `_local` is accessed 131 | print(_local) | = help: Prefer using trailing underscores to avoid shadowing a variable + +RUF052.py:138:5: RUF052 [*] Local dummy variable `_P` is accessed + | +136 | from collections import namedtuple +137 | +138 | _P = ParamSpec("_P") + | ^^ RUF052 +139 | _T = TypeVar(name="_T", covariant=True, bound=int|str) +140 | _NT = NamedTuple("_NT", [("foo", int)]) + | + = help: Remove leading underscores + +ℹ Safe fix +135 135 | from enum import Enum +136 136 | from collections import namedtuple +137 137 | +138 |- _P = ParamSpec("_P") + 138 |+ P = ParamSpec("P") +139 139 | _T = TypeVar(name="_T", covariant=True, bound=int|str) +140 140 | _NT = NamedTuple("_NT", [("foo", int)]) +141 141 | _E = Enum("_E", ["a", "b", "c"]) +-------------------------------------------------------------------------------- +144 144 | _DynamicClass = type("_DynamicClass", (), {}) +145 145 | _NotADynamicClass = type("_NotADynamicClass") +146 146 | +147 |- print(_T, _P, _NT, _E, _NT2, _NT3, _DynamicClass, _NotADynamicClass) + 147 |+ print(_T, P, _NT, _E, _NT2, _NT3, _DynamicClass, _NotADynamicClass) + +RUF052.py:139:5: RUF052 [*] Local dummy variable `_T` is accessed + | +138 | _P = ParamSpec("_P") +139 | _T = TypeVar(name="_T", covariant=True, bound=int|str) + | ^^ RUF052 +140 | _NT = NamedTuple("_NT", [("foo", int)]) +141 | _E = Enum("_E", ["a", "b", "c"]) + | + = help: Remove leading underscores + +ℹ Safe fix +136 136 | from collections import namedtuple +137 137 | +138 138 | _P = ParamSpec("_P") +139 |- _T = TypeVar(name="_T", covariant=True, bound=int|str) + 139 |+ T = TypeVar(name="T", covariant=True, bound=int|str) +140 140 | _NT = NamedTuple("_NT", [("foo", int)]) +141 141 | _E = Enum("_E", ["a", "b", "c"]) +142 142 | _NT2 = namedtuple("_NT2", ['x', 'y', 'z']) +-------------------------------------------------------------------------------- +144 144 | _DynamicClass = type("_DynamicClass", (), {}) +145 145 | _NotADynamicClass = type("_NotADynamicClass") +146 146 | +147 |- print(_T, _P, _NT, _E, _NT2, _NT3, _DynamicClass, _NotADynamicClass) + 147 |+ print(T, _P, _NT, _E, _NT2, _NT3, _DynamicClass, _NotADynamicClass) + +RUF052.py:140:5: RUF052 [*] Local dummy variable `_NT` is accessed + | +138 | _P = ParamSpec("_P") +139 | _T = TypeVar(name="_T", covariant=True, bound=int|str) +140 | _NT = NamedTuple("_NT", [("foo", int)]) + | ^^^ RUF052 +141 | _E = Enum("_E", ["a", "b", "c"]) +142 | _NT2 = namedtuple("_NT2", ['x', 'y', 'z']) + | + = help: Remove leading underscores + +ℹ Safe fix +137 137 | +138 138 | _P = ParamSpec("_P") +139 139 | _T = TypeVar(name="_T", covariant=True, bound=int|str) +140 |- _NT = NamedTuple("_NT", [("foo", int)]) + 140 |+ NT = NamedTuple("NT", [("foo", int)]) +141 141 | _E = Enum("_E", ["a", "b", "c"]) +142 142 | _NT2 = namedtuple("_NT2", ['x', 'y', 'z']) +143 143 | _NT3 = namedtuple(typename="_NT3", field_names=['x', 'y', 'z']) +144 144 | _DynamicClass = type("_DynamicClass", (), {}) +145 145 | _NotADynamicClass = type("_NotADynamicClass") +146 146 | +147 |- print(_T, _P, _NT, _E, _NT2, _NT3, _DynamicClass, _NotADynamicClass) + 147 |+ print(_T, _P, NT, _E, _NT2, _NT3, _DynamicClass, _NotADynamicClass) + +RUF052.py:141:5: RUF052 [*] Local dummy variable `_E` is accessed + | +139 | _T = TypeVar(name="_T", covariant=True, bound=int|str) +140 | _NT = NamedTuple("_NT", [("foo", int)]) +141 | _E = Enum("_E", ["a", "b", "c"]) + | ^^ RUF052 +142 | _NT2 = namedtuple("_NT2", ['x', 'y', 'z']) +143 | _NT3 = namedtuple(typename="_NT3", field_names=['x', 'y', 'z']) + | + = help: Remove leading underscores + +ℹ Safe fix +138 138 | _P = ParamSpec("_P") +139 139 | _T = TypeVar(name="_T", covariant=True, bound=int|str) +140 140 | _NT = NamedTuple("_NT", [("foo", int)]) +141 |- _E = Enum("_E", ["a", "b", "c"]) + 141 |+ E = Enum("E", ["a", "b", "c"]) +142 142 | _NT2 = namedtuple("_NT2", ['x', 'y', 'z']) +143 143 | _NT3 = namedtuple(typename="_NT3", field_names=['x', 'y', 'z']) +144 144 | _DynamicClass = type("_DynamicClass", (), {}) +145 145 | _NotADynamicClass = type("_NotADynamicClass") +146 146 | +147 |- print(_T, _P, _NT, _E, _NT2, _NT3, _DynamicClass, _NotADynamicClass) + 147 |+ print(_T, _P, _NT, E, _NT2, _NT3, _DynamicClass, _NotADynamicClass) + +RUF052.py:142:5: RUF052 [*] Local dummy variable `_NT2` is accessed + | +140 | _NT = NamedTuple("_NT", [("foo", int)]) +141 | _E = Enum("_E", ["a", "b", "c"]) +142 | _NT2 = namedtuple("_NT2", ['x', 'y', 'z']) + | ^^^^ RUF052 +143 | _NT3 = namedtuple(typename="_NT3", field_names=['x', 'y', 'z']) +144 | _DynamicClass = type("_DynamicClass", (), {}) + | + = help: Remove leading underscores + +ℹ Safe fix +139 139 | _T = TypeVar(name="_T", covariant=True, bound=int|str) +140 140 | _NT = NamedTuple("_NT", [("foo", int)]) +141 141 | _E = Enum("_E", ["a", "b", "c"]) +142 |- _NT2 = namedtuple("_NT2", ['x', 'y', 'z']) + 142 |+ NT2 = namedtuple("NT2", ['x', 'y', 'z']) +143 143 | _NT3 = namedtuple(typename="_NT3", field_names=['x', 'y', 'z']) +144 144 | _DynamicClass = type("_DynamicClass", (), {}) +145 145 | _NotADynamicClass = type("_NotADynamicClass") +146 146 | +147 |- print(_T, _P, _NT, _E, _NT2, _NT3, _DynamicClass, _NotADynamicClass) + 147 |+ print(_T, _P, _NT, _E, NT2, _NT3, _DynamicClass, _NotADynamicClass) + +RUF052.py:143:5: RUF052 [*] Local dummy variable `_NT3` is accessed + | +141 | _E = Enum("_E", ["a", "b", "c"]) +142 | _NT2 = namedtuple("_NT2", ['x', 'y', 'z']) +143 | _NT3 = namedtuple(typename="_NT3", field_names=['x', 'y', 'z']) + | ^^^^ RUF052 +144 | _DynamicClass = type("_DynamicClass", (), {}) +145 | _NotADynamicClass = type("_NotADynamicClass") + | + = help: Remove leading underscores + +ℹ Safe fix +140 140 | _NT = NamedTuple("_NT", [("foo", int)]) +141 141 | _E = Enum("_E", ["a", "b", "c"]) +142 142 | _NT2 = namedtuple("_NT2", ['x', 'y', 'z']) +143 |- _NT3 = namedtuple(typename="_NT3", field_names=['x', 'y', 'z']) + 143 |+ NT3 = namedtuple(typename="NT3", field_names=['x', 'y', 'z']) +144 144 | _DynamicClass = type("_DynamicClass", (), {}) +145 145 | _NotADynamicClass = type("_NotADynamicClass") +146 146 | +147 |- print(_T, _P, _NT, _E, _NT2, _NT3, _DynamicClass, _NotADynamicClass) + 147 |+ print(_T, _P, _NT, _E, _NT2, NT3, _DynamicClass, _NotADynamicClass) + +RUF052.py:144:5: RUF052 [*] Local dummy variable `_DynamicClass` is accessed + | +142 | _NT2 = namedtuple("_NT2", ['x', 'y', 'z']) +143 | _NT3 = namedtuple(typename="_NT3", field_names=['x', 'y', 'z']) +144 | _DynamicClass = type("_DynamicClass", (), {}) + | ^^^^^^^^^^^^^ RUF052 +145 | _NotADynamicClass = type("_NotADynamicClass") + | + = help: Remove leading underscores + +ℹ Safe fix +141 141 | _E = Enum("_E", ["a", "b", "c"]) +142 142 | _NT2 = namedtuple("_NT2", ['x', 'y', 'z']) +143 143 | _NT3 = namedtuple(typename="_NT3", field_names=['x', 'y', 'z']) +144 |- _DynamicClass = type("_DynamicClass", (), {}) + 144 |+ DynamicClass = type("DynamicClass", (), {}) +145 145 | _NotADynamicClass = type("_NotADynamicClass") +146 146 | +147 |- print(_T, _P, _NT, _E, _NT2, _NT3, _DynamicClass, _NotADynamicClass) + 147 |+ print(_T, _P, _NT, _E, _NT2, _NT3, DynamicClass, _NotADynamicClass) + +RUF052.py:145:5: RUF052 [*] Local dummy variable `_NotADynamicClass` is accessed + | +143 | _NT3 = namedtuple(typename="_NT3", field_names=['x', 'y', 'z']) +144 | _DynamicClass = type("_DynamicClass", (), {}) +145 | _NotADynamicClass = type("_NotADynamicClass") + | ^^^^^^^^^^^^^^^^^ RUF052 +146 | +147 | print(_T, _P, _NT, _E, _NT2, _NT3, _DynamicClass, _NotADynamicClass) + | + = help: Remove leading underscores + +ℹ Safe fix +142 142 | _NT2 = namedtuple("_NT2", ['x', 'y', 'z']) +143 143 | _NT3 = namedtuple(typename="_NT3", field_names=['x', 'y', 'z']) +144 144 | _DynamicClass = type("_DynamicClass", (), {}) +145 |- _NotADynamicClass = type("_NotADynamicClass") + 145 |+ NotADynamicClass = type("_NotADynamicClass") +146 146 | +147 |- print(_T, _P, _NT, _E, _NT2, _NT3, _DynamicClass, _NotADynamicClass) + 147 |+ print(_T, _P, _NT, _E, _NT2, _NT3, _DynamicClass, NotADynamicClass) diff --git a/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__custom_dummy_var_regexp_preset__RUF052_RUF052.py_1.snap b/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__custom_dummy_var_regexp_preset__RUF052_RUF052.py_1.snap index eef08f0aeb..610a2fc251 100644 --- a/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__custom_dummy_var_regexp_preset__RUF052_RUF052.py_1.snap +++ b/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__custom_dummy_var_regexp_preset__RUF052_RUF052.py_1.snap @@ -148,3 +148,196 @@ RUF052.py:130:9: RUF052 Local dummy variable `_local` is accessed 131 | print(_local) | = help: Prefer using trailing underscores to avoid shadowing a variable + +RUF052.py:138:5: RUF052 [*] Local dummy variable `_P` is accessed + | +136 | from collections import namedtuple +137 | +138 | _P = ParamSpec("_P") + | ^^ RUF052 +139 | _T = TypeVar(name="_T", covariant=True, bound=int|str) +140 | _NT = NamedTuple("_NT", [("foo", int)]) + | + = help: Remove leading underscores + +ℹ Safe fix +135 135 | from enum import Enum +136 136 | from collections import namedtuple +137 137 | +138 |- _P = ParamSpec("_P") + 138 |+ P = ParamSpec("P") +139 139 | _T = TypeVar(name="_T", covariant=True, bound=int|str) +140 140 | _NT = NamedTuple("_NT", [("foo", int)]) +141 141 | _E = Enum("_E", ["a", "b", "c"]) +-------------------------------------------------------------------------------- +144 144 | _DynamicClass = type("_DynamicClass", (), {}) +145 145 | _NotADynamicClass = type("_NotADynamicClass") +146 146 | +147 |- print(_T, _P, _NT, _E, _NT2, _NT3, _DynamicClass, _NotADynamicClass) + 147 |+ print(_T, P, _NT, _E, _NT2, _NT3, _DynamicClass, _NotADynamicClass) + +RUF052.py:139:5: RUF052 [*] Local dummy variable `_T` is accessed + | +138 | _P = ParamSpec("_P") +139 | _T = TypeVar(name="_T", covariant=True, bound=int|str) + | ^^ RUF052 +140 | _NT = NamedTuple("_NT", [("foo", int)]) +141 | _E = Enum("_E", ["a", "b", "c"]) + | + = help: Remove leading underscores + +ℹ Safe fix +136 136 | from collections import namedtuple +137 137 | +138 138 | _P = ParamSpec("_P") +139 |- _T = TypeVar(name="_T", covariant=True, bound=int|str) + 139 |+ T = TypeVar(name="T", covariant=True, bound=int|str) +140 140 | _NT = NamedTuple("_NT", [("foo", int)]) +141 141 | _E = Enum("_E", ["a", "b", "c"]) +142 142 | _NT2 = namedtuple("_NT2", ['x', 'y', 'z']) +-------------------------------------------------------------------------------- +144 144 | _DynamicClass = type("_DynamicClass", (), {}) +145 145 | _NotADynamicClass = type("_NotADynamicClass") +146 146 | +147 |- print(_T, _P, _NT, _E, _NT2, _NT3, _DynamicClass, _NotADynamicClass) + 147 |+ print(T, _P, _NT, _E, _NT2, _NT3, _DynamicClass, _NotADynamicClass) + +RUF052.py:140:5: RUF052 [*] Local dummy variable `_NT` is accessed + | +138 | _P = ParamSpec("_P") +139 | _T = TypeVar(name="_T", covariant=True, bound=int|str) +140 | _NT = NamedTuple("_NT", [("foo", int)]) + | ^^^ RUF052 +141 | _E = Enum("_E", ["a", "b", "c"]) +142 | _NT2 = namedtuple("_NT2", ['x', 'y', 'z']) + | + = help: Remove leading underscores + +ℹ Safe fix +137 137 | +138 138 | _P = ParamSpec("_P") +139 139 | _T = TypeVar(name="_T", covariant=True, bound=int|str) +140 |- _NT = NamedTuple("_NT", [("foo", int)]) + 140 |+ NT = NamedTuple("NT", [("foo", int)]) +141 141 | _E = Enum("_E", ["a", "b", "c"]) +142 142 | _NT2 = namedtuple("_NT2", ['x', 'y', 'z']) +143 143 | _NT3 = namedtuple(typename="_NT3", field_names=['x', 'y', 'z']) +144 144 | _DynamicClass = type("_DynamicClass", (), {}) +145 145 | _NotADynamicClass = type("_NotADynamicClass") +146 146 | +147 |- print(_T, _P, _NT, _E, _NT2, _NT3, _DynamicClass, _NotADynamicClass) + 147 |+ print(_T, _P, NT, _E, _NT2, _NT3, _DynamicClass, _NotADynamicClass) + +RUF052.py:141:5: RUF052 [*] Local dummy variable `_E` is accessed + | +139 | _T = TypeVar(name="_T", covariant=True, bound=int|str) +140 | _NT = NamedTuple("_NT", [("foo", int)]) +141 | _E = Enum("_E", ["a", "b", "c"]) + | ^^ RUF052 +142 | _NT2 = namedtuple("_NT2", ['x', 'y', 'z']) +143 | _NT3 = namedtuple(typename="_NT3", field_names=['x', 'y', 'z']) + | + = help: Remove leading underscores + +ℹ Safe fix +138 138 | _P = ParamSpec("_P") +139 139 | _T = TypeVar(name="_T", covariant=True, bound=int|str) +140 140 | _NT = NamedTuple("_NT", [("foo", int)]) +141 |- _E = Enum("_E", ["a", "b", "c"]) + 141 |+ E = Enum("E", ["a", "b", "c"]) +142 142 | _NT2 = namedtuple("_NT2", ['x', 'y', 'z']) +143 143 | _NT3 = namedtuple(typename="_NT3", field_names=['x', 'y', 'z']) +144 144 | _DynamicClass = type("_DynamicClass", (), {}) +145 145 | _NotADynamicClass = type("_NotADynamicClass") +146 146 | +147 |- print(_T, _P, _NT, _E, _NT2, _NT3, _DynamicClass, _NotADynamicClass) + 147 |+ print(_T, _P, _NT, E, _NT2, _NT3, _DynamicClass, _NotADynamicClass) + +RUF052.py:142:5: RUF052 [*] Local dummy variable `_NT2` is accessed + | +140 | _NT = NamedTuple("_NT", [("foo", int)]) +141 | _E = Enum("_E", ["a", "b", "c"]) +142 | _NT2 = namedtuple("_NT2", ['x', 'y', 'z']) + | ^^^^ RUF052 +143 | _NT3 = namedtuple(typename="_NT3", field_names=['x', 'y', 'z']) +144 | _DynamicClass = type("_DynamicClass", (), {}) + | + = help: Remove leading underscores + +ℹ Safe fix +139 139 | _T = TypeVar(name="_T", covariant=True, bound=int|str) +140 140 | _NT = NamedTuple("_NT", [("foo", int)]) +141 141 | _E = Enum("_E", ["a", "b", "c"]) +142 |- _NT2 = namedtuple("_NT2", ['x', 'y', 'z']) + 142 |+ NT2 = namedtuple("NT2", ['x', 'y', 'z']) +143 143 | _NT3 = namedtuple(typename="_NT3", field_names=['x', 'y', 'z']) +144 144 | _DynamicClass = type("_DynamicClass", (), {}) +145 145 | _NotADynamicClass = type("_NotADynamicClass") +146 146 | +147 |- print(_T, _P, _NT, _E, _NT2, _NT3, _DynamicClass, _NotADynamicClass) + 147 |+ print(_T, _P, _NT, _E, NT2, _NT3, _DynamicClass, _NotADynamicClass) + +RUF052.py:143:5: RUF052 [*] Local dummy variable `_NT3` is accessed + | +141 | _E = Enum("_E", ["a", "b", "c"]) +142 | _NT2 = namedtuple("_NT2", ['x', 'y', 'z']) +143 | _NT3 = namedtuple(typename="_NT3", field_names=['x', 'y', 'z']) + | ^^^^ RUF052 +144 | _DynamicClass = type("_DynamicClass", (), {}) +145 | _NotADynamicClass = type("_NotADynamicClass") + | + = help: Remove leading underscores + +ℹ Safe fix +140 140 | _NT = NamedTuple("_NT", [("foo", int)]) +141 141 | _E = Enum("_E", ["a", "b", "c"]) +142 142 | _NT2 = namedtuple("_NT2", ['x', 'y', 'z']) +143 |- _NT3 = namedtuple(typename="_NT3", field_names=['x', 'y', 'z']) + 143 |+ NT3 = namedtuple(typename="NT3", field_names=['x', 'y', 'z']) +144 144 | _DynamicClass = type("_DynamicClass", (), {}) +145 145 | _NotADynamicClass = type("_NotADynamicClass") +146 146 | +147 |- print(_T, _P, _NT, _E, _NT2, _NT3, _DynamicClass, _NotADynamicClass) + 147 |+ print(_T, _P, _NT, _E, _NT2, NT3, _DynamicClass, _NotADynamicClass) + +RUF052.py:144:5: RUF052 [*] Local dummy variable `_DynamicClass` is accessed + | +142 | _NT2 = namedtuple("_NT2", ['x', 'y', 'z']) +143 | _NT3 = namedtuple(typename="_NT3", field_names=['x', 'y', 'z']) +144 | _DynamicClass = type("_DynamicClass", (), {}) + | ^^^^^^^^^^^^^ RUF052 +145 | _NotADynamicClass = type("_NotADynamicClass") + | + = help: Remove leading underscores + +ℹ Safe fix +141 141 | _E = Enum("_E", ["a", "b", "c"]) +142 142 | _NT2 = namedtuple("_NT2", ['x', 'y', 'z']) +143 143 | _NT3 = namedtuple(typename="_NT3", field_names=['x', 'y', 'z']) +144 |- _DynamicClass = type("_DynamicClass", (), {}) + 144 |+ DynamicClass = type("DynamicClass", (), {}) +145 145 | _NotADynamicClass = type("_NotADynamicClass") +146 146 | +147 |- print(_T, _P, _NT, _E, _NT2, _NT3, _DynamicClass, _NotADynamicClass) + 147 |+ print(_T, _P, _NT, _E, _NT2, _NT3, DynamicClass, _NotADynamicClass) + +RUF052.py:145:5: RUF052 [*] Local dummy variable `_NotADynamicClass` is accessed + | +143 | _NT3 = namedtuple(typename="_NT3", field_names=['x', 'y', 'z']) +144 | _DynamicClass = type("_DynamicClass", (), {}) +145 | _NotADynamicClass = type("_NotADynamicClass") + | ^^^^^^^^^^^^^^^^^ RUF052 +146 | +147 | print(_T, _P, _NT, _E, _NT2, _NT3, _DynamicClass, _NotADynamicClass) + | + = help: Remove leading underscores + +ℹ Safe fix +142 142 | _NT2 = namedtuple("_NT2", ['x', 'y', 'z']) +143 143 | _NT3 = namedtuple(typename="_NT3", field_names=['x', 'y', 'z']) +144 144 | _DynamicClass = type("_DynamicClass", (), {}) +145 |- _NotADynamicClass = type("_NotADynamicClass") + 145 |+ NotADynamicClass = type("_NotADynamicClass") +146 146 | +147 |- print(_T, _P, _NT, _E, _NT2, _NT3, _DynamicClass, _NotADynamicClass) + 147 |+ print(_T, _P, _NT, _E, _NT2, _NT3, _DynamicClass, NotADynamicClass) diff --git a/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__custom_dummy_var_regexp_preset__RUF052_RUF052.py_2.snap b/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__custom_dummy_var_regexp_preset__RUF052_RUF052.py_2.snap index dd918e2ea1..8974885f86 100644 --- a/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__custom_dummy_var_regexp_preset__RUF052_RUF052.py_2.snap +++ b/crates/ruff_linter/src/rules/ruff/snapshots/ruff_linter__rules__ruff__tests__custom_dummy_var_regexp_preset__RUF052_RUF052.py_2.snap @@ -97,3 +97,89 @@ RUF052.py:130:9: RUF052 Local dummy variable `_local` is accessed 131 | print(_local) | = help: Prefer using trailing underscores to avoid shadowing a variable + +RUF052.py:138:5: RUF052 Local dummy variable `_P` is accessed + | +136 | from collections import namedtuple +137 | +138 | _P = ParamSpec("_P") + | ^^ RUF052 +139 | _T = TypeVar(name="_T", covariant=True, bound=int|str) +140 | _NT = NamedTuple("_NT", [("foo", int)]) + | + = help: Remove leading underscores + +RUF052.py:139:5: RUF052 Local dummy variable `_T` is accessed + | +138 | _P = ParamSpec("_P") +139 | _T = TypeVar(name="_T", covariant=True, bound=int|str) + | ^^ RUF052 +140 | _NT = NamedTuple("_NT", [("foo", int)]) +141 | _E = Enum("_E", ["a", "b", "c"]) + | + = help: Remove leading underscores + +RUF052.py:140:5: RUF052 Local dummy variable `_NT` is accessed + | +138 | _P = ParamSpec("_P") +139 | _T = TypeVar(name="_T", covariant=True, bound=int|str) +140 | _NT = NamedTuple("_NT", [("foo", int)]) + | ^^^ RUF052 +141 | _E = Enum("_E", ["a", "b", "c"]) +142 | _NT2 = namedtuple("_NT2", ['x', 'y', 'z']) + | + = help: Remove leading underscores + +RUF052.py:141:5: RUF052 Local dummy variable `_E` is accessed + | +139 | _T = TypeVar(name="_T", covariant=True, bound=int|str) +140 | _NT = NamedTuple("_NT", [("foo", int)]) +141 | _E = Enum("_E", ["a", "b", "c"]) + | ^^ RUF052 +142 | _NT2 = namedtuple("_NT2", ['x', 'y', 'z']) +143 | _NT3 = namedtuple(typename="_NT3", field_names=['x', 'y', 'z']) + | + = help: Remove leading underscores + +RUF052.py:142:5: RUF052 Local dummy variable `_NT2` is accessed + | +140 | _NT = NamedTuple("_NT", [("foo", int)]) +141 | _E = Enum("_E", ["a", "b", "c"]) +142 | _NT2 = namedtuple("_NT2", ['x', 'y', 'z']) + | ^^^^ RUF052 +143 | _NT3 = namedtuple(typename="_NT3", field_names=['x', 'y', 'z']) +144 | _DynamicClass = type("_DynamicClass", (), {}) + | + = help: Remove leading underscores + +RUF052.py:143:5: RUF052 Local dummy variable `_NT3` is accessed + | +141 | _E = Enum("_E", ["a", "b", "c"]) +142 | _NT2 = namedtuple("_NT2", ['x', 'y', 'z']) +143 | _NT3 = namedtuple(typename="_NT3", field_names=['x', 'y', 'z']) + | ^^^^ RUF052 +144 | _DynamicClass = type("_DynamicClass", (), {}) +145 | _NotADynamicClass = type("_NotADynamicClass") + | + = help: Remove leading underscores + +RUF052.py:144:5: RUF052 Local dummy variable `_DynamicClass` is accessed + | +142 | _NT2 = namedtuple("_NT2", ['x', 'y', 'z']) +143 | _NT3 = namedtuple(typename="_NT3", field_names=['x', 'y', 'z']) +144 | _DynamicClass = type("_DynamicClass", (), {}) + | ^^^^^^^^^^^^^ RUF052 +145 | _NotADynamicClass = type("_NotADynamicClass") + | + = help: Remove leading underscores + +RUF052.py:145:5: RUF052 Local dummy variable `_NotADynamicClass` is accessed + | +143 | _NT3 = namedtuple(typename="_NT3", field_names=['x', 'y', 'z']) +144 | _DynamicClass = type("_DynamicClass", (), {}) +145 | _NotADynamicClass = type("_NotADynamicClass") + | ^^^^^^^^^^^^^^^^^ RUF052 +146 | +147 | print(_T, _P, _NT, _E, _NT2, _NT3, _DynamicClass, _NotADynamicClass) + | + = help: Remove leading underscores