Skip to content

Commit baf7f95

Browse files
committed
Fix missing union expressions for piped non-types in PYI055
1 parent 0029b4f commit baf7f95

File tree

4 files changed

+231
-16
lines changed

4 files changed

+231
-16
lines changed

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

+19
Original file line numberDiff line numberDiff line change
@@ -37,3 +37,22 @@ def func():
3737

3838
# PYI055
3939
x: Union[type[requests_mock.Mocker], type[httpretty], type[str]] = requests_mock.Mocker
40+
41+
42+
def convert_union(union: UnionType) -> _T | None:
43+
converters: tuple[
44+
type[_T] | type[Converter[_T]] | Converter[_T] | Callable[[str], _T], ... # PYI055
45+
] = union.__args__
46+
...
47+
48+
def convert_union(union: UnionType) -> _T | None:
49+
converters: tuple[
50+
Union[type[_T] | type[Converter[_T]] | Converter[_T] | Callable[[str], _T]], ... # PYI055
51+
] = union.__args__
52+
...
53+
54+
def convert_union(union: UnionType) -> _T | None:
55+
converters: tuple[
56+
Union[type[_T] | type[Converter[_T]]] | Converter[_T] | Callable[[str], _T], ... # PYI055
57+
] = union.__args__
58+
...

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

+23-16
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ pub(crate) fn unnecessary_type_union<'a>(checker: &mut Checker, union: &'a Expr)
8080
}
8181

8282
let mut type_exprs = Vec::new();
83+
let mut other_exprs = Vec::new();
8384

8485
let mut collect_type_exprs = |expr: &'a Expr, _| {
8586
let Some(subscript) = expr.as_subscript_expr() else {
@@ -91,6 +92,8 @@ pub(crate) fn unnecessary_type_union<'a>(checker: &mut Checker, union: &'a Expr)
9192
.is_some_and(|call_path| matches!(call_path.as_slice(), ["" | "builtins", "type"]))
9293
{
9394
type_exprs.push(&subscript.slice);
95+
} else {
96+
other_exprs.push(expr);
9497
}
9598
};
9699

@@ -144,24 +147,28 @@ pub(crate) fn unnecessary_type_union<'a>(checker: &mut Checker, union: &'a Expr)
144147
range: TextRange::default(),
145148
}))
146149
} else {
147-
checker
148-
.generator()
149-
.expr(&Expr::Subscript(ast::ExprSubscript {
150-
value: Box::new(Expr::Name(ast::ExprName {
151-
id: "type".into(),
152-
ctx: ExprContext::Load,
153-
range: TextRange::default(),
154-
})),
155-
slice: Box::new(concatenate_bin_ors(
156-
type_exprs
157-
.clone()
158-
.into_iter()
159-
.map(std::convert::AsRef::as_ref)
160-
.collect(),
161-
)),
150+
let types = &Expr::Subscript(ast::ExprSubscript {
151+
value: Box::new(Expr::Name(ast::ExprName {
152+
id: "type".into(),
162153
ctx: ExprContext::Load,
163154
range: TextRange::default(),
164-
}))
155+
})),
156+
slice: Box::new(concatenate_bin_ors(
157+
type_exprs
158+
.clone()
159+
.into_iter()
160+
.map(std::convert::AsRef::as_ref)
161+
.collect(),
162+
)),
163+
ctx: ExprContext::Load,
164+
range: TextRange::default(),
165+
});
166+
167+
let mut exprs = Vec::new();
168+
exprs.push(types);
169+
exprs.extend(other_exprs);
170+
171+
checker.generator().expr(&concatenate_bin_ors(exprs))
165172
};
166173

