From f034623851e543edd762a05ee89163b356db795c Mon Sep 17 00:00:00 2001 From: ukyen Date: Fri, 21 Jun 2024 10:18:18 +0100 Subject: [PATCH] fix(F811): assignment can also be shadowed binding --- .../resources/test/fixtures/pyflakes/F811_30.py | 13 +++++++++++++ .../src/checkers/ast/analyze/deferred_scopes.rs | 7 +++++++ crates/ruff_linter/src/rules/pyflakes/mod.rs | 1 + ...er__rules__pyflakes__tests__F811_F811_30.py.snap | 12 ++++++++++++ crates/ruff_python_semantic/src/binding.rs | 5 +++-- 5 files changed, 36 insertions(+), 2 deletions(-) create mode 100644 crates/ruff_linter/resources/test/fixtures/pyflakes/F811_30.py create mode 100644 crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__F811_F811_30.py.snap diff --git a/crates/ruff_linter/resources/test/fixtures/pyflakes/F811_30.py b/crates/ruff_linter/resources/test/fixtures/pyflakes/F811_30.py new file mode 100644 index 00000000000000..8125d75ef19cae --- /dev/null +++ b/crates/ruff_linter/resources/test/fixtures/pyflakes/F811_30.py @@ -0,0 +1,13 @@ +"""Regression test for: https://github.com/astral-sh/ruff/issues/11828""" + + +class A: + """A.""" + + def foo(self) -> None: + """Foo.""" + + bar = foo + + def bar(self) -> None: + """Bar.""" diff --git a/crates/ruff_linter/src/checkers/ast/analyze/deferred_scopes.rs b/crates/ruff_linter/src/checkers/ast/analyze/deferred_scopes.rs index c4d0ee7944b785..a66dac8a7f71bb 100644 --- a/crates/ruff_linter/src/checkers/ast/analyze/deferred_scopes.rs +++ b/crates/ruff_linter/src/checkers/ast/analyze/deferred_scopes.rs @@ -227,6 +227,13 @@ pub(crate) fn deferred_scopes(checker: &mut Checker) { continue; }; + // Ignore non-import redefinitions + if matches!(shadowed.kind, BindingKind::Assignment) + && matches!(binding.kind, BindingKind::Assignment) + { + continue; + } + // If this is an overloaded function, abort. if shadowed.kind.is_function_definition() { if checker diff --git a/crates/ruff_linter/src/rules/pyflakes/mod.rs b/crates/ruff_linter/src/rules/pyflakes/mod.rs index a700b7ed6c498c..f2b1e6d114e1bb 100644 --- a/crates/ruff_linter/src/rules/pyflakes/mod.rs +++ b/crates/ruff_linter/src/rules/pyflakes/mod.rs @@ -125,6 +125,7 @@ mod tests { #[test_case(Rule::RedefinedWhileUnused, Path::new("F811_27.py"))] #[test_case(Rule::RedefinedWhileUnused, Path::new("F811_28.py"))] #[test_case(Rule::RedefinedWhileUnused, Path::new("F811_29.pyi"))] + #[test_case(Rule::RedefinedWhileUnused, Path::new("F811_30.py"))] #[test_case(Rule::UndefinedName, Path::new("F821_0.py"))] #[test_case(Rule::UndefinedName, Path::new("F821_1.py"))] #[test_case(Rule::UndefinedName, Path::new("F821_2.py"))] diff --git a/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__F811_F811_30.py.snap b/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__F811_F811_30.py.snap new file mode 100644 index 00000000000000..0ecd338c14c926 --- /dev/null +++ b/crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__F811_F811_30.py.snap @@ -0,0 +1,12 @@ +--- +source: crates/ruff_linter/src/rules/pyflakes/mod.rs +--- +F811_30.py:12:9: F811 Redefinition of unused `bar` from line 10 + | +10 | bar = foo +11 | +12 | def bar(self) -> None: + | ^^^ F811 +13 | """Bar.""" + | + = help: Remove definition: `bar` diff --git a/crates/ruff_python_semantic/src/binding.rs b/crates/ruff_python_semantic/src/binding.rs index 22ee07490c8267..3573f4a4ad140d 100644 --- a/crates/ruff_python_semantic/src/binding.rs +++ b/crates/ruff_python_semantic/src/binding.rs @@ -179,14 +179,15 @@ impl<'a> Binding<'a> { } _ => {} } - // Otherwise, the shadowed binding must be a class definition, function definition, or - // import to be considered a redefinition. + // Otherwise, the shadowed binding must be a class definition, function definition, + // import, or assignment to be considered a redefinition. matches!( existing.kind, BindingKind::ClassDefinition(_) | BindingKind::FunctionDefinition(_) | BindingKind::Import(_) | BindingKind::FromImport(_) + | BindingKind::Assignment ) }