Skip to content

Commit 8d956e0

Browse files
authored
[pyflakes] Fix false positive for names shadowing re-exports (F811) (#23356)
## Summary Fixes #10874. In stub files, explicit re-exports (`from x import y as y`) at module scope were falsely flagged as redefined (F811) by class-scoped attributes with the same name. A class attribute binding in a nested scope should not invalidate a module-level re-export. Skip the F811 diagnostic when the shadowed binding is an explicit re-export (`is_explicit_export()`). ## Test plan - Reproduction case: `ruff check --select F811 stub.pyi` no longer emits false positive - `cargo test -p ruff_linter -- pyflakes` — all 462 tests pass
1 parent df7e826 commit 8d956e0

4 files changed

Lines changed: 19 additions & 0 deletions

File tree

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# Regression test for https://github.com/astral-sh/ruff/issues/10874
2+
# Explicit re-exports at module scope should not be flagged as redefined
3+
# by class-scoped attributes with the same name.
4+
from x import y as y
5+
6+
class Foo:
7+
y = 42 # OK — class attribute, different scope from module-level re-export

crates/ruff_linter/src/rules/pyflakes/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@ mod tests {
132132
#[test_case(Rule::RedefinedWhileUnused, Path::new("F811_30.py"))]
133133
#[test_case(Rule::RedefinedWhileUnused, Path::new("F811_31.py"))]
134134
#[test_case(Rule::RedefinedWhileUnused, Path::new("F811_32.py"))]
135+
#[test_case(Rule::RedefinedWhileUnused, Path::new("F811_33.pyi"))]
135136
#[test_case(Rule::UndefinedName, Path::new("F821_0.py"))]
136137
#[test_case(Rule::UndefinedName, Path::new("F821_1.py"))]
137138
#[test_case(Rule::UndefinedName, Path::new("F821_2.py"))]

crates/ruff_linter/src/rules/pyflakes/rules/redefined_while_unused.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,13 @@ pub(crate) fn redefined_while_unused(checker: &Checker, scope_id: ScopeId, scope
126126
) {
127127
continue;
128128
}
129+
130+
// Don't flag explicit re-exports (e.g., `from x import y as y`).
131+
// A binding in a nested scope (like a class attribute) doesn't
132+
// invalidate a module-level re-export.
133+
if shadowed.is_explicit_export() {
134+
continue;
135+
}
129136
}
130137

131138
// If the bindings are in different forks, abort.
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
source: crates/ruff_linter/src/rules/pyflakes/mod.rs
3+
---
4+

0 commit comments

Comments
 (0)