From e36611c4d817c105741745e86c5b47149d4c98a9 Mon Sep 17 00:00:00 2001 From: Robsdedude Date: Fri, 20 Jun 2025 19:04:51 +0000 Subject: [PATCH] [`flake8_pyi`] Fix `PYI041`'s fix causing TypeError with `None | None | ...` (#18637) ## Summary Fix `PYI041`'s fix turning `None | int | None | float` into `None | None | float`, which raises a `TypeError` when executed. The fix consists of making sure that the merged super-type is inserted where the first type that is merged was before. ## Test Plan Tests have been expanded with examples from the issue. ## Related Issue Fixes https://github.com/astral-sh/ruff/issues/18298 --- .../flake8_pyi/{PYI041.py => PYI041_1.py} | 20 + .../flake8_pyi/{PYI041.pyi => PYI041_1.pyi} | 8 + .../test/fixtures/flake8_pyi/PYI041_2.py | 8 + .../ruff_linter/src/rules/flake8_pyi/mod.rs | 5 +- .../rules/redundant_numeric_union.rs | 14 + ...__flake8_pyi__tests__PYI041_PYI041.py.snap | 311 --------------- ...flake8_pyi__tests__PYI041_PYI041_1.py.snap | 361 ++++++++++++++++++ ...ake8_pyi__tests__PYI041_PYI041_1.pyi.snap} | 73 +++- ...flake8_pyi__tests__PYI041_PYI041_2.py.snap | 12 + 9 files changed, 482 insertions(+), 330 deletions(-) rename crates/ruff_linter/resources/test/fixtures/flake8_pyi/{PYI041.py => PYI041_1.py} (74%) rename crates/ruff_linter/resources/test/fixtures/flake8_pyi/{PYI041.pyi => PYI041_1.pyi} (84%) create mode 100644 crates/ruff_linter/resources/test/fixtures/flake8_pyi/PYI041_2.py delete mode 100644 crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI041_PYI041.py.snap create mode 100644 crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI041_PYI041_1.py.snap rename crates/ruff_linter/src/rules/flake8_pyi/snapshots/{ruff_linter__rules__flake8_pyi__tests__PYI041_PYI041.pyi.snap => ruff_linter__rules__flake8_pyi__tests__PYI041_PYI041_1.pyi.snap} (76%) create mode 100644 crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI041_PYI041_2.py.snap diff --git a/crates/ruff_linter/resources/test/fixtures/flake8_pyi/PYI041.py b/crates/ruff_linter/resources/test/fixtures/flake8_pyi/PYI041_1.py similarity index 74% rename from crates/ruff_linter/resources/test/fixtures/flake8_pyi/PYI041.py rename to crates/ruff_linter/resources/test/fixtures/flake8_pyi/PYI041_1.py index c5b367644d..b952f46ca0 100644 --- a/crates/ruff_linter/resources/test/fixtures/flake8_pyi/PYI041.py +++ b/crates/ruff_linter/resources/test/fixtures/flake8_pyi/PYI041_1.py @@ -1,4 +1,5 @@ from typing import ( + TYPE_CHECKING, Union, ) @@ -90,3 +91,22 @@ class Foo: def bad5(self, arg: int | (float | complex)) -> None: ... + + +# https://github.com/astral-sh/ruff/issues/18298 +# fix must not yield runtime `None | None | ...` (TypeError) +class Issue18298: + def f1(self, arg: None | int | None | float = None) -> None: # PYI041 - no fix + pass + + if TYPE_CHECKING: + + def f2(self, arg: None | int | None | float = None) -> None: ... # PYI041 - with fix + + else: + + def f2(self, arg=None) -> None: + pass + + def f3(self, arg: None | float | None | int | None = None) -> None: # PYI041 - with fix + pass diff --git a/crates/ruff_linter/resources/test/fixtures/flake8_pyi/PYI041.pyi b/crates/ruff_linter/resources/test/fixtures/flake8_pyi/PYI041_1.pyi similarity index 84% rename from crates/ruff_linter/resources/test/fixtures/flake8_pyi/PYI041.pyi rename to crates/ruff_linter/resources/test/fixtures/flake8_pyi/PYI041_1.pyi index e465c3eb90..22acb5571a 100644 --- a/crates/ruff_linter/resources/test/fixtures/flake8_pyi/PYI041.pyi +++ b/crates/ruff_linter/resources/test/fixtures/flake8_pyi/PYI041_1.pyi @@ -70,3 +70,11 @@ class Foo: def bad4(self, arg: Union[float | complex, int]) -> None: ... # PYI041 def bad5(self, arg: int | (float | complex)) -> None: ... # PYI041 + + +# https://github.com/astral-sh/ruff/issues/18298 +# fix must not yield runtime `None | None | ...` (TypeError) +class Issue18298: + def f1(self, arg: None | int | None | float = None) -> None: ... # PYI041 - with fix + + def f3(self, arg: None | float | None | int | None = None) -> None: ... # PYI041 - with fix diff --git a/crates/ruff_linter/resources/test/fixtures/flake8_pyi/PYI041_2.py b/crates/ruff_linter/resources/test/fixtures/flake8_pyi/PYI041_2.py new file mode 100644 index 0000000000..af92b93f8c --- /dev/null +++ b/crates/ruff_linter/resources/test/fixtures/flake8_pyi/PYI041_2.py @@ -0,0 +1,8 @@ +from __future__ import annotations + + +# https://github.com/astral-sh/ruff/issues/18298 +# fix must not yield runtime `None | None | ...` (TypeError) +class Issue18298: + def f1(self, arg: None | int | None | float = None) -> None: + pass diff --git a/crates/ruff_linter/src/rules/flake8_pyi/mod.rs b/crates/ruff_linter/src/rules/flake8_pyi/mod.rs index dd94cc2f14..c90aca969b 100644 --- a/crates/ruff_linter/src/rules/flake8_pyi/mod.rs +++ b/crates/ruff_linter/src/rules/flake8_pyi/mod.rs @@ -72,8 +72,9 @@ mod tests { #[test_case(Rule::RedundantFinalLiteral, Path::new("PYI064.pyi"))] #[test_case(Rule::RedundantLiteralUnion, Path::new("PYI051.py"))] #[test_case(Rule::RedundantLiteralUnion, Path::new("PYI051.pyi"))] - #[test_case(Rule::RedundantNumericUnion, Path::new("PYI041.py"))] - #[test_case(Rule::RedundantNumericUnion, Path::new("PYI041.pyi"))] + #[test_case(Rule::RedundantNumericUnion, Path::new("PYI041_1.py"))] + #[test_case(Rule::RedundantNumericUnion, Path::new("PYI041_1.pyi"))] + #[test_case(Rule::RedundantNumericUnion, Path::new("PYI041_2.py"))] #[test_case(Rule::SnakeCaseTypeAlias, Path::new("PYI042.py"))] #[test_case(Rule::SnakeCaseTypeAlias, Path::new("PYI042.pyi"))] #[test_case(Rule::StrOrReprDefinedInStub, Path::new("PYI029.py"))] diff --git a/crates/ruff_linter/src/rules/flake8_pyi/rules/redundant_numeric_union.rs b/crates/ruff_linter/src/rules/flake8_pyi/rules/redundant_numeric_union.rs index 8c22d3b009..b493474929 100644 --- a/crates/ruff_linter/src/rules/flake8_pyi/rules/redundant_numeric_union.rs +++ b/crates/ruff_linter/src/rules/flake8_pyi/rules/redundant_numeric_union.rs @@ -132,6 +132,15 @@ fn check_annotation<'a>(checker: &Checker, annotation: &'a Expr) { let mut diagnostic = checker.report_diagnostic(RedundantNumericUnion { redundancy }, annotation.range()); + if !checker.semantic().execution_context().is_typing() + && !checker.source_type.is_stub() + && fix_starts_with_none_none(&necessary_nodes) + { + // If there are multiple `None` literals, we cannot apply the fix in a runtime context. + // E.g., `None | None | int` will cause a `RuntimeError`. + return; + } + // Mark [`Fix`] as unsafe when comments are in range. let applicability = if checker.comment_ranges().intersects(annotation.range()) { Applicability::Unsafe @@ -265,3 +274,8 @@ fn generate_pep604_fix( applicability, ) } + +/// Check whether the proposed fix starts with two `None` literals. +fn fix_starts_with_none_none(nodes: &[&Expr]) -> bool { + nodes.len() >= 2 && nodes.iter().take(2).all(|node| node.is_none_literal_expr()) +} diff --git a/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI041_PYI041.py.snap b/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI041_PYI041.py.snap deleted file mode 100644 index b4680e7da8..0000000000 --- a/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI041_PYI041.py.snap +++ /dev/null @@ -1,311 +0,0 @@ ---- -source: crates/ruff_linter/src/rules/flake8_pyi/mod.rs -snapshot_kind: text ---- -PYI041.py:22:14: PYI041 [*] Use `float` instead of `int | float` - | -22 | def f0(arg1: float | int) -> None: - | ^^^^^^^^^^^ PYI041 -23 | ... - | - = help: Remove redundant type - -ℹ Safe fix -19 19 | ... -20 20 | -21 21 | -22 |-def f0(arg1: float | int) -> None: - 22 |+def f0(arg1: float) -> None: -23 23 | ... -24 24 | -25 25 | - -PYI041.py:26:30: PYI041 [*] Use `complex` instead of `float | complex` - | -26 | def f1(arg1: float, *, arg2: float | list[str] | type[bool] | complex) -> None: - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI041 -27 | ... - | - = help: Remove redundant type - -ℹ Safe fix -23 23 | ... -24 24 | -25 25 | -26 |-def f1(arg1: float, *, arg2: float | list[str] | type[bool] | complex) -> None: - 26 |+def f1(arg1: float, *, arg2: list[str] | type[bool] | complex) -> None: -27 27 | ... -28 28 | -29 29 | - -PYI041.py:30:28: PYI041 [*] Use `float` instead of `int | float` - | -30 | def f2(arg1: int, /, arg2: int | int | float) -> None: - | ^^^^^^^^^^^^^^^^^ PYI041 -31 | ... - | - = help: Remove redundant type - -ℹ Safe fix -27 27 | ... -28 28 | -29 29 | -30 |-def f2(arg1: int, /, arg2: int | int | float) -> None: - 30 |+def f2(arg1: int, /, arg2: float) -> None: -31 31 | ... -32 32 | -33 33 | - -PYI041.py:34:26: PYI041 [*] Use `float` instead of `int | float` - | -34 | def f3(arg1: int, *args: Union[int | int | float]) -> None: - | ^^^^^^^^^^^^^^^^^^^^^^^^ PYI041 -35 | ... - | - = help: Remove redundant type - -ℹ Safe fix -31 31 | ... -32 32 | -33 33 | -34 |-def f3(arg1: int, *args: Union[int | int | float]) -> None: - 34 |+def f3(arg1: int, *args: float) -> None: -35 35 | ... -36 36 | -37 37 | - -PYI041.py:38:24: PYI041 [*] Use `float` instead of `int | float` - | -38 | async def f4(**kwargs: int | int | float) -> None: - | ^^^^^^^^^^^^^^^^^ PYI041 -39 | ... - | - = help: Remove redundant type - -ℹ Safe fix -35 35 | ... -36 36 | -37 37 | -38 |-async def f4(**kwargs: int | int | float) -> None: - 38 |+async def f4(**kwargs: float) -> None: -39 39 | ... -40 40 | -41 41 | - -PYI041.py:42:26: PYI041 [*] Use `float` instead of `int | float` - | -42 | def f5(arg1: int, *args: Union[int, int, float]) -> None: - | ^^^^^^^^^^^^^^^^^^^^^^ PYI041 -43 | ... - | - = help: Remove redundant type - -ℹ Safe fix -39 39 | ... -40 40 | -41 41 | -42 |-def f5(arg1: int, *args: Union[int, int, float]) -> None: - 42 |+def f5(arg1: int, *args: float) -> None: -43 43 | ... -44 44 | -45 45 | - -PYI041.py:46:26: PYI041 [*] Use `float` instead of `int | float` - | -46 | def f6(arg1: int, *args: Union[Union[int, int, float]]) -> None: - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI041 -47 | ... - | - = help: Remove redundant type - -ℹ Safe fix -43 43 | ... -44 44 | -45 45 | -46 |-def f6(arg1: int, *args: Union[Union[int, int, float]]) -> None: - 46 |+def f6(arg1: int, *args: float) -> None: -47 47 | ... -48 48 | -49 49 | - -PYI041.py:50:26: PYI041 [*] Use `float` instead of `int | float` - | -50 | def f7(arg1: int, *args: Union[Union[Union[int, int, float]]]) -> None: - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI041 -51 | ... - | - = help: Remove redundant type - -ℹ Safe fix -47 47 | ... -48 48 | -49 49 | -50 |-def f7(arg1: int, *args: Union[Union[Union[int, int, float]]]) -> None: - 50 |+def f7(arg1: int, *args: float) -> None: -51 51 | ... -52 52 | -53 53 | - -PYI041.py:54:26: PYI041 [*] Use `float` instead of `int | float` - | -54 | def f8(arg1: int, *args: Union[Union[Union[int | int | float]]]) -> None: - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI041 -55 | ... - | - = help: Remove redundant type - -ℹ Safe fix -51 51 | ... -52 52 | -53 53 | -54 |-def f8(arg1: int, *args: Union[Union[Union[int | int | float]]]) -> None: - 54 |+def f8(arg1: int, *args: float) -> None: -55 55 | ... -56 56 | -57 57 | - -PYI041.py:59:10: PYI041 [*] Use `complex` instead of `int | float | complex` - | -58 | def f9( -59 | arg: Union[ # comment - | __________^ -60 | | float, # another -61 | | complex, int] - | |_____________________^ PYI041 -62 | ) -> None: -63 | ... - | - = help: Remove redundant type - -ℹ Unsafe fix -56 56 | -57 57 | -58 58 | def f9( -59 |- arg: Union[ # comment -60 |- float, # another -61 |- complex, int] - 59 |+ arg: complex -62 60 | ) -> None: -63 61 | ... -64 62 | - -PYI041.py:67:9: PYI041 [*] Use `complex` instead of `int | float | complex` - | -65 | def f10( -66 | arg: ( -67 | / int | # comment -68 | | float | # another -69 | | complex - | |_______________^ PYI041 -70 | ) -71 | ) -> None: - | - = help: Remove redundant type - -ℹ Unsafe fix -64 64 | -65 65 | def f10( -66 66 | arg: ( -67 |- int | # comment -68 |- float | # another -69 67 | complex -70 68 | ) -71 69 | ) -> None: - -PYI041.py:79:24: PYI041 [*] Use `complex` instead of `int | float | complex` - | -77 | ... -78 | -79 | def bad(self, arg: int | float | complex) -> None: - | ^^^^^^^^^^^^^^^^^^^^^ PYI041 -80 | ... - | - = help: Remove redundant type - -ℹ Safe fix -76 76 | def good(self, arg: int) -> None: -77 77 | ... -78 78 | -79 |- def bad(self, arg: int | float | complex) -> None: - 79 |+ def bad(self, arg: complex) -> None: -80 80 | ... -81 81 | -82 82 | def bad2(self, arg: int | Union[float, complex]) -> None: - -PYI041.py:82:25: PYI041 [*] Use `complex` instead of `int | float | complex` - | -80 | ... -81 | -82 | def bad2(self, arg: int | Union[float, complex]) -> None: - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI041 -83 | ... - | - = help: Remove redundant type - -ℹ Safe fix -79 79 | def bad(self, arg: int | float | complex) -> None: -80 80 | ... -81 81 | -82 |- def bad2(self, arg: int | Union[float, complex]) -> None: - 82 |+ def bad2(self, arg: complex) -> None: -83 83 | ... -84 84 | -85 85 | def bad3(self, arg: Union[Union[float, complex], int]) -> None: - -PYI041.py:85:25: PYI041 [*] Use `complex` instead of `int | float | complex` - | -83 | ... -84 | -85 | def bad3(self, arg: Union[Union[float, complex], int]) -> None: - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI041 -86 | ... - | - = help: Remove redundant type - -ℹ Safe fix -82 82 | def bad2(self, arg: int | Union[float, complex]) -> None: -83 83 | ... -84 84 | -85 |- def bad3(self, arg: Union[Union[float, complex], int]) -> None: - 85 |+ def bad3(self, arg: complex) -> None: -86 86 | ... -87 87 | -88 88 | def bad4(self, arg: Union[float | complex, int]) -> None: - -PYI041.py:88:25: PYI041 [*] Use `complex` instead of `int | float | complex` - | -86 | ... -87 | -88 | def bad4(self, arg: Union[float | complex, int]) -> None: - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI041 -89 | ... - | - = help: Remove redundant type - -ℹ Safe fix -85 85 | def bad3(self, arg: Union[Union[float, complex], int]) -> None: -86 86 | ... -87 87 | -88 |- def bad4(self, arg: Union[float | complex, int]) -> None: - 88 |+ def bad4(self, arg: complex) -> None: -89 89 | ... -90 90 | -91 91 | def bad5(self, arg: int | (float | complex)) -> None: - -PYI041.py:91:25: PYI041 [*] Use `complex` instead of `int | float | complex` - | -89 | ... -90 | -91 | def bad5(self, arg: int | (float | complex)) -> None: - | ^^^^^^^^^^^^^^^^^^^^^^^ PYI041 -92 | ... - | - = help: Remove redundant type - -ℹ Safe fix -88 88 | def bad4(self, arg: Union[float | complex, int]) -> None: -89 89 | ... -90 90 | -91 |- def bad5(self, arg: int | (float | complex)) -> None: - 91 |+ def bad5(self, arg: complex) -> None: -92 92 | ... diff --git a/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI041_PYI041_1.py.snap b/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI041_PYI041_1.py.snap new file mode 100644 index 0000000000..a161e8d6f8 --- /dev/null +++ b/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI041_PYI041_1.py.snap @@ -0,0 +1,361 @@ +--- +source: crates/ruff_linter/src/rules/flake8_pyi/mod.rs +--- +PYI041_1.py:23:14: PYI041 [*] Use `float` instead of `int | float` + | +23 | def f0(arg1: float | int) -> None: + | ^^^^^^^^^^^ PYI041 +24 | ... + | + = help: Remove redundant type + +ℹ Safe fix +20 20 | ... +21 21 | +22 22 | +23 |-def f0(arg1: float | int) -> None: + 23 |+def f0(arg1: float) -> None: +24 24 | ... +25 25 | +26 26 | + +PYI041_1.py:27:30: PYI041 [*] Use `complex` instead of `float | complex` + | +27 | def f1(arg1: float, *, arg2: float | list[str] | type[bool] | complex) -> None: + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI041 +28 | ... + | + = help: Remove redundant type + +ℹ Safe fix +24 24 | ... +25 25 | +26 26 | +27 |-def f1(arg1: float, *, arg2: float | list[str] | type[bool] | complex) -> None: + 27 |+def f1(arg1: float, *, arg2: list[str] | type[bool] | complex) -> None: +28 28 | ... +29 29 | +30 30 | + +PYI041_1.py:31:28: PYI041 [*] Use `float` instead of `int | float` + | +31 | def f2(arg1: int, /, arg2: int | int | float) -> None: + | ^^^^^^^^^^^^^^^^^ PYI041 +32 | ... + | + = help: Remove redundant type + +ℹ Safe fix +28 28 | ... +29 29 | +30 30 | +31 |-def f2(arg1: int, /, arg2: int | int | float) -> None: + 31 |+def f2(arg1: int, /, arg2: float) -> None: +32 32 | ... +33 33 | +34 34 | + +PYI041_1.py:35:26: PYI041 [*] Use `float` instead of `int | float` + | +35 | def f3(arg1: int, *args: Union[int | int | float]) -> None: + | ^^^^^^^^^^^^^^^^^^^^^^^^ PYI041 +36 | ... + | + = help: Remove redundant type + +ℹ Safe fix +32 32 | ... +33 33 | +34 34 | +35 |-def f3(arg1: int, *args: Union[int | int | float]) -> None: + 35 |+def f3(arg1: int, *args: float) -> None: +36 36 | ... +37 37 | +38 38 | + +PYI041_1.py:39:24: PYI041 [*] Use `float` instead of `int | float` + | +39 | async def f4(**kwargs: int | int | float) -> None: + | ^^^^^^^^^^^^^^^^^ PYI041 +40 | ... + | + = help: Remove redundant type + +ℹ Safe fix +36 36 | ... +37 37 | +38 38 | +39 |-async def f4(**kwargs: int | int | float) -> None: + 39 |+async def f4(**kwargs: float) -> None: +40 40 | ... +41 41 | +42 42 | + +PYI041_1.py:43:26: PYI041 [*] Use `float` instead of `int | float` + | +43 | def f5(arg1: int, *args: Union[int, int, float]) -> None: + | ^^^^^^^^^^^^^^^^^^^^^^ PYI041 +44 | ... + | + = help: Remove redundant type + +ℹ Safe fix +40 40 | ... +41 41 | +42 42 | +43 |-def f5(arg1: int, *args: Union[int, int, float]) -> None: + 43 |+def f5(arg1: int, *args: float) -> None: +44 44 | ... +45 45 | +46 46 | + +PYI041_1.py:47:26: PYI041 [*] Use `float` instead of `int | float` + | +47 | def f6(arg1: int, *args: Union[Union[int, int, float]]) -> None: + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI041 +48 | ... + | + = help: Remove redundant type + +ℹ Safe fix +44 44 | ... +45 45 | +46 46 | +47 |-def f6(arg1: int, *args: Union[Union[int, int, float]]) -> None: + 47 |+def f6(arg1: int, *args: float) -> None: +48 48 | ... +49 49 | +50 50 | + +PYI041_1.py:51:26: PYI041 [*] Use `float` instead of `int | float` + | +51 | def f7(arg1: int, *args: Union[Union[Union[int, int, float]]]) -> None: + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI041 +52 | ... + | + = help: Remove redundant type + +ℹ Safe fix +48 48 | ... +49 49 | +50 50 | +51 |-def f7(arg1: int, *args: Union[Union[Union[int, int, float]]]) -> None: + 51 |+def f7(arg1: int, *args: float) -> None: +52 52 | ... +53 53 | +54 54 | + +PYI041_1.py:55:26: PYI041 [*] Use `float` instead of `int | float` + | +55 | def f8(arg1: int, *args: Union[Union[Union[int | int | float]]]) -> None: + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI041 +56 | ... + | + = help: Remove redundant type + +ℹ Safe fix +52 52 | ... +53 53 | +54 54 | +55 |-def f8(arg1: int, *args: Union[Union[Union[int | int | float]]]) -> None: + 55 |+def f8(arg1: int, *args: float) -> None: +56 56 | ... +57 57 | +58 58 | + +PYI041_1.py:60:10: PYI041 [*] Use `complex` instead of `int | float | complex` + | +59 | def f9( +60 | arg: Union[ # comment + | __________^ +61 | | float, # another +62 | | complex, int] + | |_____________________^ PYI041 +63 | ) -> None: +64 | ... + | + = help: Remove redundant type + +ℹ Unsafe fix +57 57 | +58 58 | +59 59 | def f9( +60 |- arg: Union[ # comment +61 |- float, # another +62 |- complex, int] + 60 |+ arg: complex +63 61 | ) -> None: +64 62 | ... +65 63 | + +PYI041_1.py:68:9: PYI041 [*] Use `complex` instead of `int | float | complex` + | +66 | def f10( +67 | arg: ( +68 | / int | # comment +69 | | float | # another +70 | | complex + | |_______________^ PYI041 +71 | ) +72 | ) -> None: + | + = help: Remove redundant type + +ℹ Unsafe fix +65 65 | +66 66 | def f10( +67 67 | arg: ( +68 |- int | # comment +69 |- float | # another +70 68 | complex +71 69 | ) +72 70 | ) -> None: + +PYI041_1.py:80:24: PYI041 [*] Use `complex` instead of `int | float | complex` + | +78 | ... +79 | +80 | def bad(self, arg: int | float | complex) -> None: + | ^^^^^^^^^^^^^^^^^^^^^ PYI041 +81 | ... + | + = help: Remove redundant type + +ℹ Safe fix +77 77 | def good(self, arg: int) -> None: +78 78 | ... +79 79 | +80 |- def bad(self, arg: int | float | complex) -> None: + 80 |+ def bad(self, arg: complex) -> None: +81 81 | ... +82 82 | +83 83 | def bad2(self, arg: int | Union[float, complex]) -> None: + +PYI041_1.py:83:25: PYI041 [*] Use `complex` instead of `int | float | complex` + | +81 | ... +82 | +83 | def bad2(self, arg: int | Union[float, complex]) -> None: + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI041 +84 | ... + | + = help: Remove redundant type + +ℹ Safe fix +80 80 | def bad(self, arg: int | float | complex) -> None: +81 81 | ... +82 82 | +83 |- def bad2(self, arg: int | Union[float, complex]) -> None: + 83 |+ def bad2(self, arg: complex) -> None: +84 84 | ... +85 85 | +86 86 | def bad3(self, arg: Union[Union[float, complex], int]) -> None: + +PYI041_1.py:86:25: PYI041 [*] Use `complex` instead of `int | float | complex` + | +84 | ... +85 | +86 | def bad3(self, arg: Union[Union[float, complex], int]) -> None: + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI041 +87 | ... + | + = help: Remove redundant type + +ℹ Safe fix +83 83 | def bad2(self, arg: int | Union[float, complex]) -> None: +84 84 | ... +85 85 | +86 |- def bad3(self, arg: Union[Union[float, complex], int]) -> None: + 86 |+ def bad3(self, arg: complex) -> None: +87 87 | ... +88 88 | +89 89 | def bad4(self, arg: Union[float | complex, int]) -> None: + +PYI041_1.py:89:25: PYI041 [*] Use `complex` instead of `int | float | complex` + | +87 | ... +88 | +89 | def bad4(self, arg: Union[float | complex, int]) -> None: + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI041 +90 | ... + | + = help: Remove redundant type + +ℹ Safe fix +86 86 | def bad3(self, arg: Union[Union[float, complex], int]) -> None: +87 87 | ... +88 88 | +89 |- def bad4(self, arg: Union[float | complex, int]) -> None: + 89 |+ def bad4(self, arg: complex) -> None: +90 90 | ... +91 91 | +92 92 | def bad5(self, arg: int | (float | complex)) -> None: + +PYI041_1.py:92:25: PYI041 [*] Use `complex` instead of `int | float | complex` + | +90 | ... +91 | +92 | def bad5(self, arg: int | (float | complex)) -> None: + | ^^^^^^^^^^^^^^^^^^^^^^^ PYI041 +93 | ... + | + = help: Remove redundant type + +ℹ Safe fix +89 89 | def bad4(self, arg: Union[float | complex, int]) -> None: +90 90 | ... +91 91 | +92 |- def bad5(self, arg: int | (float | complex)) -> None: + 92 |+ def bad5(self, arg: complex) -> None: +93 93 | ... +94 94 | +95 95 | + +PYI041_1.py:99:23: PYI041 Use `float` instead of `int | float` + | + 97 | # fix must not yield runtime `None | None | ...` (TypeError) + 98 | class Issue18298: + 99 | def f1(self, arg: None | int | None | float = None) -> None: # PYI041 - no fix + | ^^^^^^^^^^^^^^^^^^^^^^^^^ PYI041 +100 | pass + | + = help: Remove redundant type + +PYI041_1.py:104:27: PYI041 [*] Use `float` instead of `int | float` + | +102 | if TYPE_CHECKING: +103 | +104 | def f2(self, arg: None | int | None | float = None) -> None: ... # PYI041 - with fix + | ^^^^^^^^^^^^^^^^^^^^^^^^^ PYI041 +105 | +106 | else: + | + = help: Remove redundant type + +ℹ Safe fix +101 101 | +102 102 | if TYPE_CHECKING: +103 103 | +104 |- def f2(self, arg: None | int | None | float = None) -> None: ... # PYI041 - with fix + 104 |+ def f2(self, arg: None | None | float = None) -> None: ... # PYI041 - with fix +105 105 | +106 106 | else: +107 107 | + +PYI041_1.py:111:23: PYI041 [*] Use `float` instead of `int | float` + | +109 | pass +110 | +111 | def f3(self, arg: None | float | None | int | None = None) -> None: # PYI041 - with fix + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI041 +112 | pass + | + = help: Remove redundant type + +ℹ Safe fix +108 108 | def f2(self, arg=None) -> None: +109 109 | pass +110 110 | +111 |- def f3(self, arg: None | float | None | int | None = None) -> None: # PYI041 - with fix + 111 |+ def f3(self, arg: None | float | None | None = None) -> None: # PYI041 - with fix +112 112 | pass diff --git a/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI041_PYI041.pyi.snap b/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI041_PYI041_1.pyi.snap similarity index 76% rename from crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI041_PYI041.pyi.snap rename to crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI041_PYI041_1.pyi.snap index dbf365cb5b..69f8f6fd36 100644 --- a/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI041_PYI041.pyi.snap +++ b/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI041_PYI041_1.pyi.snap @@ -1,8 +1,7 @@ --- source: crates/ruff_linter/src/rules/flake8_pyi/mod.rs -snapshot_kind: text --- -PYI041.pyi:21:14: PYI041 [*] Use `float` instead of `int | float` +PYI041_1.pyi:21:14: PYI041 [*] Use `float` instead of `int | float` | 21 | def f0(arg1: float | int) -> None: ... # PYI041 | ^^^^^^^^^^^ PYI041 @@ -19,7 +18,7 @@ PYI041.pyi:21:14: PYI041 [*] Use `float` instead of `int | float` 23 23 | 24 24 | def f1(arg1: float, *, arg2: float | list[str] | type[bool] | complex) -> None: ... # PYI041 -PYI041.pyi:24:30: PYI041 [*] Use `complex` instead of `float | complex` +PYI041_1.pyi:24:30: PYI041 [*] Use `complex` instead of `float | complex` | 24 | def f1(arg1: float, *, arg2: float | list[str] | type[bool] | complex) -> None: ... # PYI041 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI041 @@ -36,7 +35,7 @@ PYI041.pyi:24:30: PYI041 [*] Use `complex` instead of `float | complex` 26 26 | 27 27 | def f2(arg1: int, /, arg2: int | int | float) -> None: ... # PYI041 -PYI041.pyi:27:28: PYI041 [*] Use `float` instead of `int | float` +PYI041_1.pyi:27:28: PYI041 [*] Use `float` instead of `int | float` | 27 | def f2(arg1: int, /, arg2: int | int | float) -> None: ... # PYI041 | ^^^^^^^^^^^^^^^^^ PYI041 @@ -53,7 +52,7 @@ PYI041.pyi:27:28: PYI041 [*] Use `float` instead of `int | float` 29 29 | 30 30 | def f3(arg1: int, *args: Union[int | int | float]) -> None: ... # PYI041 -PYI041.pyi:30:26: PYI041 [*] Use `float` instead of `int | float` +PYI041_1.pyi:30:26: PYI041 [*] Use `float` instead of `int | float` | 30 | def f3(arg1: int, *args: Union[int | int | float]) -> None: ... # PYI041 | ^^^^^^^^^^^^^^^^^^^^^^^^ PYI041 @@ -70,7 +69,7 @@ PYI041.pyi:30:26: PYI041 [*] Use `float` instead of `int | float` 32 32 | 33 33 | async def f4(**kwargs: int | int | float) -> None: ... # PYI041 -PYI041.pyi:33:24: PYI041 [*] Use `float` instead of `int | float` +PYI041_1.pyi:33:24: PYI041 [*] Use `float` instead of `int | float` | 33 | async def f4(**kwargs: int | int | float) -> None: ... # PYI041 | ^^^^^^^^^^^^^^^^^ PYI041 @@ -89,7 +88,7 @@ PYI041.pyi:33:24: PYI041 [*] Use `float` instead of `int | float` 35 35 | def f5( 36 36 | arg: Union[ # comment -PYI041.pyi:36:10: PYI041 [*] Use `complex` instead of `int | float | complex` +PYI041_1.pyi:36:10: PYI041 [*] Use `complex` instead of `int | float | complex` | 35 | def f5( 36 | arg: Union[ # comment @@ -113,7 +112,7 @@ PYI041.pyi:36:10: PYI041 [*] Use `complex` instead of `int | float | complex` 40 38 | 41 39 | def f6( -PYI041.pyi:43:9: PYI041 [*] Use `complex` instead of `int | float | complex` +PYI041_1.pyi:43:9: PYI041 [*] Use `complex` instead of `int | float | complex` | 41 | def f6( 42 | arg: ( @@ -136,7 +135,7 @@ PYI041.pyi:43:9: PYI041 [*] Use `complex` instead of `int | float | complex` 46 44 | ) 47 45 | ) -> None: ... # PYI041 -PYI041.pyi:49:26: PYI041 [*] Use `float` instead of `int | float` +PYI041_1.pyi:49:26: PYI041 [*] Use `float` instead of `int | float` | 47 | ) -> None: ... # PYI041 48 | @@ -155,7 +154,7 @@ PYI041.pyi:49:26: PYI041 [*] Use `float` instead of `int | float` 51 51 | 52 52 | def f6(arg1: int, *args: Union[Union[int, int, float]]) -> None: ... # PYI041 -PYI041.pyi:52:26: PYI041 [*] Use `float` instead of `int | float` +PYI041_1.pyi:52:26: PYI041 [*] Use `float` instead of `int | float` | 52 | def f6(arg1: int, *args: Union[Union[int, int, float]]) -> None: ... # PYI041 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI041 @@ -172,7 +171,7 @@ PYI041.pyi:52:26: PYI041 [*] Use `float` instead of `int | float` 54 54 | 55 55 | def f7(arg1: int, *args: Union[Union[Union[int, int, float]]]) -> None: ... # PYI041 -PYI041.pyi:55:26: PYI041 [*] Use `float` instead of `int | float` +PYI041_1.pyi:55:26: PYI041 [*] Use `float` instead of `int | float` | 55 | def f7(arg1: int, *args: Union[Union[Union[int, int, float]]]) -> None: ... # PYI041 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI041 @@ -189,7 +188,7 @@ PYI041.pyi:55:26: PYI041 [*] Use `float` instead of `int | float` 57 57 | 58 58 | def f8(arg1: int, *args: Union[Union[Union[int | int | float]]]) -> None: ... # PYI041 -PYI041.pyi:58:26: PYI041 [*] Use `float` instead of `int | float` +PYI041_1.pyi:58:26: PYI041 [*] Use `float` instead of `int | float` | 58 | def f8(arg1: int, *args: Union[Union[Union[int | int | float]]]) -> None: ... # PYI041 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI041 @@ -206,7 +205,7 @@ PYI041.pyi:58:26: PYI041 [*] Use `float` instead of `int | float` 60 60 | 61 61 | class Foo: -PYI041.pyi:64:24: PYI041 [*] Use `complex` instead of `int | float | complex` +PYI041_1.pyi:64:24: PYI041 [*] Use `complex` instead of `int | float | complex` | 62 | def good(self, arg: int) -> None: ... 63 | @@ -227,7 +226,7 @@ PYI041.pyi:64:24: PYI041 [*] Use `complex` instead of `int | float | complex` 66 66 | def bad2(self, arg: int | Union[float, complex]) -> None: ... # PYI041 67 67 | -PYI041.pyi:66:25: PYI041 [*] Use `complex` instead of `int | float | complex` +PYI041_1.pyi:66:25: PYI041 [*] Use `complex` instead of `int | float | complex` | 64 | def bad(self, arg: int | float | complex) -> None: ... # PYI041 65 | @@ -248,7 +247,7 @@ PYI041.pyi:66:25: PYI041 [*] Use `complex` instead of `int | float | complex` 68 68 | def bad3(self, arg: Union[Union[float, complex], int]) -> None: ... # PYI041 69 69 | -PYI041.pyi:68:25: PYI041 [*] Use `complex` instead of `int | float | complex` +PYI041_1.pyi:68:25: PYI041 [*] Use `complex` instead of `int | float | complex` | 66 | def bad2(self, arg: int | Union[float, complex]) -> None: ... # PYI041 67 | @@ -269,7 +268,7 @@ PYI041.pyi:68:25: PYI041 [*] Use `complex` instead of `int | float | complex` 70 70 | def bad4(self, arg: Union[float | complex, int]) -> None: ... # PYI041 71 71 | -PYI041.pyi:70:25: PYI041 [*] Use `complex` instead of `int | float | complex` +PYI041_1.pyi:70:25: PYI041 [*] Use `complex` instead of `int | float | complex` | 68 | def bad3(self, arg: Union[Union[float, complex], int]) -> None: ... # PYI041 69 | @@ -288,8 +287,9 @@ PYI041.pyi:70:25: PYI041 [*] Use `complex` instead of `int | float | complex` 70 |+ def bad4(self, arg: complex) -> None: ... # PYI041 71 71 | 72 72 | def bad5(self, arg: int | (float | complex)) -> None: ... # PYI041 +73 73 | -PYI041.pyi:72:25: PYI041 [*] Use `complex` instead of `int | float | complex` +PYI041_1.pyi:72:25: PYI041 [*] Use `complex` instead of `int | float | complex` | 70 | def bad4(self, arg: Union[float | complex, int]) -> None: ... # PYI041 71 | @@ -304,3 +304,42 @@ PYI041.pyi:72:25: PYI041 [*] Use `complex` instead of `int | float | complex` 71 71 | 72 |- def bad5(self, arg: int | (float | complex)) -> None: ... # PYI041 72 |+ def bad5(self, arg: complex) -> None: ... # PYI041 +73 73 | +74 74 | +75 75 | # https://github.com/astral-sh/ruff/issues/18298 + +PYI041_1.pyi:78:23: PYI041 [*] Use `float` instead of `int | float` + | +76 | # fix must not yield runtime `None | None | ...` (TypeError) +77 | class Issue18298: +78 | def f1(self, arg: None | int | None | float = None) -> None: ... # PYI041 - with fix + | ^^^^^^^^^^^^^^^^^^^^^^^^^ PYI041 +79 | +80 | def f3(self, arg: None | float | None | int | None = None) -> None: ... # PYI041 - with fix + | + = help: Remove redundant type + +ℹ Safe fix +75 75 | # https://github.com/astral-sh/ruff/issues/18298 +76 76 | # fix must not yield runtime `None | None | ...` (TypeError) +77 77 | class Issue18298: +78 |- def f1(self, arg: None | int | None | float = None) -> None: ... # PYI041 - with fix + 78 |+ def f1(self, arg: None | None | float = None) -> None: ... # PYI041 - with fix +79 79 | +80 80 | def f3(self, arg: None | float | None | int | None = None) -> None: ... # PYI041 - with fix + +PYI041_1.pyi:80:23: PYI041 [*] Use `float` instead of `int | float` + | +78 | def f1(self, arg: None | int | None | float = None) -> None: ... # PYI041 - with fix +79 | +80 | def f3(self, arg: None | float | None | int | None = None) -> None: ... # PYI041 - with fix + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI041 + | + = help: Remove redundant type + +ℹ Safe fix +77 77 | class Issue18298: +78 78 | def f1(self, arg: None | int | None | float = None) -> None: ... # PYI041 - with fix +79 79 | +80 |- def f3(self, arg: None | float | None | int | None = None) -> None: ... # PYI041 - with fix + 80 |+ def f3(self, arg: None | float | None | None = None) -> None: ... # PYI041 - with fix diff --git a/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI041_PYI041_2.py.snap b/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI041_PYI041_2.py.snap new file mode 100644 index 0000000000..10c39077a2 --- /dev/null +++ b/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI041_PYI041_2.py.snap @@ -0,0 +1,12 @@ +--- +source: crates/ruff_linter/src/rules/flake8_pyi/mod.rs +--- +PYI041_2.py:7:23: PYI041 Use `float` instead of `int | float` + | +5 | # fix must not yield runtime `None | None | ...` (TypeError) +6 | class Issue18298: +7 | def f1(self, arg: None | int | None | float = None) -> None: + | ^^^^^^^^^^^^^^^^^^^^^^^^^ PYI041 +8 | pass + | + = help: Remove redundant type