167174
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(

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

+65
Original file line numberDiff line numberDiff line change
@@ -54,5 +54,70 @@ PYI055.py:39:8: PYI055 [*] Multiple `type` members in a union. Combine them into
5454
38 38 | # PYI055
5555
39 |- x: Union[type[requests_mock.Mocker], type[httpretty], type[str]] = requests_mock.Mocker
5656
39 |+ x: type[Union[requests_mock.Mocker, httpretty, str]] = requests_mock.Mocker
57+
40 40 |
58+
41 41 |
59+
42 42 | def convert_union(union: UnionType) -> _T | None:
60+
61+
PYI055.py:44:9: PYI055 [*] Multiple `type` members in a union. Combine them into one, e.g., `type[_T | Converter[_T]]`.
62+
|
63+
42 | def convert_union(union: UnionType) -> _T | None:
64+
43 | converters: tuple[
65+
44 | type[_T] | type[Converter[_T]] | Converter[_T] | Callable[[str], _T], ...
66+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI055
67+
45 | ] = union.__args__
68+
46 | ...
69+
|
70+
= help: Combine multiple `type` members
71+
72+
Safe fix
73+
41 41 |
74+
42 42 | def convert_union(union: UnionType) -> _T | None:
75+
43 43 | converters: tuple[
76+
44 |- type[_T] | type[Converter[_T]] | Converter[_T] | Callable[[str], _T], ...
77+
44 |+ type[_T | Converter[_T]] | Converter[_T] | Callable[[str], _T], ...
78+
45 45 | ] = union.__args__
79+
46 46 | ...
80+
47 47 |
81+
82+
PYI055.py:50:15: PYI055 [*] Multiple `type` members in a union. Combine them into one, e.g., `type[_T | Converter[_T]]`.
83+
|
84+
48 | def convert_union(union: UnionType) -> _T | None:
85+
49 | converters: tuple[
86+
50 | Union[type[_T] | type[Converter[_T]] | Converter[_T] | Callable[[str], _T]], ...
87+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI055
88+
51 | ] = union.__args__
89+
52 | ...
90+
|
91+
= help: Combine multiple `type` members
92+
93+
Safe fix
94+
47 47 |
95+
48 48 | def convert_union(union: UnionType) -> _T | None:
96+
49 49 | converters: tuple[
97+
50 |- Union[type[_T] | type[Converter[_T]] | Converter[_T] | Callable[[str], _T]], ...
98+
50 |+ Union[type[_T | Converter[_T]] | Converter[_T] | Callable[[str], _T]], ...
99+
51 51 | ] = union.__args__
100+
52 52 | ...
101+
53 53 |
102+
103+
PYI055.py:56:15: PYI055 [*] Multiple `type` members in a union. Combine them into one, e.g., `type[_T | Converter[_T]]`.
104+
|
105+
54 | def convert_union(union: UnionType) -> _T | None:
106+
55 | converters: tuple[
107+
56 | Union[type[_T] | type[Converter[_T]]] | Converter[_T] | Callable[[str], _T], ...
108+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI055
109+
57 | ] = union.__args__
110+
58 | ...
111+
|
112+
= help: Combine multiple `type` members
113+
114+
Safe fix
115+
53 53 |
116+
54 54 | def convert_union(union: UnionType) -> _T | None:
117+
55 55 | converters: tuple[
118+
56 |- Union[type[_T] | type[Converter[_T]]] | Converter[_T] | Callable[[str], _T], ...
119+
56 |+ Union[type[_T | Converter[_T]]] | Converter[_T] | Callable[[str], _T], ...
120+
57 57 | ] = union.__args__
121+
58 58 | ...
57122

58123

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
---
2+
source: crates/ruff_linter/src/rules/flake8_pyi/mod.rs
3+
assertion_line: 117
4+
---
5+
PYI055.py:31:8: PYI055 [*] Multiple `type` members in a union. Combine them into one, e.g., `type[requests_mock.Mocker | httpretty | str]`.
6+
|
7+
29 | def func():
8+
30 | # PYI055
9+
31 | x: type[requests_mock.Mocker] | type[httpretty] | type[str] = requests_mock.Mocker
10+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI055
11+
32 | y: Union[type[requests_mock.Mocker], type[httpretty], type[str]] = requests_mock.Mocker
12+
|
13+
= help: Combine multiple `type` members
14+
15+
ℹ Safe fix
16+
28 28 |
17+
29 29 | def func():
18+
30 30 | # PYI055
19+
31 |- x: type[requests_mock.Mocker] | type[httpretty] | type[str] = requests_mock.Mocker
20+
31 |+ x: type[requests_mock.Mocker | httpretty | str] = requests_mock.Mocker
21+
32 32 | y: Union[type[requests_mock.Mocker], type[httpretty], type[str]] = requests_mock.Mocker
22+
33 33 |
23+
34 34 |
24+
25+
PYI055.py:32:8: PYI055 [*] Multiple `type` members in a union. Combine them into one, e.g., `type[Union[requests_mock.Mocker, httpretty, str]]`.
26+
|
27+
30 | # PYI055
28+
31 | x: type[requests_mock.Mocker] | type[httpretty] | type[str] = requests_mock.Mocker
29+
32 | y: Union[type[requests_mock.Mocker], type[httpretty], type[str]] = requests_mock.Mocker
30+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI055
31+
|
32+
= help: Combine multiple `type` members
33+
34+
ℹ Safe fix
35+
29 29 | def func():
36+
30 30 | # PYI055
37+
31 31 | x: type[requests_mock.Mocker] | type[httpretty] | type[str] = requests_mock.Mocker
38+
32 |- y: Union[type[requests_mock.Mocker], type[httpretty], type[str]] = requests_mock.Mocker
39+
32 |+ y: type[Union[requests_mock.Mocker, httpretty, str]] = requests_mock.Mocker
40+
33 33 |
41+
34 34 |
42+
35 35 | def func():
43+
44+
PYI055.py:39:8: PYI055 [*] Multiple `type` members in a union. Combine them into one, e.g., `type[Union[requests_mock.Mocker, httpretty, str]]`.
45+
|
46+
38 | # PYI055
47+
39 | x: Union[type[requests_mock.Mocker], type[httpretty], type[str]] = requests_mock.Mocker
48+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI055
49+
|
50+
= help: Combine multiple `type` members
51+
52+
ℹ Safe fix
53+
36 36 | from typing import Union as U
54+
37 37 |
55+
38 38 | # PYI055
56+
39 |- x: Union[type[requests_mock.Mocker], type[httpretty], type[str]] = requests_mock.Mocker
57+
39 |+ x: type[Union[requests_mock.Mocker, httpretty, str]] = requests_mock.Mocker
58+
40 40 |
59+
41 41 |
60+
42 42 | def convert_union(union: UnionType) -> _T | None:
61+
62+
PYI055.py:44:9: PYI055 [*] Multiple `type` members in a union. Combine them into one, e.g., `type[_T | Converter[_T]]`.
63+
|
64+
42 | def convert_union(union: UnionType) -> _T | None:
65+
43 | converters: tuple[
66+
44 | type[_T] | type[Converter[_T]] | Converter[_T] | Callable[[str], _T], ... # PYI055
67+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI055
68+
45 | ] = union.__args__
69+
46 | ...
70+
|
71+
= help: Combine multiple `type` members
72+
73+
ℹ Safe fix
74+
41 41 |
75+
42 42 | def convert_union(union: UnionType) -> _T | None:
76+
43 43 | converters: tuple[
77+
44 |- type[_T] | type[Converter[_T]] | Converter[_T] | Callable[[str], _T], ... # PYI055
78+
44 |+ type[_T | Converter[_T]] | Converter[_T] | Callable[[str], _T], ... # PYI055
79+
45 45 | ] = union.__args__
80+
46 46 | ...
81+
47 47 |
82+
83+
PYI055.py:50:15: PYI055 [*] Multiple `type` members in a union. Combine them into one, e.g., `type[_T | Converter[_T]]`.
84+
|
85+
48 | def convert_union(union: UnionType) -> _T | None:
86+
49 | converters: tuple[
87+
50 | Union[type[_T] | type[Converter[_T]] | Converter[_T] | Callable[[str], _T]], ... # PYI055
88+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI055
89+
51 | ] = union.__args__
90+
52 | ...
91+
|
92+
= help: Combine multiple `type` members
93+
94+
ℹ Safe fix
95+
47 47 |
96+
48 48 | def convert_union(union: UnionType) -> _T | None:
97+
49 49 | converters: tuple[
98+
50 |- Union[type[_T] | type[Converter[_T]] | Converter[_T] | Callable[[str], _T]], ... # PYI055
99+
50 |+ Union[type[_T | Converter[_T]] | Converter[_T] | Callable[[str], _T]], ... # PYI055
100+
51 51 | ] = union.__args__
101+
52 52 | ...
102+
53 53 |
103+
104+
PYI055.py:56:15: PYI055 [*] Multiple `type` members in a union. Combine them into one, e.g., `type[_T | Converter[_T]]`.
105+
|
106+
54 | def convert_union(union: UnionType) -> _T | None:
107+
55 | converters: tuple[
108+
56 | Union[type[_T] | type[Converter[_T]]] | Converter[_T] | Callable[[str], _T], ... # PYI055
109+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI055
110+
57 | ] = union.__args__
111+
58 | ...
112+
|
113+
= help: Combine multiple `type` members
114+
115+
ℹ Safe fix
116+
53 53 |
117+
54 54 | def convert_union(union: UnionType) -> _T | None:
118+
55 55 | converters: tuple[
119+
56 |- Union[type[_T] | type[Converter[_T]]] | Converter[_T] | Callable[[str], _T], ... # PYI055
120+
56 |+ Union[type[_T | Converter[_T]]] | Converter[_T] | Callable[[str], _T], ... # PYI055
121+
57 57 | ] = union.__args__
122+
58 58 | ...
123+
124+

0 commit comments

Comments
 (0)