Skip to content

Commit e462c03

Browse files
kar-ganapntBre
andauthored
Fix F811 false positive for overloaded functions from typing-modules (#23357)
## Summary Fixes #19632. When `@overload` is imported from a custom module listed in `typing-modules` (e.g., `from std import overload`), Ruff fails to recognize it as the `typing.overload` decorator and emits false F811 diagnostics for each overloaded function definition. The root cause is in `SemanticModel::match_typing_qualified_name`: `QualifiedName::from_dotted_name` splits on dots, so a single-segment module like `std` still works, but the internal representation stores it differently from what `QualifiedName::user_defined` produces. Use `user_defined` instead to match the internal representation consistently. ## Test plan - Reproduction case: `ruff check --select F811 --config ruff.toml temp.py` no longer emits false positives for `@overload` from custom typing-modules - `cargo test -p ruff_linter -- pyflakes` — all 462 tests pass --------- Co-authored-by: Brent Westbrook <brentrwestbrook@gmail.com>
1 parent f8d1dd2 commit e462c03

4 files changed

Lines changed: 37 additions & 1 deletion

File tree

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# Regression test for https://github.com/astral-sh/ruff/issues/19632
2+
# When @overload is imported from a custom module listed in typing-modules,
3+
# Ruff should recognize it as typing.overload and not emit false F811
4+
# diagnostics for each overloaded function definition.
5+
#
6+
# Requires: typing-modules = ["std"]
7+
from std import overload
8+
9+
10+
@overload
11+
def func(a: str, b: int) -> int: ...
12+
13+
14+
@overload
15+
def func(a: int, b: str) -> int: ...
16+
17+
18+
def func(a: int | str, b: int | str) -> int:
19+
return 0

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

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -708,6 +708,19 @@ mod tests {
708708
Ok(())
709709
}
710710

711+
#[test]
712+
fn f811_typing_modules_overload() -> Result<()> {
713+
let diagnostics = test_path(
714+
Path::new("pyflakes/F811_33.py"),
715+
&LinterSettings {
716+
typing_modules: vec!["std".to_string()],
717+
..LinterSettings::for_rule(Rule::RedefinedWhileUnused)
718+
},
719+
)?;
720+
assert_diagnostics!(diagnostics);
721+
Ok(())
722+
}
723+
711724
#[test]
712725
fn extend_generics() -> Result<()> {
713726
let snapshot = "extend_immutable_calls".to_string();
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+

crates/ruff_python_semantic/src/model.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,7 @@ impl<'a> SemanticModel<'a> {
209209
}
210210

211211
if self.typing_modules.iter().any(|module| {
212-
let module = QualifiedName::from_dotted_name(module);
212+
let module = QualifiedName::user_defined(module);
213213
qualified_name == &module.append_member(target)
214214
}) {
215215
return true;

0 commit comments

Comments
 (0)