Skip to content

Commit be1fde1

Browse files
committed
[flake8_pyi] Fix PYI041's fix causing TypeError with None | None | ...
1 parent 7893cf9 commit be1fde1

File tree

5 files changed

+126
-10
lines changed

5 files changed

+126
-10
lines changed

crates/ruff_linter/resources/test/fixtures/flake8_pyi/PYI041.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,3 +90,13 @@ def bad4(self, arg: Union[float | complex, int]) -> None:
9090

9191
def bad5(self, arg: int | (float | complex)) -> None:
9292
...
93+
94+
95+
# https://github.com/astral-sh/ruff/issues/18298
96+
# fix must not yeild `None | None | ...` (TypeError)
97+
class Issue18298:
98+
def f1(self, arg: None | int | None | float = None) -> None:
99+
pass
100+
101+
def f2(self, arg: None | float | None | int = None) -> None:
102+
pass

crates/ruff_linter/resources/test/fixtures/flake8_pyi/PYI041.pyi

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,3 +70,11 @@ class Foo:
7070
def bad4(self, arg: Union[float | complex, int]) -> None: ... # PYI041
7171

7272
def bad5(self, arg: int | (float | complex)) -> None: ... # PYI041
73+
74+
75+
# https://github.com/astral-sh/ruff/issues/18298
76+
# fix must not yeild `None | None | ...` (TypeError)
77+
class Issue18298:
78+
def f1(self, arg: None | int | None | float = None) -> None: ...
79+
80+
def f2(self, arg: None | float | None | int = None) -> None: ...

