Flatten nested tuples when fixing UP007 violations (#5724)

## Summary

Also upgrading these to "Suggested" from "Manual" (they should've always
been "Suggested", I think), and adding some more test cases.
This commit is contained in:
Charlie Marsh 2023-07-13 00:11:32 -04:00 committed by GitHub
parent 34b79ead3d
commit 30702c2977
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 139 additions and 83 deletions

View File

@ -27,6 +27,14 @@ def f(x: typing.Union[(str, int), float]) -> None:
... ...
def f(x: typing.Union[(int,)]) -> None:
...
def f(x: typing.Union[()]) -> None:
...
def f(x: "Union[str, int, Union[float, bytes]]") -> None: def f(x: "Union[str, int, Union[float, bytes]]") -> None:
... ...

View File

@ -1,3 +1,4 @@
use itertools::Either::{Left, Right};
use itertools::Itertools; use itertools::Itertools;
use rustpython_parser::ast::{self, Expr, Ranged}; use rustpython_parser::ast::{self, Expr, Ranged};
@ -63,7 +64,7 @@ pub(crate) fn use_pep604_annotation(
Pep604Operator::Optional => { Pep604Operator::Optional => {
let mut diagnostic = Diagnostic::new(NonPEP604Annotation, expr.range()); let mut diagnostic = Diagnostic::new(NonPEP604Annotation, expr.range());
if fixable && checker.patch(diagnostic.kind.rule()) { if fixable && checker.patch(diagnostic.kind.rule()) {
diagnostic.set_fix(Fix::manual(Edit::range_replacement( diagnostic.set_fix(Fix::suggested(Edit::range_replacement(
optional(slice, checker.locator), optional(slice, checker.locator),
expr.range(), expr.range(),
))); )));
@ -78,14 +79,14 @@ pub(crate) fn use_pep604_annotation(
// Invalid type annotation. // Invalid type annotation.
} }
Expr::Tuple(ast::ExprTuple { elts, .. }) => { Expr::Tuple(ast::ExprTuple { elts, .. }) => {
diagnostic.set_fix(Fix::manual(Edit::range_replacement( diagnostic.set_fix(Fix::suggested(Edit::range_replacement(
union(elts, checker.locator), union(elts, checker.locator),
expr.range(), expr.range(),
))); )));
} }
_ => { _ => {
// Single argument. // Single argument.
diagnostic.set_fix(Fix::manual(Edit::range_replacement( diagnostic.set_fix(Fix::suggested(Edit::range_replacement(
checker.locator.slice(slice.range()).to_string(), checker.locator.slice(slice.range()).to_string(),
expr.range(), expr.range(),
))); )));
@ -97,12 +98,23 @@ pub(crate) fn use_pep604_annotation(
} }
} }
/// Format the expression as a PEP 604-style optional.
fn optional(expr: &Expr, locator: &Locator) -> String { fn optional(expr: &Expr, locator: &Locator) -> String {
format!("{} | None", locator.slice(expr.range())) format!("{} | None", locator.slice(expr.range()))
} }
/// Format the expressions as a PEP 604-style union.
fn union(elts: &[Expr], locator: &Locator) -> String { fn union(elts: &[Expr], locator: &Locator) -> String {
elts.iter() let mut elts = elts
.map(|expr| locator.slice(expr.range())) .iter()
.join(" | ") .flat_map(|expr| match expr {
Expr::Tuple(ast::ExprTuple { elts, .. }) => Left(elts.iter()),
_ => Right(std::iter::once(expr)),
})
.peekable();
if elts.peek().is_none() {
"()".to_string()
} else {
elts.map(|expr| locator.slice(expr.range())).join(" | ")
}
} }

View File

@ -9,7 +9,7 @@ UP007.py:6:10: UP007 [*] Use `X | Y` for type annotations
| |
= help: Convert to `X | Y` = help: Convert to `X | Y`
Possible fix Suggested fix
3 3 | from typing import Union 3 3 | from typing import Union
4 4 | 4 4 |
5 5 | 5 5 |
@ -27,7 +27,7 @@ UP007.py:10:10: UP007 [*] Use `X | Y` for type annotations
| |
= help: Convert to `X | Y` = help: Convert to `X | Y`
Possible fix Suggested fix
7 7 | ... 7 7 | ...
8 8 | 8 8 |
9 9 | 9 9 |
@ -45,7 +45,7 @@ UP007.py:14:10: UP007 [*] Use `X | Y` for type annotations
| |
= help: Convert to `X | Y` = help: Convert to `X | Y`
Possible fix Suggested fix
11 11 | ... 11 11 | ...
12 12 | 12 12 |
13 13 | 13 13 |
@ -63,7 +63,7 @@ UP007.py:14:26: UP007 [*] Use `X | Y` for type annotations
| |
= help: Convert to `X | Y` = help: Convert to `X | Y`
Possible fix Suggested fix
11 11 | ... 11 11 | ...
12 12 | 12 12 |
13 13 | 13 13 |
@ -81,7 +81,7 @@ UP007.py:18:10: UP007 [*] Use `X | Y` for type annotations
| |
= help: Convert to `X | Y` = help: Convert to `X | Y`
Possible fix Suggested fix
15 15 | ... 15 15 | ...
16 16 | 16 16 |
17 17 | 17 17 |
@ -99,7 +99,7 @@ UP007.py:22:10: UP007 [*] Use `X | Y` for type annotations
| |
= help: Convert to `X | Y` = help: Convert to `X | Y`
Possible fix Suggested fix
19 19 | ... 19 19 | ...
20 20 | 20 20 |
21 21 | 21 21 |
@ -117,127 +117,163 @@ UP007.py:26:10: UP007 [*] Use `X | Y` for type annotations
| |
= help: Convert to `X | Y` = help: Convert to `X | Y`
Possible fix Suggested fix
23 23 | ... 23 23 | ...
24 24 | 24 24 |
25 25 | 25 25 |
26 |-def f(x: typing.Union[(str, int), float]) -> None: 26 |-def f(x: typing.Union[(str, int), float]) -> None:
26 |+def f(x: (str, int) | float) -> None: 26 |+def f(x: str | int | float) -> None:
27 27 | ... 27 27 | ...
28 28 | 28 28 |
29 29 | 29 29 |
UP007.py:30:11: UP007 [*] Use `X | Y` for type annotations UP007.py:30:10: UP007 [*] Use `X | Y` for type annotations
| |
30 | def f(x: "Union[str, int, Union[float, bytes]]") -> None: 30 | def f(x: typing.Union[(int,)]) -> None:
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ UP007 | ^^^^^^^^^^^^^^^^^^^^ UP007
31 | ... 31 | ...
| |
= help: Convert to `X | Y` = help: Convert to `X | Y`
Possible fix Suggested fix
27 27 | ... 27 27 | ...
28 28 | 28 28 |
29 29 | 29 29 |
30 |-def f(x: "Union[str, int, Union[float, bytes]]") -> None: 30 |-def f(x: typing.Union[(int,)]) -> None:
30 |+def f(x: "str | int | Union[float, bytes]") -> None: 30 |+def f(x: int) -> None:
31 31 | ... 31 31 | ...
32 32 | 32 32 |
33 33 | 33 33 |
UP007.py:30:27: UP007 [*] Use `X | Y` for type annotations UP007.py:34:10: UP007 [*] Use `X | Y` for type annotations
| |
30 | def f(x: "Union[str, int, Union[float, bytes]]") -> None: 34 | def f(x: typing.Union[()]) -> None:
| ^^^^^^^^^^^^^^^^^^^ UP007 | ^^^^^^^^^^^^^^^^ UP007
31 | ...
|
= help: Convert to `X | Y`
Possible fix
27 27 | ...
28 28 |
29 29 |
30 |-def f(x: "Union[str, int, Union[float, bytes]]") -> None:
30 |+def f(x: "Union[str, int, float | bytes]") -> None:
31 31 | ...
32 32 |
33 33 |
UP007.py:34:11: UP007 [*] Use `X | Y` for type annotations
|
34 | def f(x: "typing.Union[str, int]") -> None:
| ^^^^^^^^^^^^^^^^^^^^^^ UP007
35 | ... 35 | ...
| |
= help: Convert to `X | Y` = help: Convert to `X | Y`
Possible fix Suggested fix
31 31 | ... 31 31 | ...
32 32 | 32 32 |
33 33 | 33 33 |
34 |-def f(x: "typing.Union[str, int]") -> None: 34 |-def f(x: typing.Union[()]) -> None:
34 |+def f(x: "str | int") -> None: 34 |+def f(x: ()) -> None:
35 35 | ... 35 35 | ...
36 36 | 36 36 |
37 37 | 37 37 |
UP007.py:47:8: UP007 [*] Use `X | Y` for type annotations UP007.py:38:11: UP007 [*] Use `X | Y` for type annotations
| |
46 | def f() -> None: 38 | def f(x: "Union[str, int, Union[float, bytes]]") -> None:
47 | x: Optional[str] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ UP007
| ^^^^^^^^^^^^^ UP007 39 | ...
48 | x = Optional[str]
| |
= help: Convert to `X | Y` = help: Convert to `X | Y`
Possible fix Suggested fix
35 35 | ...
36 36 |
37 37 |
38 |-def f(x: "Union[str, int, Union[float, bytes]]") -> None:
38 |+def f(x: "str | int | Union[float, bytes]") -> None:
39 39 | ...
40 40 |
41 41 |
UP007.py:38:27: UP007 [*] Use `X | Y` for type annotations
|
38 | def f(x: "Union[str, int, Union[float, bytes]]") -> None:
| ^^^^^^^^^^^^^^^^^^^ UP007
39 | ...
|
= help: Convert to `X | Y`
Suggested fix
35 35 | ...
36 36 |
37 37 |
38 |-def f(x: "Union[str, int, Union[float, bytes]]") -> None:
38 |+def f(x: "Union[str, int, float | bytes]") -> None:
39 39 | ...
40 40 |
41 41 |
UP007.py:42:11: UP007 [*] Use `X | Y` for type annotations
|
42 | def f(x: "typing.Union[str, int]") -> None:
| ^^^^^^^^^^^^^^^^^^^^^^ UP007
43 | ...
|
= help: Convert to `X | Y`
Suggested fix
39 39 | ...
40 40 |
41 41 |
42 |-def f(x: "typing.Union[str, int]") -> None:
42 |+def f(x: "str | int") -> None:
43 43 | ...
44 44 | 44 44 |
45 45 | 45 45 |
46 46 | def f() -> None:
47 |- x: Optional[str]
47 |+ x: str | None
48 48 | x = Optional[str]
49 49 |
50 50 | x = Union[str, int]
UP007.py:48:9: UP007 Use `X | Y` for type annotations UP007.py:55:8: UP007 [*] Use `X | Y` for type annotations
| |
46 | def f() -> None: 54 | def f() -> None:
47 | x: Optional[str] 55 | x: Optional[str]
48 | x = Optional[str] | ^^^^^^^^^^^^^ UP007
56 | x = Optional[str]
|
= help: Convert to `X | Y`
Suggested fix
52 52 |
53 53 |
54 54 | def f() -> None:
55 |- x: Optional[str]
55 |+ x: str | None
56 56 | x = Optional[str]
57 57 |
58 58 | x = Union[str, int]
UP007.py:56:9: UP007 Use `X | Y` for type annotations
|
54 | def f() -> None:
55 | x: Optional[str]
56 | x = Optional[str]
| ^^^^^^^^^^^^^ UP007 | ^^^^^^^^^^^^^ UP007
49 | 57 |
50 | x = Union[str, int] 58 | x = Union[str, int]
| |
= help: Convert to `X | Y` = help: Convert to `X | Y`
UP007.py:50:9: UP007 Use `X | Y` for type annotations UP007.py:58:9: UP007 Use `X | Y` for type annotations
| |
48 | x = Optional[str] 56 | x = Optional[str]
49 | 57 |
50 | x = Union[str, int] 58 | x = Union[str, int]
| ^^^^^^^^^^^^^^^ UP007 | ^^^^^^^^^^^^^^^ UP007
51 | x = Union["str", "int"] 59 | x = Union["str", "int"]
52 | x: Union[str, int] 60 | x: Union[str, int]
| |
= help: Convert to `X | Y` = help: Convert to `X | Y`
UP007.py:52:8: UP007 [*] Use `X | Y` for type annotations UP007.py:60:8: UP007 [*] Use `X | Y` for type annotations
| |
50 | x = Union[str, int] 58 | x = Union[str, int]
51 | x = Union["str", "int"] 59 | x = Union["str", "int"]
52 | x: Union[str, int] 60 | x: Union[str, int]
| ^^^^^^^^^^^^^^^ UP007 | ^^^^^^^^^^^^^^^ UP007
53 | x: Union["str", "int"] 61 | x: Union["str", "int"]
| |
= help: Convert to `X | Y` = help: Convert to `X | Y`
Possible fix Suggested fix
49 49 | 57 57 |
50 50 | x = Union[str, int] 58 58 | x = Union[str, int]
51 51 | x = Union["str", "int"] 59 59 | x = Union["str", "int"]
52 |- x: Union[str, int] 60 |- x: Union[str, int]
52 |+ x: str | int 60 |+ x: str | int
53 53 | x: Union["str", "int"] 61 61 | x: Union["str", "int"]

View File

@ -10,7 +10,7 @@ future_annotations.py:40:4: UP007 [*] Use `X | Y` for type annotations
| |
= help: Convert to `X | Y` = help: Convert to `X | Y`
Possible fix Suggested fix
37 37 | return y 37 37 | return y
38 38 | 38 38 |
39 39 | 39 39 |

View File

@ -10,7 +10,7 @@ future_annotations.py:40:4: UP007 [*] Use `X | Y` for type annotations
| |
= help: Convert to `X | Y` = help: Convert to `X | Y`
Possible fix Suggested fix
37 37 | return y 37 37 | return y
38 38 | 38 38 |
39 39 | 39 39 |
@ -28,7 +28,7 @@ future_annotations.py:42:21: UP007 [*] Use `X | Y` for type annotations
| |
= help: Convert to `X | Y` = help: Convert to `X | Y`
Possible fix Suggested fix
39 39 | 39 39 |
40 40 | x: Optional[int] = None 40 40 | x: Optional[int] = None
41 41 | 41 41 |