From abb3c6ea95a8366b416745d4f3e4a37f6a6bfcfd Mon Sep 17 00:00:00 2001 From: Simon Brugman Date: Thu, 28 Nov 2024 19:30:50 +0100 Subject: [PATCH] [`flake8-pyi`] Avoid rewriting invalid type expressions in `unnecessary-type-union` (PYI055) (#14660) --- .../test/fixtures/flake8_pyi/PYI055.py | 20 +++- .../test/fixtures/flake8_pyi/PYI055.pyi | 19 ++- .../rules/unnecessary_type_union.rs | 7 +- ...__flake8_pyi__tests__PYI055_PYI055.py.snap | 1 + ..._flake8_pyi__tests__PYI055_PYI055.pyi.snap | 113 +++++++++--------- 5 files changed, 92 insertions(+), 68 deletions(-) diff --git a/crates/ruff_linter/resources/test/fixtures/flake8_pyi/PYI055.py b/crates/ruff_linter/resources/test/fixtures/flake8_pyi/PYI055.py index 01bf820f007af0..7173a725a1e612 100644 --- a/crates/ruff_linter/resources/test/fixtures/flake8_pyi/PYI055.py +++ b/crates/ruff_linter/resources/test/fixtures/flake8_pyi/PYI055.py @@ -5,10 +5,10 @@ t: type[int] | type[str] | type[float] u: builtins.type[int] | type[str] | builtins.type[complex] v: Union[type[float], type[complex]] -w: Union[type[float, int], type[complex]] -x: Union[Union[type[float, int], type[complex]]] -y: Union[Union[Union[type[float, int], type[complex]]]] -z: Union[type[complex], Union[Union[type[float, int]]]] +w: Union[type[float | int], type[complex]] +x: Union[Union[type[Union[float, int]], type[complex]]] +y: Union[Union[Union[type[float | int], type[complex]]]] +z: Union[type[complex], Union[Union[type[Union[float, int]]]]] def func(arg: type[int] | str | type[float]) -> None: @@ -16,8 +16,8 @@ def func(arg: type[int] | str | type[float]) -> None: # OK -x: type[int, str, float] -y: builtins.type[int, str, complex] +x: type[int | str | float] +y: builtins.type[int | str | complex] z: Union[float, complex] @@ -68,3 +68,11 @@ def convert_union(union: UnionType) -> _T | None: Union[type[_T] | type[Converter[_T]] | str] | Converter[_T] | Callable[[str], _T], ... # PYI055 ] = union.__args__ ... + + +# `type[float, int]`` is not valid, use `type[float|int]` or `type[Union[float, int]]` +# OK for PYI055, should be covered by another check. +a: Union[type[float, int], type[complex]] +b: Union[Union[type[float, int], type[complex]]] +c: Union[Union[Union[type[float, int], type[complex]]]] +d: Union[type[complex], Union[Union[type[float, int]]]] diff --git a/crates/ruff_linter/resources/test/fixtures/flake8_pyi/PYI055.pyi b/crates/ruff_linter/resources/test/fixtures/flake8_pyi/PYI055.pyi index 4d8f6d793ea39d..516d2630b417c1 100644 --- a/crates/ruff_linter/resources/test/fixtures/flake8_pyi/PYI055.pyi +++ b/crates/ruff_linter/resources/test/fixtures/flake8_pyi/PYI055.pyi @@ -5,16 +5,16 @@ s: builtins.type[int] | builtins.type[str] | builtins.type[complex] t: type[int] | type[str] | type[float] u: builtins.type[int] | type[str] | builtins.type[complex] v: Union[type[float], type[complex]] -w: Union[type[float, int], type[complex]] -x: Union[Union[type[float, int], type[complex]]] -y: Union[Union[Union[type[float, int], type[complex]]]] -z: Union[type[complex], Union[Union[type[float, int]]]] +w: Union[type[Union[float, int]], type[complex]] +x: Union[Union[type[Union[float, int]], type[complex]]] +y: Union[Union[Union[type[Union[float, int]], type[complex]]]] +z: Union[type[complex], Union[Union[type[Union[float, int]]]]] def func(arg: type[int] | str | type[float]) -> None: ... # OK -x: type[int, str, float] -y: builtins.type[int, str, complex] +x: type[int | str | float] +y: builtins.type[int | str | complex] z: Union[float, complex] def func(arg: type[int, float] | str) -> None: ... @@ -29,3 +29,10 @@ def func(): item3: Union[ # comment type[requests_mock.Mocker], # another comment type[httpretty], type[str]] = requests_mock.Mocker + + +# OK +w: Union[type[float, int], type[complex]] +x: Union[Union[type[float, int], type[complex]]] +y: Union[Union[Union[type[float, int], type[complex]]]] +z: Union[type[complex], Union[Union[type[float, int]]]] diff --git a/crates/ruff_linter/src/rules/flake8_pyi/rules/unnecessary_type_union.rs b/crates/ruff_linter/src/rules/flake8_pyi/rules/unnecessary_type_union.rs index 2dec5021b66e91..c069e4d4f9be8a 100644 --- a/crates/ruff_linter/src/rules/flake8_pyi/rules/unnecessary_type_union.rs +++ b/crates/ruff_linter/src/rules/flake8_pyi/rules/unnecessary_type_union.rs @@ -89,7 +89,12 @@ pub(crate) fn unnecessary_type_union<'a>(checker: &mut Checker, union: &'a Expr) } match expr { Expr::Subscript(ast::ExprSubscript { slice, value, .. }) => { - if semantic.match_builtin_expr(value, "type") { + // The annotation `type[a, b]` is not valid since `type` accepts + // a single parameter. This likely is a confusion with `type[a | b]` or + // `type[Union[a, b]]`. Do not emit a diagnostic for invalid type + // annotations. + if !matches!(**slice, Expr::Tuple(_)) && semantic.match_builtin_expr(value, "type") + { type_exprs.push(slice); } else { other_exprs.push(expr); diff --git a/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI055_PYI055.py.snap b/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI055_PYI055.py.snap index 1506146651bdfc..e9dadd685d5b56 100644 --- a/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI055_PYI055.py.snap +++ b/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI055_PYI055.py.snap @@ -167,3 +167,4 @@ PYI055.py:68:9: PYI055 [*] Multiple `type` members in a union. Combine them into 68 |+ type[_T | Converter[_T]] | str | Converter[_T] | Callable[[str], _T], ... # PYI055 69 69 | ] = union.__args__ 70 70 | ... +71 71 | diff --git a/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI055_PYI055.pyi.snap b/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI055_PYI055.pyi.snap index 2d647bbcce5141..33dd7ff67dc76c 100644 --- a/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI055_PYI055.pyi.snap +++ b/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI055_PYI055.pyi.snap @@ -40,7 +40,7 @@ PYI055.pyi:5:4: PYI055 [*] Multiple `type` members in a union. Combine them into 5 |+t: type[int | str | float] 6 6 | u: builtins.type[int] | type[str] | builtins.type[complex] 7 7 | v: Union[type[float], type[complex]] -8 8 | w: Union[type[float, int], type[complex]] +8 8 | w: Union[type[Union[float, int]], type[complex]] PYI055.pyi:6:4: PYI055 [*] Multiple `type` members in a union. Combine them into one, e.g., `type[int | str | complex]`. | @@ -49,7 +49,7 @@ PYI055.pyi:6:4: PYI055 [*] Multiple `type` members in a union. Combine them into 6 | u: builtins.type[int] | type[str] | builtins.type[complex] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI055 7 | v: Union[type[float], type[complex]] -8 | w: Union[type[float, int], type[complex]] +8 | w: Union[type[Union[float, int]], type[complex]] | = help: Combine multiple `type` members @@ -60,8 +60,8 @@ PYI055.pyi:6:4: PYI055 [*] Multiple `type` members in a union. Combine them into 6 |-u: builtins.type[int] | type[str] | builtins.type[complex] 6 |+u: type[int | str | complex] 7 7 | v: Union[type[float], type[complex]] -8 8 | w: Union[type[float, int], type[complex]] -9 9 | x: Union[Union[type[float, int], type[complex]]] +8 8 | w: Union[type[Union[float, int]], type[complex]] +9 9 | x: Union[Union[type[Union[float, int]], type[complex]]] PYI055.pyi:7:4: PYI055 [*] Multiple `type` members in a union. Combine them into one, e.g., `type[Union[float, complex]]`. | @@ -69,8 +69,8 @@ PYI055.pyi:7:4: PYI055 [*] Multiple `type` members in a union. Combine them into 6 | u: builtins.type[int] | type[str] | builtins.type[complex] 7 | v: Union[type[float], type[complex]] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI055 -8 | w: Union[type[float, int], type[complex]] -9 | x: Union[Union[type[float, int], type[complex]]] +8 | w: Union[type[Union[float, int]], type[complex]] +9 | x: Union[Union[type[Union[float, int]], type[complex]]] | = help: Combine multiple `type` members @@ -80,18 +80,18 @@ PYI055.pyi:7:4: PYI055 [*] Multiple `type` members in a union. Combine them into 6 6 | u: builtins.type[int] | type[str] | builtins.type[complex] 7 |-v: Union[type[float], type[complex]] 7 |+v: type[Union[float, complex]] -8 8 | w: Union[type[float, int], type[complex]] -9 9 | x: Union[Union[type[float, int], type[complex]]] -10 10 | y: Union[Union[Union[type[float, int], type[complex]]]] +8 8 | w: Union[type[Union[float, int]], type[complex]] +9 9 | x: Union[Union[type[Union[float, int]], type[complex]]] +10 10 | y: Union[Union[Union[type[Union[float, int]], type[complex]]]] -PYI055.pyi:8:4: PYI055 [*] Multiple `type` members in a union. Combine them into one, e.g., `type[Union[float, int, complex]]`. +PYI055.pyi:8:4: PYI055 [*] Multiple `type` members in a union. Combine them into one, e.g., `type[Union[Union[float, int], complex]]`. | 6 | u: builtins.type[int] | type[str] | builtins.type[complex] 7 | v: Union[type[float], type[complex]] - 8 | w: Union[type[float, int], type[complex]] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI055 - 9 | x: Union[Union[type[float, int], type[complex]]] -10 | y: Union[Union[Union[type[float, int], type[complex]]]] + 8 | w: Union[type[Union[float, int]], type[complex]] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI055 + 9 | x: Union[Union[type[Union[float, int]], type[complex]]] +10 | y: Union[Union[Union[type[Union[float, int]], type[complex]]]] | = help: Combine multiple `type` members @@ -99,77 +99,77 @@ PYI055.pyi:8:4: PYI055 [*] Multiple `type` members in a union. Combine them into 5 5 | t: type[int] | type[str] | type[float] 6 6 | u: builtins.type[int] | type[str] | builtins.type[complex] 7 7 | v: Union[type[float], type[complex]] -8 |-w: Union[type[float, int], type[complex]] - 8 |+w: type[Union[float, int, complex]] -9 9 | x: Union[Union[type[float, int], type[complex]]] -10 10 | y: Union[Union[Union[type[float, int], type[complex]]]] -11 11 | z: Union[type[complex], Union[Union[type[float, int]]]] +8 |-w: Union[type[Union[float, int]], type[complex]] + 8 |+w: type[Union[Union[float, int], complex]] +9 9 | x: Union[Union[type[Union[float, int]], type[complex]]] +10 10 | y: Union[Union[Union[type[Union[float, int]], type[complex]]]] +11 11 | z: Union[type[complex], Union[Union[type[Union[float, int]]]]] -PYI055.pyi:9:4: PYI055 [*] Multiple `type` members in a union. Combine them into one, e.g., `type[Union[float, int, complex]]`. +PYI055.pyi:9:4: PYI055 [*] Multiple `type` members in a union. Combine them into one, e.g., `type[Union[Union[float, int], complex]]`. | 7 | v: Union[type[float], type[complex]] - 8 | w: Union[type[float, int], type[complex]] - 9 | x: Union[Union[type[float, int], type[complex]]] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI055 -10 | y: Union[Union[Union[type[float, int], type[complex]]]] -11 | z: Union[type[complex], Union[Union[type[float, int]]]] + 8 | w: Union[type[Union[float, int]], type[complex]] + 9 | x: Union[Union[type[Union[float, int]], type[complex]]] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI055 +10 | y: Union[Union[Union[type[Union[float, int]], type[complex]]]] +11 | z: Union[type[complex], Union[Union[type[Union[float, int]]]]] | = help: Combine multiple `type` members ℹ Safe fix 6 6 | u: builtins.type[int] | type[str] | builtins.type[complex] 7 7 | v: Union[type[float], type[complex]] -8 8 | w: Union[type[float, int], type[complex]] -9 |-x: Union[Union[type[float, int], type[complex]]] - 9 |+x: type[Union[float, int, complex]] -10 10 | y: Union[Union[Union[type[float, int], type[complex]]]] -11 11 | z: Union[type[complex], Union[Union[type[float, int]]]] +8 8 | w: Union[type[Union[float, int]], type[complex]] +9 |-x: Union[Union[type[Union[float, int]], type[complex]]] + 9 |+x: type[Union[Union[float, int], complex]] +10 10 | y: Union[Union[Union[type[Union[float, int]], type[complex]]]] +11 11 | z: Union[type[complex], Union[Union[type[Union[float, int]]]]] 12 12 | -PYI055.pyi:10:4: PYI055 [*] Multiple `type` members in a union. Combine them into one, e.g., `type[Union[float, int, complex]]`. +PYI055.pyi:10:4: PYI055 [*] Multiple `type` members in a union. Combine them into one, e.g., `type[Union[Union[float, int], complex]]`. | - 8 | w: Union[type[float, int], type[complex]] - 9 | x: Union[Union[type[float, int], type[complex]]] -10 | y: Union[Union[Union[type[float, int], type[complex]]]] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI055 -11 | z: Union[type[complex], Union[Union[type[float, int]]]] + 8 | w: Union[type[Union[float, int]], type[complex]] + 9 | x: Union[Union[type[Union[float, int]], type[complex]]] +10 | y: Union[Union[Union[type[Union[float, int]], type[complex]]]] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI055 +11 | z: Union[type[complex], Union[Union[type[Union[float, int]]]]] | = help: Combine multiple `type` members ℹ Safe fix 7 7 | v: Union[type[float], type[complex]] -8 8 | w: Union[type[float, int], type[complex]] -9 9 | x: Union[Union[type[float, int], type[complex]]] -10 |-y: Union[Union[Union[type[float, int], type[complex]]]] - 10 |+y: type[Union[float, int, complex]] -11 11 | z: Union[type[complex], Union[Union[type[float, int]]]] +8 8 | w: Union[type[Union[float, int]], type[complex]] +9 9 | x: Union[Union[type[Union[float, int]], type[complex]]] +10 |-y: Union[Union[Union[type[Union[float, int]], type[complex]]]] + 10 |+y: type[Union[Union[float, int], complex]] +11 11 | z: Union[type[complex], Union[Union[type[Union[float, int]]]]] 12 12 | 13 13 | def func(arg: type[int] | str | type[float]) -> None: ... -PYI055.pyi:11:4: PYI055 [*] Multiple `type` members in a union. Combine them into one, e.g., `type[Union[complex, float, int]]`. +PYI055.pyi:11:4: PYI055 [*] Multiple `type` members in a union. Combine them into one, e.g., `type[Union[complex, Union[float, int]]]`. | - 9 | x: Union[Union[type[float, int], type[complex]]] -10 | y: Union[Union[Union[type[float, int], type[complex]]]] -11 | z: Union[type[complex], Union[Union[type[float, int]]]] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI055 + 9 | x: Union[Union[type[Union[float, int]], type[complex]]] +10 | y: Union[Union[Union[type[Union[float, int]], type[complex]]]] +11 | z: Union[type[complex], Union[Union[type[Union[float, int]]]]] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI055 12 | 13 | def func(arg: type[int] | str | type[float]) -> None: ... | = help: Combine multiple `type` members ℹ Safe fix -8 8 | w: Union[type[float, int], type[complex]] -9 9 | x: Union[Union[type[float, int], type[complex]]] -10 10 | y: Union[Union[Union[type[float, int], type[complex]]]] -11 |-z: Union[type[complex], Union[Union[type[float, int]]]] - 11 |+z: type[Union[complex, float, int]] +8 8 | w: Union[type[Union[float, int]], type[complex]] +9 9 | x: Union[Union[type[Union[float, int]], type[complex]]] +10 10 | y: Union[Union[Union[type[Union[float, int]], type[complex]]]] +11 |-z: Union[type[complex], Union[Union[type[Union[float, int]]]]] + 11 |+z: type[Union[complex, Union[float, int]]] 12 12 | 13 13 | def func(arg: type[int] | str | type[float]) -> None: ... 14 14 | PYI055.pyi:13:15: PYI055 [*] Multiple `type` members in a union. Combine them into one, e.g., `type[int | float]`. | -11 | z: Union[type[complex], Union[Union[type[float, int]]]] +11 | z: Union[type[complex], Union[Union[type[Union[float, int]]]]] 12 | 13 | def func(arg: type[int] | str | type[float]) -> None: ... | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI055 @@ -179,14 +179,14 @@ PYI055.pyi:13:15: PYI055 [*] Multiple `type` members in a union. Combine them in = help: Combine multiple `type` members ℹ Safe fix -10 10 | y: Union[Union[Union[type[float, int], type[complex]]]] -11 11 | z: Union[type[complex], Union[Union[type[float, int]]]] +10 10 | y: Union[Union[Union[type[Union[float, int]], type[complex]]]] +11 11 | z: Union[type[complex], Union[Union[type[Union[float, int]]]]] 12 12 | 13 |-def func(arg: type[int] | str | type[float]) -> None: ... 13 |+def func(arg: type[int | float] | str) -> None: ... 14 14 | 15 15 | # OK -16 16 | x: type[int, str, float] +16 16 | x: type[int | str | float] PYI055.pyi:23:7: PYI055 [*] Multiple `type` members in a union. Combine them into one, e.g., `type[requests_mock.Mocker | httpretty]`. | @@ -270,3 +270,6 @@ PYI055.pyi:29:12: PYI055 [*] Multiple `type` members in a union. Combine them in 30 |- type[requests_mock.Mocker], # another comment 31 |- type[httpretty], type[str]] = requests_mock.Mocker 29 |+ item3: type[Union[requests_mock.Mocker, httpretty, str]] = requests_mock.Mocker +32 30 | +33 31 | +34 32 | # OK