crates/ruff_linter/src/rules/flake8_pyi/rules/redundant_numeric_union.rs

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,8 @@ fn check_annotation<'a>(checker: &Checker, annotation: &'a Expr) {
104104

105105
// Traverse the union a second time to construct the fix.
106106
let mut necessary_nodes: Vec<&Expr> = Vec::new();
107+
let mut super_type_index = None;
108+
// let mut super_type_inserted = false;
107109

108110
let mut union_type = UnionKind::TypingUnion;
109111
let mut remove_numeric_type = |expr: &'a Expr, parent: &'a Expr| {
@@ -117,12 +119,30 @@ fn check_annotation<'a>(checker: &Checker, annotation: &'a Expr) {
117119
union_type = UnionKind::PEP604;
118120
}
119121

120-
// `int` is always dropped, since `float` or `complex` must be present.
121-
// `float` is only dropped if `complex`` is present.
122-
if (builtin_type == "float" && !numeric_flags.contains(NumericFlags::COMPLEX))
123-
|| (builtin_type != "float" && builtin_type != "int")
124-
{
125-
necessary_nodes.push(expr);
122+
let is_super_type = match builtin_type {
123+
"complex" => true,
124+
"float" => !numeric_flags.contains(NumericFlags::COMPLEX),
125+
"int" => !(NumericFlags::COMPLEX | NumericFlags::FLOAT).intersects(numeric_flags),
126+
_ => {
127+
// Keep type annotations that are not numeric.
128+
necessary_nodes.push(expr);
129+
return;
130+
}
131+
};
132+
133+
match super_type_index {
134+
Some(idx) => {
135+
if is_super_type {
136+
// The super type is already inserted at the end of the list.
137+
necessary_nodes[idx] = expr;
138+
}
139+
}
140+
None => {
141+
super_type_index = Some(necessary_nodes.len());
142+
// Push the current type, even if it is not a super type.
143+
// It will be replaced later.
144+
necessary_nodes.push(expr);
145+
}
126146
}
127147
};
128148

crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI041_PYI041.py.snap

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
---
22
source: crates/ruff_linter/src/rules/flake8_pyi/mod.rs
3-
snapshot_kind: text
43
---
54
PYI041.py:22:14: PYI041 [*] Use `float` instead of `int | float`
65
|
@@ -33,7 +32,7 @@ PYI041.py:26:30: PYI041 [*] Use `complex` instead of `float | complex`
3332
24 24 |
3433
25 25 |
3534
26 |-def f1(arg1: float, *, arg2: float | list[str] | type[bool] | complex) -> None:
36-
26 |+def f1(arg1: float, *, arg2: list[str] | type[bool] | complex) -> None:
35+
26 |+def f1(arg1: float, *, arg2: complex | list[str] | type[bool]) -> None:
3736
27 27 | ...
3837
28 28 |
3938
29 29 |
@@ -309,3 +308,43 @@ PYI041.py:91:25: PYI041 [*] Use `complex` instead of `int | float | complex`
309308
91 |- def bad5(self, arg: int | (float | complex)) -> None:
310309
91 |+ def bad5(self, arg: complex) -> None:
311310
92 92 | ...
311+
93 93 |
312+
94 94 |
313+
314+
PYI041.py:98:23: PYI041 [*] Use `float` instead of `int | float`
315+
|
316+
96 | # fix must not yeild `None | None | ...` (TypeError)
317+
97 | class Issue18298:
318+
98 | def f1(self, arg: None | int | None | float = None) -> None:
319+
| ^^^^^^^^^^^^^^^^^^^^^^^^^ PYI041
320+
99 | pass
321+
|
322+
= help: Remove redundant type
323+
324+
Safe fix
325+
95 95 | # https://github.com/astral-sh/ruff/issues/18298
326+
96 96 | # fix must not yeild `None | None | ...` (TypeError)
327+
97 97 | class Issue18298:
328+
98 |- def f1(self, arg: None | int | None | float = None) -> None:
329+
98 |+ def f1(self, arg: None | float | None = None) -> None:
330+
99 99 | pass
331+
100 100 |
332+
101 101 | def f2(self, arg: None | float | None | int = None) -> None:
333+
334+
PYI041.py:101:23: PYI041 [*] Use `float` instead of `int | float`
335+
|
336+
99 | pass
337+
100 |
338+
101 | def f2(self, arg: None | float | None | int = None) -> None:
339+
| ^^^^^^^^^^^^^^^^^^^^^^^^^ PYI041
340+
102 | pass
341+
|
342+
= help: Remove redundant type
343+
344+
Safe fix
345+
98 98 | def f1(self, arg: None | int | None | float = None) -> None:
346+
99 99 | pass
347+
100 100 |
348+
101 |- def f2(self, arg: None | float | None | int = None) -> None:
349+
101 |+ def f2(self, arg: None | float | None = None) -> None:
350+
102 102 | pass

crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI041_PYI041.pyi.snap

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
---
22
source: crates/ruff_linter/src/rules/flake8_pyi/mod.rs
3-
snapshot_kind: text
43
---
54
PYI041.pyi:21:14: PYI041 [*] Use `float` instead of `int | float`
65
|
@@ -31,7 +30,7 @@ PYI041.pyi:24:30: PYI041 [*] Use `complex` instead of `float | complex`
3130
22 22 |
3231
23 23 |
3332
24 |-def f1(arg1: float, *, arg2: float | list[str] | type[bool] | complex) -> None: ... # PYI041
34-
24 |+def f1(arg1: float, *, arg2: list[str] | type[bool] | complex) -> None: ... # PYI041
33+
24 |+def f1(arg1: float, *, arg2: complex | list[str] | type[bool]) -> None: ... # PYI041
3534
25 25 |
3635
26 26 |
3736
27 27 | def f2(arg1: int, /, arg2: int | int | float) -> None: ... # PYI041
@@ -288,6 +287,7 @@ PYI041.pyi:70:25: PYI041 [*] Use `complex` instead of `int | float | complex`
288287
70 |+ def bad4(self, arg: complex) -> None: ... # PYI041
289288
71 71 |
290289
72 72 | def bad5(self, arg: int | (float | complex)) -> None: ... # PYI041
290+
73 73 |
291291

292292
PYI041.pyi:72:25: PYI041 [*] Use `complex` instead of `int | float | complex`
293293
|
@@ -304,3 +304,42 @@ PYI041.pyi:72:25: PYI041 [*] Use `complex` instead of `int | float | complex`
304304
71 71 |
305305
72 |- def bad5(self, arg: int | (float | complex)) -> None: ... # PYI041
306306
72 |+ def bad5(self, arg: complex) -> None: ... # PYI041
307+
73 73 |
308+
74 74 |
309+
75 75 | # https://github.com/astral-sh/ruff/issues/18298
310+
311+
PYI041.pyi:78:23: PYI041 [*] Use `float` instead of `int | float`
312+
|
313+
76 | # fix must not yeild `None | None | ...` (TypeError)
314+
77 | class Issue18298:
315+
78 | def f1(self, arg: None | int | None | float = None) -> None: ...
316+
| ^^^^^^^^^^^^^^^^^^^^^^^^^ PYI041
317+
79 |
318+
80 | def f2(self, arg: None | float | None | int = None) -> None: ...
319+
|
320+
= help: Remove redundant type
321+
322+
Safe fix
323+
75 75 | # https://github.com/astral-sh/ruff/issues/18298
324+
76 76 | # fix must not yeild `None | None | ...` (TypeError)
325+
77 77 | class Issue18298:
326+
78 |- def f1(self, arg: None | int | None | float = None) -> None: ...
327+
78 |+ def f1(self, arg: None | float | None = None) -> None: ...
328+
79 79 |
329+
80 80 | def f2(self, arg: None | float | None | int = None) -> None: ...
330+
331+
PYI041.pyi:80:23: PYI041 [*] Use `float` instead of `int | float`
332+
|
333+
78 | def f1(self, arg: None | int | None | float = None) -> None: ...
334+
79 |
335+
80 | def f2(self, arg: None | float | None | int = None) -> None: ...
336+
| ^^^^^^^^^^^^^^^^^^^^^^^^^ PYI041
337+
|
338+
= help: Remove redundant type
339+
340+
Safe fix
341+
77 77 | class Issue18298:
342+
78 78 | def f1(self, arg: None | int | None | float = None) -> None: ...
343+
79 79 |
344+
80 |- def f2(self, arg: None | float | None | int = None) -> None: ...
345+
80 |+ def f2(self, arg: None | float | None = None) -> None: ...

0 commit comments

Comments
 (0)