From 4094b04346c09bd0e7d4a83daa5aa1c32d068a71 Mon Sep 17 00:00:00 2001 From: LaBatata101 Date: Sun, 27 Apr 2025 18:38:01 -0300 Subject: [PATCH 1/9] [`flake8-pyi`] Improve autofix safety for `PYI061` Handles the case where the `None` literal inside `Literal` is part of a tuple, e.g. `List[None,]`. We don't create the fix for this case. --- .../test/fixtures/flake8_pyi/PYI061.py | 3 +++ .../test/fixtures/flake8_pyi/PYI061.pyi | 4 ++++ .../rules/redundant_none_literal.rs | 14 ++++++++++++- ...__flake8_pyi__tests__PYI061_PYI061.py.snap | 20 +++++++++++++++++++ ..._flake8_pyi__tests__PYI061_PYI061.pyi.snap | 16 +++++++++++++++ ...ke8_pyi__tests__py38_PYI061_PYI061.py.snap | 20 +++++++++++++++++++ ...e8_pyi__tests__py38_PYI061_PYI061.pyi.snap | 16 +++++++++++++++ 7 files changed, 92 insertions(+), 1 deletion(-) diff --git a/crates/ruff_linter/resources/test/fixtures/flake8_pyi/PYI061.py b/crates/ruff_linter/resources/test/fixtures/flake8_pyi/PYI061.py index 51baabe892107..e7eb29374a2e6 100644 --- a/crates/ruff_linter/resources/test/fixtures/flake8_pyi/PYI061.py +++ b/crates/ruff_linter/resources/test/fixtures/flake8_pyi/PYI061.py @@ -79,3 +79,6 @@ def good_func(arg1: Literal[int] | None): d: None | (Literal[None] | None) e: None | ((None | Literal[None]) | None) | None f: Literal[None] | Literal[None] + +# https://github.com/astral-sh/ruff/issues/16177 +g: Literal[None,] | Literal[None,] diff --git a/crates/ruff_linter/resources/test/fixtures/flake8_pyi/PYI061.pyi b/crates/ruff_linter/resources/test/fixtures/flake8_pyi/PYI061.pyi index ed879dd646e34..b2b23e95ebae3 100644 --- a/crates/ruff_linter/resources/test/fixtures/flake8_pyi/PYI061.pyi +++ b/crates/ruff_linter/resources/test/fixtures/flake8_pyi/PYI061.pyi @@ -54,3 +54,7 @@ c: (None | Literal[None]) | None d: None | (Literal[None] | None) e: None | ((None | Literal[None]) | None) | None f: Literal[None] | Literal[None] + + +# https://github.com/astral-sh/ruff/issues/16177 +g: Literal[None,] | Literal[None,] diff --git a/crates/ruff_linter/src/rules/flake8_pyi/rules/redundant_none_literal.rs b/crates/ruff_linter/src/rules/flake8_pyi/rules/redundant_none_literal.rs index 16a48bc5f5717..f0a576fc72413 100644 --- a/crates/ruff_linter/src/rules/flake8_pyi/rules/redundant_none_literal.rs +++ b/crates/ruff_linter/src/rules/flake8_pyi/rules/redundant_none_literal.rs @@ -178,7 +178,7 @@ fn create_fix( if expr != literal_expr { if let Expr::Subscript(ExprSubscript { value, slice, .. }) = expr { if semantic.match_typing_expr(value, "Literal") - && matches!(**slice, Expr::NoneLiteral(_)) + && is_slice_none_literal(slice) { is_fixable = false; } @@ -258,3 +258,15 @@ enum UnionKind { TypingOptional, BitOr, } + +fn is_slice_none_literal(slice: &Expr) -> bool { + match slice { + Expr::NoneLiteral(_) => true, + // If the slice contains a tuple, e.g. `Literal[None,]` we are only interested in the first + // element. + Expr::Tuple(ast::ExprTuple { elts, .. }) if elts.len() == 1 => elts + .first() + .is_some_and(|expr| matches!(expr, Expr::NoneLiteral(_))), + _ => false, + } +} diff --git a/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI061_PYI061.py.snap b/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI061_PYI061.py.snap index 33bf166e8b796..91fdf88c01fdf 100644 --- a/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI061_PYI061.py.snap +++ b/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI061_PYI061.py.snap @@ -442,6 +442,8 @@ PYI061.py:81:12: PYI061 Use `None` rather than `Literal[None]` 80 | e: None | ((None | Literal[None]) | None) | None 81 | f: Literal[None] | Literal[None] | ^^^^ PYI061 +82 | +83 | # https://github.com/astral-sh/ruff/issues/16177 | = help: Replace with `None` @@ -451,5 +453,23 @@ PYI061.py:81:28: PYI061 Use `None` rather than `Literal[None]` 80 | e: None | ((None | Literal[None]) | None) | None 81 | f: Literal[None] | Literal[None] | ^^^^ PYI061 +82 | +83 | # https://github.com/astral-sh/ruff/issues/16177 + | + = help: Replace with `None` + +PYI061.py:84:12: PYI061 Use `None` rather than `Literal[None]` + | +83 | # https://github.com/astral-sh/ruff/issues/16177 +84 | g: Literal[None,] | Literal[None,] + | ^^^^ PYI061 + | + = help: Replace with `None` + +PYI061.py:84:29: PYI061 Use `None` rather than `Literal[None]` + | +83 | # https://github.com/astral-sh/ruff/issues/16177 +84 | g: Literal[None,] | Literal[None,] + | ^^^^ PYI061 | = help: Replace with `None` diff --git a/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI061_PYI061.pyi.snap b/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI061_PYI061.pyi.snap index a2c845c0649b8..0d1e3a0727cfa 100644 --- a/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI061_PYI061.pyi.snap +++ b/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI061_PYI061.pyi.snap @@ -322,3 +322,19 @@ PYI061.pyi:56:28: PYI061 Use `None` rather than `Literal[None]` | ^^^^ PYI061 | = help: Replace with `None` + +PYI061.pyi:60:12: PYI061 Use `None` rather than `Literal[None]` + | +59 | # https://github.com/astral-sh/ruff/issues/16177 +60 | g: Literal[None,] | Literal[None,] + | ^^^^ PYI061 + | + = help: Replace with `None` + +PYI061.pyi:60:29: PYI061 Use `None` rather than `Literal[None]` + | +59 | # https://github.com/astral-sh/ruff/issues/16177 +60 | g: Literal[None,] | Literal[None,] + | ^^^^ PYI061 + | + = help: Replace with `None` diff --git a/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__py38_PYI061_PYI061.py.snap b/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__py38_PYI061_PYI061.py.snap index 99f4969598fa9..84a0fa597d805 100644 --- a/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__py38_PYI061_PYI061.py.snap +++ b/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__py38_PYI061_PYI061.py.snap @@ -484,6 +484,8 @@ PYI061.py:81:12: PYI061 Use `None` rather than `Literal[None]` 80 | e: None | ((None | Literal[None]) | None) | None 81 | f: Literal[None] | Literal[None] | ^^^^ PYI061 +82 | +83 | # https://github.com/astral-sh/ruff/issues/16177 | = help: Replace with `None` @@ -493,5 +495,23 @@ PYI061.py:81:28: PYI061 Use `None` rather than `Literal[None]` 80 | e: None | ((None | Literal[None]) | None) | None 81 | f: Literal[None] | Literal[None] | ^^^^ PYI061 +82 | +83 | # https://github.com/astral-sh/ruff/issues/16177 + | + = help: Replace with `None` + +PYI061.py:84:12: PYI061 Use `None` rather than `Literal[None]` + | +83 | # https://github.com/astral-sh/ruff/issues/16177 +84 | g: Literal[None,] | Literal[None,] + | ^^^^ PYI061 + | + = help: Replace with `None` + +PYI061.py:84:29: PYI061 Use `None` rather than `Literal[None]` + | +83 | # https://github.com/astral-sh/ruff/issues/16177 +84 | g: Literal[None,] | Literal[None,] + | ^^^^ PYI061 | = help: Replace with `None` diff --git a/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__py38_PYI061_PYI061.pyi.snap b/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__py38_PYI061_PYI061.pyi.snap index a2c845c0649b8..0d1e3a0727cfa 100644 --- a/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__py38_PYI061_PYI061.pyi.snap +++ b/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__py38_PYI061_PYI061.pyi.snap @@ -322,3 +322,19 @@ PYI061.pyi:56:28: PYI061 Use `None` rather than `Literal[None]` | ^^^^ PYI061 | = help: Replace with `None` + +PYI061.pyi:60:12: PYI061 Use `None` rather than `Literal[None]` + | +59 | # https://github.com/astral-sh/ruff/issues/16177 +60 | g: Literal[None,] | Literal[None,] + | ^^^^ PYI061 + | + = help: Replace with `None` + +PYI061.pyi:60:29: PYI061 Use `None` rather than `Literal[None]` + | +59 | # https://github.com/astral-sh/ruff/issues/16177 +60 | g: Literal[None,] | Literal[None,] + | ^^^^ PYI061 + | + = help: Replace with `None` From 3e69b38ef54a6e97e01e888a80ea9db812c5f2fb Mon Sep 17 00:00:00 2001 From: Victor Hugo Gomes Date: Sun, 27 Apr 2025 18:49:16 -0300 Subject: [PATCH 2/9] Simplify code Co-authored-by: Alex Waygood --- .../rules/flake8_pyi/rules/redundant_none_literal.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/crates/ruff_linter/src/rules/flake8_pyi/rules/redundant_none_literal.rs b/crates/ruff_linter/src/rules/flake8_pyi/rules/redundant_none_literal.rs index f0a576fc72413..4d6c36404a15b 100644 --- a/crates/ruff_linter/src/rules/flake8_pyi/rules/redundant_none_literal.rs +++ b/crates/ruff_linter/src/rules/flake8_pyi/rules/redundant_none_literal.rs @@ -262,11 +262,11 @@ enum UnionKind { fn is_slice_none_literal(slice: &Expr) -> bool { match slice { Expr::NoneLiteral(_) => true, - // If the slice contains a tuple, e.g. `Literal[None,]` we are only interested in the first - // element. - Expr::Tuple(ast::ExprTuple { elts, .. }) if elts.len() == 1 => elts - .first() - .is_some_and(|expr| matches!(expr, Expr::NoneLiteral(_))), + // If the slice contains a single-element tuple, e.g. `Literal[None,]`, + // check the first element. + Expr::Tuple(ast::ExprTuple { elts, .. }) => { + matches!(&**elts, [Expr::NoneLiteral(_)]) + } _ => false, } } From 7784e7617d9d77aea37b11c6b6a35f005b07c205 Mon Sep 17 00:00:00 2001 From: LaBatata101 Date: Sun, 27 Apr 2025 19:18:59 -0300 Subject: [PATCH 3/9] Update solution --- .../flake8_pyi/rules/redundant_none_literal.rs | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/crates/ruff_linter/src/rules/flake8_pyi/rules/redundant_none_literal.rs b/crates/ruff_linter/src/rules/flake8_pyi/rules/redundant_none_literal.rs index 4d6c36404a15b..e3fafac628991 100644 --- a/crates/ruff_linter/src/rules/flake8_pyi/rules/redundant_none_literal.rs +++ b/crates/ruff_linter/src/rules/flake8_pyi/rules/redundant_none_literal.rs @@ -130,6 +130,9 @@ pub(crate) fn redundant_none_literal<'a>(checker: &Checker, literal_expr: &'a Ex literal_elements.clone(), union_kind, ) + .map(|fix| { + fix.map(|fix| fix.isolate(Checker::isolation(semantic.current_statement_id()))) + }) }); checker.report_diagnostic(diagnostic); } @@ -178,7 +181,7 @@ fn create_fix( if expr != literal_expr { if let Expr::Subscript(ExprSubscript { value, slice, .. }) = expr { if semantic.match_typing_expr(value, "Literal") - && is_slice_none_literal(slice) + && matches!(**slice, Expr::NoneLiteral(_)) { is_fixable = false; } @@ -258,15 +261,3 @@ enum UnionKind { TypingOptional, BitOr, } - -fn is_slice_none_literal(slice: &Expr) -> bool { - match slice { - Expr::NoneLiteral(_) => true, - // If the slice contains a single-element tuple, e.g. `Literal[None,]`, - // check the first element. - Expr::Tuple(ast::ExprTuple { elts, .. }) => { - matches!(&**elts, [Expr::NoneLiteral(_)]) - } - _ => false, - } -} From b8d0f56be8be793c9f54af02cd04329d12bfc6df Mon Sep 17 00:00:00 2001 From: LaBatata101 Date: Sun, 27 Apr 2025 19:26:33 -0300 Subject: [PATCH 4/9] Update snapshots --- ...s__flake8_pyi__tests__PYI061_PYI061.py.snap | 18 ++++++++++++++++-- ...__flake8_pyi__tests__PYI061_PYI061.pyi.snap | 18 ++++++++++++++++-- ...ake8_pyi__tests__py38_PYI061_PYI061.py.snap | 18 ++++++++++++++++-- ...ke8_pyi__tests__py38_PYI061_PYI061.pyi.snap | 18 ++++++++++++++++-- 4 files changed, 64 insertions(+), 8 deletions(-) diff --git a/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI061_PYI061.py.snap b/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI061_PYI061.py.snap index 91fdf88c01fdf..76088556268d2 100644 --- a/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI061_PYI061.py.snap +++ b/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI061_PYI061.py.snap @@ -458,7 +458,7 @@ PYI061.py:81:28: PYI061 Use `None` rather than `Literal[None]` | = help: Replace with `None` -PYI061.py:84:12: PYI061 Use `None` rather than `Literal[None]` +PYI061.py:84:12: PYI061 [*] Use `None` rather than `Literal[None]` | 83 | # https://github.com/astral-sh/ruff/issues/16177 84 | g: Literal[None,] | Literal[None,] @@ -466,10 +466,24 @@ PYI061.py:84:12: PYI061 Use `None` rather than `Literal[None]` | = help: Replace with `None` -PYI061.py:84:29: PYI061 Use `None` rather than `Literal[None]` +ℹ Safe fix +81 81 | f: Literal[None] | Literal[None] +82 82 | +83 83 | # https://github.com/astral-sh/ruff/issues/16177 +84 |-g: Literal[None,] | Literal[None,] + 84 |+g: None | Literal[None,] + +PYI061.py:84:29: PYI061 [*] Use `None` rather than `Literal[None]` | 83 | # https://github.com/astral-sh/ruff/issues/16177 84 | g: Literal[None,] | Literal[None,] | ^^^^ PYI061 | = help: Replace with `None` + +ℹ Safe fix +81 81 | f: Literal[None] | Literal[None] +82 82 | +83 83 | # https://github.com/astral-sh/ruff/issues/16177 +84 |-g: Literal[None,] | Literal[None,] + 84 |+g: Literal[None,] | None diff --git a/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI061_PYI061.pyi.snap b/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI061_PYI061.pyi.snap index 0d1e3a0727cfa..ef7bb3c130977 100644 --- a/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI061_PYI061.pyi.snap +++ b/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI061_PYI061.pyi.snap @@ -323,7 +323,7 @@ PYI061.pyi:56:28: PYI061 Use `None` rather than `Literal[None]` | = help: Replace with `None` -PYI061.pyi:60:12: PYI061 Use `None` rather than `Literal[None]` +PYI061.pyi:60:12: PYI061 [*] Use `None` rather than `Literal[None]` | 59 | # https://github.com/astral-sh/ruff/issues/16177 60 | g: Literal[None,] | Literal[None,] @@ -331,10 +331,24 @@ PYI061.pyi:60:12: PYI061 Use `None` rather than `Literal[None]` | = help: Replace with `None` -PYI061.pyi:60:29: PYI061 Use `None` rather than `Literal[None]` +ℹ Safe fix +57 57 | +58 58 | +59 59 | # https://github.com/astral-sh/ruff/issues/16177 +60 |-g: Literal[None,] | Literal[None,] + 60 |+g: None | Literal[None,] + +PYI061.pyi:60:29: PYI061 [*] Use `None` rather than `Literal[None]` | 59 | # https://github.com/astral-sh/ruff/issues/16177 60 | g: Literal[None,] | Literal[None,] | ^^^^ PYI061 | = help: Replace with `None` + +ℹ Safe fix +57 57 | +58 58 | +59 59 | # https://github.com/astral-sh/ruff/issues/16177 +60 |-g: Literal[None,] | Literal[None,] + 60 |+g: Literal[None,] | None diff --git a/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__py38_PYI061_PYI061.py.snap b/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__py38_PYI061_PYI061.py.snap index 84a0fa597d805..23269f477543b 100644 --- a/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__py38_PYI061_PYI061.py.snap +++ b/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__py38_PYI061_PYI061.py.snap @@ -500,7 +500,7 @@ PYI061.py:81:28: PYI061 Use `None` rather than `Literal[None]` | = help: Replace with `None` -PYI061.py:84:12: PYI061 Use `None` rather than `Literal[None]` +PYI061.py:84:12: PYI061 [*] Use `None` rather than `Literal[None]` | 83 | # https://github.com/astral-sh/ruff/issues/16177 84 | g: Literal[None,] | Literal[None,] @@ -508,10 +508,24 @@ PYI061.py:84:12: PYI061 Use `None` rather than `Literal[None]` | = help: Replace with `None` -PYI061.py:84:29: PYI061 Use `None` rather than `Literal[None]` +ℹ Safe fix +81 81 | f: Literal[None] | Literal[None] +82 82 | +83 83 | # https://github.com/astral-sh/ruff/issues/16177 +84 |-g: Literal[None,] | Literal[None,] + 84 |+g: None | Literal[None,] + +PYI061.py:84:29: PYI061 [*] Use `None` rather than `Literal[None]` | 83 | # https://github.com/astral-sh/ruff/issues/16177 84 | g: Literal[None,] | Literal[None,] | ^^^^ PYI061 | = help: Replace with `None` + +ℹ Safe fix +81 81 | f: Literal[None] | Literal[None] +82 82 | +83 83 | # https://github.com/astral-sh/ruff/issues/16177 +84 |-g: Literal[None,] | Literal[None,] + 84 |+g: Literal[None,] | None diff --git a/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__py38_PYI061_PYI061.pyi.snap b/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__py38_PYI061_PYI061.pyi.snap index 0d1e3a0727cfa..ef7bb3c130977 100644 --- a/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__py38_PYI061_PYI061.pyi.snap +++ b/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__py38_PYI061_PYI061.pyi.snap @@ -323,7 +323,7 @@ PYI061.pyi:56:28: PYI061 Use `None` rather than `Literal[None]` | = help: Replace with `None` -PYI061.pyi:60:12: PYI061 Use `None` rather than `Literal[None]` +PYI061.pyi:60:12: PYI061 [*] Use `None` rather than `Literal[None]` | 59 | # https://github.com/astral-sh/ruff/issues/16177 60 | g: Literal[None,] | Literal[None,] @@ -331,10 +331,24 @@ PYI061.pyi:60:12: PYI061 Use `None` rather than `Literal[None]` | = help: Replace with `None` -PYI061.pyi:60:29: PYI061 Use `None` rather than `Literal[None]` +ℹ Safe fix +57 57 | +58 58 | +59 59 | # https://github.com/astral-sh/ruff/issues/16177 +60 |-g: Literal[None,] | Literal[None,] + 60 |+g: None | Literal[None,] + +PYI061.pyi:60:29: PYI061 [*] Use `None` rather than `Literal[None]` | 59 | # https://github.com/astral-sh/ruff/issues/16177 60 | g: Literal[None,] | Literal[None,] | ^^^^ PYI061 | = help: Replace with `None` + +ℹ Safe fix +57 57 | +58 58 | +59 59 | # https://github.com/astral-sh/ruff/issues/16177 +60 |-g: Literal[None,] | Literal[None,] + 60 |+g: Literal[None,] | None From 8543acfca5f6fb485780bf644b14dcd3c0a7dbcd Mon Sep 17 00:00:00 2001 From: LaBatata101 Date: Sun, 27 Apr 2025 19:44:19 -0300 Subject: [PATCH 5/9] Add comment --- .../src/rules/flake8_pyi/rules/redundant_none_literal.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/crates/ruff_linter/src/rules/flake8_pyi/rules/redundant_none_literal.rs b/crates/ruff_linter/src/rules/flake8_pyi/rules/redundant_none_literal.rs index e3fafac628991..9f03196df53aa 100644 --- a/crates/ruff_linter/src/rules/flake8_pyi/rules/redundant_none_literal.rs +++ b/crates/ruff_linter/src/rules/flake8_pyi/rules/redundant_none_literal.rs @@ -130,6 +130,9 @@ pub(crate) fn redundant_none_literal<'a>(checker: &Checker, literal_expr: &'a Ex literal_elements.clone(), union_kind, ) + // Isolate the fix to ensure multiple fixes on the same expression (like + // `Literal[None,] | Literal[None,]` -> `None | None`) happen across separate passes, + // preventing the production of invalid code. .map(|fix| { fix.map(|fix| fix.isolate(Checker::isolation(semantic.current_statement_id()))) }) From 782936cec76894d0481dd5304fde9c4ec029e9e5 Mon Sep 17 00:00:00 2001 From: LaBatata101 Date: Sun, 27 Apr 2025 20:06:12 -0300 Subject: [PATCH 6/9] Add integration test --- crates/ruff/tests/lint.rs | 23 +- ...nt__flake8_pyi_redundant_none_literal.snap | 31 ++ .../test/fixtures/flake8_pyi/PYI061.py | 3 - .../test/fixtures/flake8_pyi/PYI061.pyi | 5 - ...__flake8_pyi__tests__PYI061_PYI061.py.snap | 34 -- ..._flake8_pyi__tests__PYI061_PYI061.pyi.snap | 375 ++++++++---------- ...ke8_pyi__tests__py38_PYI061_PYI061.py.snap | 34 -- ...e8_pyi__tests__py38_PYI061_PYI061.pyi.snap | 375 ++++++++---------- 8 files changed, 399 insertions(+), 481 deletions(-) create mode 100644 crates/ruff/tests/snapshots/lint__flake8_pyi_redundant_none_literal.snap diff --git a/crates/ruff/tests/lint.rs b/crates/ruff/tests/lint.rs index d9b18fcfb78a2..77759ddf6451c 100644 --- a/crates/ruff/tests/lint.rs +++ b/crates/ruff/tests/lint.rs @@ -3475,7 +3475,7 @@ requires-python = ">= 3.11" &inner_pyproject, r#" [tool.ruff] -target-version = "py310" +target-version = "py310" "#, )?; @@ -4980,6 +4980,27 @@ fn flake8_import_convention_unused_aliased_import_no_conflict() { .pass_stdin("1")); } +// See: https://github.com/astral-sh/ruff/issues/16177 +#[test] +fn flake8_pyi_redundant_none_literal() { + assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME)) + .args(STDIN_BASE_OPTIONS) + .args(["--select", "PYI061"]) + .args(["--stdin-filename", "test.py"]) + .arg("--preview") + .arg("--fix") + .arg("-") + .pass_stdin( + r#" +from typing import Literal + +# Ruff offers a fix for one of these, but not both of them, as if both were autofixed +# it would result in a `TypeError` at runtime. +x: Literal[None,] | Literal[None,] +"# + )); +} + /// Test that private, old-style `TypeVar` generics /// 1. Get replaced with PEP 695 type parameters (UP046, UP047) /// 2. Get renamed to remove leading underscores (UP049) diff --git a/crates/ruff/tests/snapshots/lint__flake8_pyi_redundant_none_literal.snap b/crates/ruff/tests/snapshots/lint__flake8_pyi_redundant_none_literal.snap new file mode 100644 index 0000000000000..fb2e5dc771603 --- /dev/null +++ b/crates/ruff/tests/snapshots/lint__flake8_pyi_redundant_none_literal.snap @@ -0,0 +1,31 @@ +--- +source: crates/ruff/tests/lint.rs +info: + program: ruff + args: + - check + - "--no-cache" + - "--output-format" + - concise + - "--select" + - PYI061 + - "--stdin-filename" + - test.py + - "--preview" + - "--fix" + - "-" + stdin: "\nfrom typing import Literal\n\n# Ruff offers a fix for one of these, but not both of them, as if both were autofixed\n# it would result in a `TypeError` at runtime.\nx: Literal[None,] | Literal[None,]\n" +--- +success: false +exit_code: 1 +----- stdout ----- + +from typing import Literal + +# Ruff offers a fix for one of these, but not both of them, as if both were autofixed +# it would result in a `TypeError` at runtime. +x: None | Literal[None,] + +----- stderr ----- +test.py:6:19: PYI061 Use `None` rather than `Literal[None]` +Found 2 errors (1 fixed, 1 remaining). diff --git a/crates/ruff_linter/resources/test/fixtures/flake8_pyi/PYI061.py b/crates/ruff_linter/resources/test/fixtures/flake8_pyi/PYI061.py index e7eb29374a2e6..51baabe892107 100644 --- a/crates/ruff_linter/resources/test/fixtures/flake8_pyi/PYI061.py +++ b/crates/ruff_linter/resources/test/fixtures/flake8_pyi/PYI061.py @@ -79,6 +79,3 @@ def good_func(arg1: Literal[int] | None): d: None | (Literal[None] | None) e: None | ((None | Literal[None]) | None) | None f: Literal[None] | Literal[None] - -# https://github.com/astral-sh/ruff/issues/16177 -g: Literal[None,] | Literal[None,] diff --git a/crates/ruff_linter/resources/test/fixtures/flake8_pyi/PYI061.pyi b/crates/ruff_linter/resources/test/fixtures/flake8_pyi/PYI061.pyi index b2b23e95ebae3..2fb9b211490a7 100644 --- a/crates/ruff_linter/resources/test/fixtures/flake8_pyi/PYI061.pyi +++ b/crates/ruff_linter/resources/test/fixtures/flake8_pyi/PYI061.pyi @@ -1,6 +1,5 @@ from typing import Literal, Union - def func1(arg1: Literal[None]): ... @@ -54,7 +53,3 @@ c: (None | Literal[None]) | None d: None | (Literal[None] | None) e: None | ((None | Literal[None]) | None) | None f: Literal[None] | Literal[None] - - -# https://github.com/astral-sh/ruff/issues/16177 -g: Literal[None,] | Literal[None,] diff --git a/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI061_PYI061.py.snap b/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI061_PYI061.py.snap index 76088556268d2..33bf166e8b796 100644 --- a/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI061_PYI061.py.snap +++ b/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI061_PYI061.py.snap @@ -442,8 +442,6 @@ PYI061.py:81:12: PYI061 Use `None` rather than `Literal[None]` 80 | e: None | ((None | Literal[None]) | None) | None 81 | f: Literal[None] | Literal[None] | ^^^^ PYI061 -82 | -83 | # https://github.com/astral-sh/ruff/issues/16177 | = help: Replace with `None` @@ -453,37 +451,5 @@ PYI061.py:81:28: PYI061 Use `None` rather than `Literal[None]` 80 | e: None | ((None | Literal[None]) | None) | None 81 | f: Literal[None] | Literal[None] | ^^^^ PYI061 -82 | -83 | # https://github.com/astral-sh/ruff/issues/16177 | = help: Replace with `None` - -PYI061.py:84:12: PYI061 [*] Use `None` rather than `Literal[None]` - | -83 | # https://github.com/astral-sh/ruff/issues/16177 -84 | g: Literal[None,] | Literal[None,] - | ^^^^ PYI061 - | - = help: Replace with `None` - -ℹ Safe fix -81 81 | f: Literal[None] | Literal[None] -82 82 | -83 83 | # https://github.com/astral-sh/ruff/issues/16177 -84 |-g: Literal[None,] | Literal[None,] - 84 |+g: None | Literal[None,] - -PYI061.py:84:29: PYI061 [*] Use `None` rather than `Literal[None]` - | -83 | # https://github.com/astral-sh/ruff/issues/16177 -84 | g: Literal[None,] | Literal[None,] - | ^^^^ PYI061 - | - = help: Replace with `None` - -ℹ Safe fix -81 81 | f: Literal[None] | Literal[None] -82 82 | -83 83 | # https://github.com/astral-sh/ruff/issues/16177 -84 |-g: Literal[None,] | Literal[None,] - 84 |+g: Literal[None,] | None diff --git a/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI061_PYI061.pyi.snap b/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI061_PYI061.pyi.snap index ef7bb3c130977..6424e502ec178 100644 --- a/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI061_PYI061.pyi.snap +++ b/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI061_PYI061.pyi.snap @@ -1,9 +1,11 @@ --- source: crates/ruff_linter/src/rules/flake8_pyi/mod.rs --- -PYI061.pyi:4:25: PYI061 [*] Use `None` rather than `Literal[None]` +PYI061.pyi:3:25: PYI061 [*] Use `None` rather than `Literal[None]` | -4 | def func1(arg1: Literal[None]): ... +1 | from typing import Literal, Union +2 | +3 | def func1(arg1: Literal[None]): ... | ^^^^ PYI061 | = help: Replace with `None` @@ -11,344 +13,313 @@ PYI061.pyi:4:25: PYI061 [*] Use `None` rather than `Literal[None]` ℹ Safe fix 1 1 | from typing import Literal, Union 2 2 | -3 3 | -4 |-def func1(arg1: Literal[None]): ... - 4 |+def func1(arg1: None): ... +3 |-def func1(arg1: Literal[None]): ... + 3 |+def func1(arg1: None): ... +4 4 | 5 5 | -6 6 | -7 7 | def func2(arg1: Literal[None] | int): ... +6 6 | def func2(arg1: Literal[None] | int): ... -PYI061.pyi:7:25: PYI061 [*] Use `None` rather than `Literal[None]` +PYI061.pyi:6:25: PYI061 [*] Use `None` rather than `Literal[None]` | -7 | def func2(arg1: Literal[None] | int): ... +6 | def func2(arg1: Literal[None] | int): ... | ^^^^ PYI061 | = help: Replace with `None` ℹ Safe fix -4 4 | def func1(arg1: Literal[None]): ... +3 3 | def func1(arg1: Literal[None]): ... +4 4 | 5 5 | -6 6 | -7 |-def func2(arg1: Literal[None] | int): ... - 7 |+def func2(arg1: None | int): ... +6 |-def func2(arg1: Literal[None] | int): ... + 6 |+def func2(arg1: None | int): ... +7 7 | 8 8 | -9 9 | -10 10 | def func3() -> Literal[None]: ... +9 9 | def func3() -> Literal[None]: ... -PYI061.pyi:10:24: PYI061 [*] Use `None` rather than `Literal[None]` - | -10 | def func3() -> Literal[None]: ... - | ^^^^ PYI061 - | - = help: Replace with `None` +PYI061.pyi:9:24: PYI061 [*] Use `None` rather than `Literal[None]` + | +9 | def func3() -> Literal[None]: ... + | ^^^^ PYI061 + | + = help: Replace with `None` ℹ Safe fix -7 7 | def func2(arg1: Literal[None] | int): ... +6 6 | def func2(arg1: Literal[None] | int): ... +7 7 | 8 8 | -9 9 | -10 |-def func3() -> Literal[None]: ... - 10 |+def func3() -> None: ... +9 |-def func3() -> Literal[None]: ... + 9 |+def func3() -> None: ... +10 10 | 11 11 | -12 12 | -13 13 | def func4(arg1: Literal[int, None, float]): ... +12 12 | def func4(arg1: Literal[int, None, float]): ... -PYI061.pyi:13:30: PYI061 [*] Use `Literal[...] | None` rather than `Literal[None, ...]` +PYI061.pyi:12:30: PYI061 [*] Use `Literal[...] | None` rather than `Literal[None, ...]` | -13 | def func4(arg1: Literal[int, None, float]): ... +12 | def func4(arg1: Literal[int, None, float]): ... | ^^^^ PYI061 | = help: Replace with `Literal[...] | None` ℹ Safe fix -10 10 | def func3() -> Literal[None]: ... +9 9 | def func3() -> Literal[None]: ... +10 10 | 11 11 | -12 12 | -13 |-def func4(arg1: Literal[int, None, float]): ... - 13 |+def func4(arg1: Literal[int, float] | None): ... +12 |-def func4(arg1: Literal[int, None, float]): ... + 12 |+def func4(arg1: Literal[int, float] | None): ... +13 13 | 14 14 | -15 15 | -16 16 | def func5(arg1: Literal[None, None]): ... +15 15 | def func5(arg1: Literal[None, None]): ... -PYI061.pyi:16:25: PYI061 [*] Use `None` rather than `Literal[None]` +PYI061.pyi:15:25: PYI061 [*] Use `None` rather than `Literal[None]` | -16 | def func5(arg1: Literal[None, None]): ... +15 | def func5(arg1: Literal[None, None]): ... | ^^^^ PYI061 | = help: Replace with `None` ℹ Safe fix -13 13 | def func4(arg1: Literal[int, None, float]): ... +12 12 | def func4(arg1: Literal[int, None, float]): ... +13 13 | 14 14 | -15 15 | -16 |-def func5(arg1: Literal[None, None]): ... - 16 |+def func5(arg1: None): ... +15 |-def func5(arg1: Literal[None, None]): ... + 15 |+def func5(arg1: None): ... +16 16 | 17 17 | -18 18 | -19 19 | def func6(arg1: Literal[ +18 18 | def func6(arg1: Literal[ -PYI061.pyi:16:31: PYI061 [*] Use `None` rather than `Literal[None]` +PYI061.pyi:15:31: PYI061 [*] Use `None` rather than `Literal[None]` | -16 | def func5(arg1: Literal[None, None]): ... +15 | def func5(arg1: Literal[None, None]): ... | ^^^^ PYI061 | = help: Replace with `None` ℹ Safe fix -13 13 | def func4(arg1: Literal[int, None, float]): ... +12 12 | def func4(arg1: Literal[int, None, float]): ... +13 13 | 14 14 | -15 15 | -16 |-def func5(arg1: Literal[None, None]): ... - 16 |+def func5(arg1: None): ... +15 |-def func5(arg1: Literal[None, None]): ... + 15 |+def func5(arg1: None): ... +16 16 | 17 17 | -18 18 | -19 19 | def func6(arg1: Literal[ +18 18 | def func6(arg1: Literal[ -PYI061.pyi:21:5: PYI061 [*] Use `Literal[...] | None` rather than `Literal[None, ...]` +PYI061.pyi:20:5: PYI061 [*] Use `Literal[...] | None` rather than `Literal[None, ...]` | -19 | def func6(arg1: Literal[ -20 | "hello", -21 | None # Comment 1 +18 | def func6(arg1: Literal[ +19 | "hello", +20 | None # Comment 1 | ^^^^ PYI061 -22 | , "world" -23 | ]): ... +21 | , "world" +22 | ]): ... | = help: Replace with `Literal[...] | None` ℹ Unsafe fix -16 16 | def func5(arg1: Literal[None, None]): ... +15 15 | def func5(arg1: Literal[None, None]): ... +16 16 | 17 17 | -18 18 | -19 |-def func6(arg1: Literal[ -20 |- "hello", -21 |- None # Comment 1 -22 |- , "world" -23 |-]): ... - 19 |+def func6(arg1: Literal["hello", "world"] | None): ... +18 |-def func6(arg1: Literal[ +19 |- "hello", +20 |- None # Comment 1 +21 |- , "world" +22 |-]): ... + 18 |+def func6(arg1: Literal["hello", "world"] | None): ... +23 19 | 24 20 | -25 21 | -26 22 | def func7(arg1: Literal[ +25 21 | def func7(arg1: Literal[ -PYI061.pyi:27:5: PYI061 [*] Use `None` rather than `Literal[None]` +PYI061.pyi:26:5: PYI061 [*] Use `None` rather than `Literal[None]` | -26 | def func7(arg1: Literal[ -27 | None # Comment 1 +25 | def func7(arg1: Literal[ +26 | None # Comment 1 | ^^^^ PYI061 -28 | ]): ... +27 | ]): ... | = help: Replace with `None` ℹ Unsafe fix -23 23 | ]): ... +22 22 | ]): ... +23 23 | 24 24 | -25 25 | -26 |-def func7(arg1: Literal[ -27 |- None # Comment 1 -28 |-]): ... - 26 |+def func7(arg1: None): ... +25 |-def func7(arg1: Literal[ +26 |- None # Comment 1 +27 |-]): ... + 25 |+def func7(arg1: None): ... +28 26 | 29 27 | -30 28 | -31 29 | def func8(arg1: Literal[None] | None):... +30 28 | def func8(arg1: Literal[None] | None):... -PYI061.pyi:31:25: PYI061 Use `None` rather than `Literal[None]` +PYI061.pyi:30:25: PYI061 Use `None` rather than `Literal[None]` | -31 | def func8(arg1: Literal[None] | None):... +30 | def func8(arg1: Literal[None] | None):... | ^^^^ PYI061 | = help: Replace with `None` -PYI061.pyi:34:31: PYI061 [*] Use `None` rather than `Literal[None]` +PYI061.pyi:33:31: PYI061 [*] Use `None` rather than `Literal[None]` | -34 | def func9(arg1: Union[Literal[None], None]): ... +33 | def func9(arg1: Union[Literal[None], None]): ... | ^^^^ PYI061 | = help: Replace with `None` ℹ Safe fix -31 31 | def func8(arg1: Literal[None] | None):... +30 30 | def func8(arg1: Literal[None] | None):... +31 31 | 32 32 | -33 33 | -34 |-def func9(arg1: Union[Literal[None], None]): ... - 34 |+def func9(arg1: Union[None, None]): ... +33 |-def func9(arg1: Union[Literal[None], None]): ... + 33 |+def func9(arg1: Union[None, None]): ... +34 34 | 35 35 | -36 36 | -37 37 | # OK +36 36 | # OK -PYI061.pyi:42:9: PYI061 [*] Use `None` rather than `Literal[None]` +PYI061.pyi:41:9: PYI061 [*] Use `None` rather than `Literal[None]` | -41 | # From flake8-pyi -42 | Literal[None] # PYI061 None inside "Literal[]" expression. Replace with "None" +40 | # From flake8-pyi +41 | Literal[None] # PYI061 None inside "Literal[]" expression. Replace with "None" | ^^^^ PYI061 -43 | Literal[True, None] # PYI061 None inside "Literal[]" expression. Replace with "Literal[True] | None" +42 | Literal[True, None] # PYI061 None inside "Literal[]" expression. Replace with "Literal[True] | None" | = help: Replace with `None` ℹ Safe fix +38 38 | 39 39 | -40 40 | -41 41 | # From flake8-pyi -42 |-Literal[None] # PYI061 None inside "Literal[]" expression. Replace with "None" - 42 |+None # PYI061 None inside "Literal[]" expression. Replace with "None" -43 43 | Literal[True, None] # PYI061 None inside "Literal[]" expression. Replace with "Literal[True] | None" +40 40 | # From flake8-pyi +41 |-Literal[None] # PYI061 None inside "Literal[]" expression. Replace with "None" + 41 |+None # PYI061 None inside "Literal[]" expression. Replace with "None" +42 42 | Literal[True, None] # PYI061 None inside "Literal[]" expression. Replace with "Literal[True] | None" +43 43 | 44 44 | -45 45 | -PYI061.pyi:43:15: PYI061 [*] Use `Literal[...] | None` rather than `Literal[None, ...]` +PYI061.pyi:42:15: PYI061 [*] Use `Literal[...] | None` rather than `Literal[None, ...]` | -41 | # From flake8-pyi -42 | Literal[None] # PYI061 None inside "Literal[]" expression. Replace with "None" -43 | Literal[True, None] # PYI061 None inside "Literal[]" expression. Replace with "Literal[True] | None" +40 | # From flake8-pyi +41 | Literal[None] # PYI061 None inside "Literal[]" expression. Replace with "None" +42 | Literal[True, None] # PYI061 None inside "Literal[]" expression. Replace with "Literal[True] | None" | ^^^^ PYI061 | = help: Replace with `Literal[...] | None` ℹ Safe fix -40 40 | -41 41 | # From flake8-pyi -42 42 | Literal[None] # PYI061 None inside "Literal[]" expression. Replace with "None" -43 |-Literal[True, None] # PYI061 None inside "Literal[]" expression. Replace with "Literal[True] | None" - 43 |+Literal[True] | None # PYI061 None inside "Literal[]" expression. Replace with "Literal[True] | None" +39 39 | +40 40 | # From flake8-pyi +41 41 | Literal[None] # PYI061 None inside "Literal[]" expression. Replace with "None" +42 |-Literal[True, None] # PYI061 None inside "Literal[]" expression. Replace with "Literal[True] | None" + 42 |+Literal[True] | None # PYI061 None inside "Literal[]" expression. Replace with "Literal[True] | None" +43 43 | 44 44 | -45 45 | -46 46 | # Regression tests for https://github.com/astral-sh/ruff/issues/14567 +45 45 | # Regression tests for https://github.com/astral-sh/ruff/issues/14567 -PYI061.pyi:47:12: PYI061 Use `None` rather than `Literal[None]` +PYI061.pyi:46:12: PYI061 Use `None` rather than `Literal[None]` | -46 | # Regression tests for https://github.com/astral-sh/ruff/issues/14567 -47 | x: Literal[None] | None +45 | # Regression tests for https://github.com/astral-sh/ruff/issues/14567 +46 | x: Literal[None] | None | ^^^^ PYI061 -48 | y: None | Literal[None] -49 | z: Union[Literal[None], None] +47 | y: None | Literal[None] +48 | z: Union[Literal[None], None] | = help: Replace with `None` -PYI061.pyi:48:19: PYI061 Use `None` rather than `Literal[None]` +PYI061.pyi:47:19: PYI061 Use `None` rather than `Literal[None]` | -46 | # Regression tests for https://github.com/astral-sh/ruff/issues/14567 -47 | x: Literal[None] | None -48 | y: None | Literal[None] +45 | # Regression tests for https://github.com/astral-sh/ruff/issues/14567 +46 | x: Literal[None] | None +47 | y: None | Literal[None] | ^^^^ PYI061 -49 | z: Union[Literal[None], None] +48 | z: Union[Literal[None], None] | = help: Replace with `None` -PYI061.pyi:49:18: PYI061 [*] Use `None` rather than `Literal[None]` +PYI061.pyi:48:18: PYI061 [*] Use `None` rather than `Literal[None]` | -47 | x: Literal[None] | None -48 | y: None | Literal[None] -49 | z: Union[Literal[None], None] +46 | x: Literal[None] | None +47 | y: None | Literal[None] +48 | z: Union[Literal[None], None] | ^^^^ PYI061 -50 | -51 | a: int | Literal[None] | None +49 | +50 | a: int | Literal[None] | None | = help: Replace with `None` ℹ Safe fix -46 46 | # Regression tests for https://github.com/astral-sh/ruff/issues/14567 -47 47 | x: Literal[None] | None -48 48 | y: None | Literal[None] -49 |-z: Union[Literal[None], None] - 49 |+z: Union[None, None] -50 50 | -51 51 | a: int | Literal[None] | None -52 52 | b: None | Literal[None] | None - -PYI061.pyi:51:18: PYI061 Use `None` rather than `Literal[None]` - | -49 | z: Union[Literal[None], None] -50 | -51 | a: int | Literal[None] | None +45 45 | # Regression tests for https://github.com/astral-sh/ruff/issues/14567 +46 46 | x: Literal[None] | None +47 47 | y: None | Literal[None] +48 |-z: Union[Literal[None], None] + 48 |+z: Union[None, None] +49 49 | +50 50 | a: int | Literal[None] | None +51 51 | b: None | Literal[None] | None + +PYI061.pyi:50:18: PYI061 Use `None` rather than `Literal[None]` + | +48 | z: Union[Literal[None], None] +49 | +50 | a: int | Literal[None] | None | ^^^^ PYI061 -52 | b: None | Literal[None] | None -53 | c: (None | Literal[None]) | None +51 | b: None | Literal[None] | None +52 | c: (None | Literal[None]) | None | = help: Replace with `None` -PYI061.pyi:52:19: PYI061 Use `None` rather than `Literal[None]` +PYI061.pyi:51:19: PYI061 Use `None` rather than `Literal[None]` | -51 | a: int | Literal[None] | None -52 | b: None | Literal[None] | None +50 | a: int | Literal[None] | None +51 | b: None | Literal[None] | None | ^^^^ PYI061 -53 | c: (None | Literal[None]) | None -54 | d: None | (Literal[None] | None) +52 | c: (None | Literal[None]) | None +53 | d: None | (Literal[None] | None) | = help: Replace with `None` -PYI061.pyi:53:20: PYI061 Use `None` rather than `Literal[None]` +PYI061.pyi:52:20: PYI061 Use `None` rather than `Literal[None]` | -51 | a: int | Literal[None] | None -52 | b: None | Literal[None] | None -53 | c: (None | Literal[None]) | None +50 | a: int | Literal[None] | None +51 | b: None | Literal[None] | None +52 | c: (None | Literal[None]) | None | ^^^^ PYI061 -54 | d: None | (Literal[None] | None) -55 | e: None | ((None | Literal[None]) | None) | None +53 | d: None | (Literal[None] | None) +54 | e: None | ((None | Literal[None]) | None) | None | = help: Replace with `None` -PYI061.pyi:54:20: PYI061 Use `None` rather than `Literal[None]` +PYI061.pyi:53:20: PYI061 Use `None` rather than `Literal[None]` | -52 | b: None | Literal[None] | None -53 | c: (None | Literal[None]) | None -54 | d: None | (Literal[None] | None) +51 | b: None | Literal[None] | None +52 | c: (None | Literal[None]) | None +53 | d: None | (Literal[None] | None) | ^^^^ PYI061 -55 | e: None | ((None | Literal[None]) | None) | None -56 | f: Literal[None] | Literal[None] +54 | e: None | ((None | Literal[None]) | None) | None +55 | f: Literal[None] | Literal[None] | = help: Replace with `None` -PYI061.pyi:55:28: PYI061 Use `None` rather than `Literal[None]` +PYI061.pyi:54:28: PYI061 Use `None` rather than `Literal[None]` | -53 | c: (None | Literal[None]) | None -54 | d: None | (Literal[None] | None) -55 | e: None | ((None | Literal[None]) | None) | None +52 | c: (None | Literal[None]) | None +53 | d: None | (Literal[None] | None) +54 | e: None | ((None | Literal[None]) | None) | None | ^^^^ PYI061 -56 | f: Literal[None] | Literal[None] +55 | f: Literal[None] | Literal[None] | = help: Replace with `None` -PYI061.pyi:56:12: PYI061 Use `None` rather than `Literal[None]` +PYI061.pyi:55:12: PYI061 Use `None` rather than `Literal[None]` | -54 | d: None | (Literal[None] | None) -55 | e: None | ((None | Literal[None]) | None) | None -56 | f: Literal[None] | Literal[None] +53 | d: None | (Literal[None] | None) +54 | e: None | ((None | Literal[None]) | None) | None +55 | f: Literal[None] | Literal[None] | ^^^^ PYI061 | = help: Replace with `None` -PYI061.pyi:56:28: PYI061 Use `None` rather than `Literal[None]` +PYI061.pyi:55:28: PYI061 Use `None` rather than `Literal[None]` | -54 | d: None | (Literal[None] | None) -55 | e: None | ((None | Literal[None]) | None) | None -56 | f: Literal[None] | Literal[None] +53 | d: None | (Literal[None] | None) +54 | e: None | ((None | Literal[None]) | None) | None +55 | f: Literal[None] | Literal[None] | ^^^^ PYI061 | = help: Replace with `None` - -PYI061.pyi:60:12: PYI061 [*] Use `None` rather than `Literal[None]` - | -59 | # https://github.com/astral-sh/ruff/issues/16177 -60 | g: Literal[None,] | Literal[None,] - | ^^^^ PYI061 - | - = help: Replace with `None` - -ℹ Safe fix -57 57 | -58 58 | -59 59 | # https://github.com/astral-sh/ruff/issues/16177 -60 |-g: Literal[None,] | Literal[None,] - 60 |+g: None | Literal[None,] - -PYI061.pyi:60:29: PYI061 [*] Use `None` rather than `Literal[None]` - | -59 | # https://github.com/astral-sh/ruff/issues/16177 -60 | g: Literal[None,] | Literal[None,] - | ^^^^ PYI061 - | - = help: Replace with `None` - -ℹ Safe fix -57 57 | -58 58 | -59 59 | # https://github.com/astral-sh/ruff/issues/16177 -60 |-g: Literal[None,] | Literal[None,] - 60 |+g: Literal[None,] | None diff --git a/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__py38_PYI061_PYI061.py.snap b/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__py38_PYI061_PYI061.py.snap index 23269f477543b..99f4969598fa9 100644 --- a/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__py38_PYI061_PYI061.py.snap +++ b/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__py38_PYI061_PYI061.py.snap @@ -484,8 +484,6 @@ PYI061.py:81:12: PYI061 Use `None` rather than `Literal[None]` 80 | e: None | ((None | Literal[None]) | None) | None 81 | f: Literal[None] | Literal[None] | ^^^^ PYI061 -82 | -83 | # https://github.com/astral-sh/ruff/issues/16177 | = help: Replace with `None` @@ -495,37 +493,5 @@ PYI061.py:81:28: PYI061 Use `None` rather than `Literal[None]` 80 | e: None | ((None | Literal[None]) | None) | None 81 | f: Literal[None] | Literal[None] | ^^^^ PYI061 -82 | -83 | # https://github.com/astral-sh/ruff/issues/16177 | = help: Replace with `None` - -PYI061.py:84:12: PYI061 [*] Use `None` rather than `Literal[None]` - | -83 | # https://github.com/astral-sh/ruff/issues/16177 -84 | g: Literal[None,] | Literal[None,] - | ^^^^ PYI061 - | - = help: Replace with `None` - -ℹ Safe fix -81 81 | f: Literal[None] | Literal[None] -82 82 | -83 83 | # https://github.com/astral-sh/ruff/issues/16177 -84 |-g: Literal[None,] | Literal[None,] - 84 |+g: None | Literal[None,] - -PYI061.py:84:29: PYI061 [*] Use `None` rather than `Literal[None]` - | -83 | # https://github.com/astral-sh/ruff/issues/16177 -84 | g: Literal[None,] | Literal[None,] - | ^^^^ PYI061 - | - = help: Replace with `None` - -ℹ Safe fix -81 81 | f: Literal[None] | Literal[None] -82 82 | -83 83 | # https://github.com/astral-sh/ruff/issues/16177 -84 |-g: Literal[None,] | Literal[None,] - 84 |+g: Literal[None,] | None diff --git a/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__py38_PYI061_PYI061.pyi.snap b/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__py38_PYI061_PYI061.pyi.snap index ef7bb3c130977..6424e502ec178 100644 --- a/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__py38_PYI061_PYI061.pyi.snap +++ b/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__py38_PYI061_PYI061.pyi.snap @@ -1,9 +1,11 @@ --- source: crates/ruff_linter/src/rules/flake8_pyi/mod.rs --- -PYI061.pyi:4:25: PYI061 [*] Use `None` rather than `Literal[None]` +PYI061.pyi:3:25: PYI061 [*] Use `None` rather than `Literal[None]` | -4 | def func1(arg1: Literal[None]): ... +1 | from typing import Literal, Union +2 | +3 | def func1(arg1: Literal[None]): ... | ^^^^ PYI061 | = help: Replace with `None` @@ -11,344 +13,313 @@ PYI061.pyi:4:25: PYI061 [*] Use `None` rather than `Literal[None]` ℹ Safe fix 1 1 | from typing import Literal, Union 2 2 | -3 3 | -4 |-def func1(arg1: Literal[None]): ... - 4 |+def func1(arg1: None): ... +3 |-def func1(arg1: Literal[None]): ... + 3 |+def func1(arg1: None): ... +4 4 | 5 5 | -6 6 | -7 7 | def func2(arg1: Literal[None] | int): ... +6 6 | def func2(arg1: Literal[None] | int): ... -PYI061.pyi:7:25: PYI061 [*] Use `None` rather than `Literal[None]` +PYI061.pyi:6:25: PYI061 [*] Use `None` rather than `Literal[None]` | -7 | def func2(arg1: Literal[None] | int): ... +6 | def func2(arg1: Literal[None] | int): ... | ^^^^ PYI061 | = help: Replace with `None` ℹ Safe fix -4 4 | def func1(arg1: Literal[None]): ... +3 3 | def func1(arg1: Literal[None]): ... +4 4 | 5 5 | -6 6 | -7 |-def func2(arg1: Literal[None] | int): ... - 7 |+def func2(arg1: None | int): ... +6 |-def func2(arg1: Literal[None] | int): ... + 6 |+def func2(arg1: None | int): ... +7 7 | 8 8 | -9 9 | -10 10 | def func3() -> Literal[None]: ... +9 9 | def func3() -> Literal[None]: ... -PYI061.pyi:10:24: PYI061 [*] Use `None` rather than `Literal[None]` - | -10 | def func3() -> Literal[None]: ... - | ^^^^ PYI061 - | - = help: Replace with `None` +PYI061.pyi:9:24: PYI061 [*] Use `None` rather than `Literal[None]` + | +9 | def func3() -> Literal[None]: ... + | ^^^^ PYI061 + | + = help: Replace with `None` ℹ Safe fix -7 7 | def func2(arg1: Literal[None] | int): ... +6 6 | def func2(arg1: Literal[None] | int): ... +7 7 | 8 8 | -9 9 | -10 |-def func3() -> Literal[None]: ... - 10 |+def func3() -> None: ... +9 |-def func3() -> Literal[None]: ... + 9 |+def func3() -> None: ... +10 10 | 11 11 | -12 12 | -13 13 | def func4(arg1: Literal[int, None, float]): ... +12 12 | def func4(arg1: Literal[int, None, float]): ... -PYI061.pyi:13:30: PYI061 [*] Use `Literal[...] | None` rather than `Literal[None, ...]` +PYI061.pyi:12:30: PYI061 [*] Use `Literal[...] | None` rather than `Literal[None, ...]` | -13 | def func4(arg1: Literal[int, None, float]): ... +12 | def func4(arg1: Literal[int, None, float]): ... | ^^^^ PYI061 | = help: Replace with `Literal[...] | None` ℹ Safe fix -10 10 | def func3() -> Literal[None]: ... +9 9 | def func3() -> Literal[None]: ... +10 10 | 11 11 | -12 12 | -13 |-def func4(arg1: Literal[int, None, float]): ... - 13 |+def func4(arg1: Literal[int, float] | None): ... +12 |-def func4(arg1: Literal[int, None, float]): ... + 12 |+def func4(arg1: Literal[int, float] | None): ... +13 13 | 14 14 | -15 15 | -16 16 | def func5(arg1: Literal[None, None]): ... +15 15 | def func5(arg1: Literal[None, None]): ... -PYI061.pyi:16:25: PYI061 [*] Use `None` rather than `Literal[None]` +PYI061.pyi:15:25: PYI061 [*] Use `None` rather than `Literal[None]` | -16 | def func5(arg1: Literal[None, None]): ... +15 | def func5(arg1: Literal[None, None]): ... | ^^^^ PYI061 | = help: Replace with `None` ℹ Safe fix -13 13 | def func4(arg1: Literal[int, None, float]): ... +12 12 | def func4(arg1: Literal[int, None, float]): ... +13 13 | 14 14 | -15 15 | -16 |-def func5(arg1: Literal[None, None]): ... - 16 |+def func5(arg1: None): ... +15 |-def func5(arg1: Literal[None, None]): ... + 15 |+def func5(arg1: None): ... +16 16 | 17 17 | -18 18 | -19 19 | def func6(arg1: Literal[ +18 18 | def func6(arg1: Literal[ -PYI061.pyi:16:31: PYI061 [*] Use `None` rather than `Literal[None]` +PYI061.pyi:15:31: PYI061 [*] Use `None` rather than `Literal[None]` | -16 | def func5(arg1: Literal[None, None]): ... +15 | def func5(arg1: Literal[None, None]): ... | ^^^^ PYI061 | = help: Replace with `None` ℹ Safe fix -13 13 | def func4(arg1: Literal[int, None, float]): ... +12 12 | def func4(arg1: Literal[int, None, float]): ... +13 13 | 14 14 | -15 15 | -16 |-def func5(arg1: Literal[None, None]): ... - 16 |+def func5(arg1: None): ... +15 |-def func5(arg1: Literal[None, None]): ... + 15 |+def func5(arg1: None): ... +16 16 | 17 17 | -18 18 | -19 19 | def func6(arg1: Literal[ +18 18 | def func6(arg1: Literal[ -PYI061.pyi:21:5: PYI061 [*] Use `Literal[...] | None` rather than `Literal[None, ...]` +PYI061.pyi:20:5: PYI061 [*] Use `Literal[...] | None` rather than `Literal[None, ...]` | -19 | def func6(arg1: Literal[ -20 | "hello", -21 | None # Comment 1 +18 | def func6(arg1: Literal[ +19 | "hello", +20 | None # Comment 1 | ^^^^ PYI061 -22 | , "world" -23 | ]): ... +21 | , "world" +22 | ]): ... | = help: Replace with `Literal[...] | None` ℹ Unsafe fix -16 16 | def func5(arg1: Literal[None, None]): ... +15 15 | def func5(arg1: Literal[None, None]): ... +16 16 | 17 17 | -18 18 | -19 |-def func6(arg1: Literal[ -20 |- "hello", -21 |- None # Comment 1 -22 |- , "world" -23 |-]): ... - 19 |+def func6(arg1: Literal["hello", "world"] | None): ... +18 |-def func6(arg1: Literal[ +19 |- "hello", +20 |- None # Comment 1 +21 |- , "world" +22 |-]): ... + 18 |+def func6(arg1: Literal["hello", "world"] | None): ... +23 19 | 24 20 | -25 21 | -26 22 | def func7(arg1: Literal[ +25 21 | def func7(arg1: Literal[ -PYI061.pyi:27:5: PYI061 [*] Use `None` rather than `Literal[None]` +PYI061.pyi:26:5: PYI061 [*] Use `None` rather than `Literal[None]` | -26 | def func7(arg1: Literal[ -27 | None # Comment 1 +25 | def func7(arg1: Literal[ +26 | None # Comment 1 | ^^^^ PYI061 -28 | ]): ... +27 | ]): ... | = help: Replace with `None` ℹ Unsafe fix -23 23 | ]): ... +22 22 | ]): ... +23 23 | 24 24 | -25 25 | -26 |-def func7(arg1: Literal[ -27 |- None # Comment 1 -28 |-]): ... - 26 |+def func7(arg1: None): ... +25 |-def func7(arg1: Literal[ +26 |- None # Comment 1 +27 |-]): ... + 25 |+def func7(arg1: None): ... +28 26 | 29 27 | -30 28 | -31 29 | def func8(arg1: Literal[None] | None):... +30 28 | def func8(arg1: Literal[None] | None):... -PYI061.pyi:31:25: PYI061 Use `None` rather than `Literal[None]` +PYI061.pyi:30:25: PYI061 Use `None` rather than `Literal[None]` | -31 | def func8(arg1: Literal[None] | None):... +30 | def func8(arg1: Literal[None] | None):... | ^^^^ PYI061 | = help: Replace with `None` -PYI061.pyi:34:31: PYI061 [*] Use `None` rather than `Literal[None]` +PYI061.pyi:33:31: PYI061 [*] Use `None` rather than `Literal[None]` | -34 | def func9(arg1: Union[Literal[None], None]): ... +33 | def func9(arg1: Union[Literal[None], None]): ... | ^^^^ PYI061 | = help: Replace with `None` ℹ Safe fix -31 31 | def func8(arg1: Literal[None] | None):... +30 30 | def func8(arg1: Literal[None] | None):... +31 31 | 32 32 | -33 33 | -34 |-def func9(arg1: Union[Literal[None], None]): ... - 34 |+def func9(arg1: Union[None, None]): ... +33 |-def func9(arg1: Union[Literal[None], None]): ... + 33 |+def func9(arg1: Union[None, None]): ... +34 34 | 35 35 | -36 36 | -37 37 | # OK +36 36 | # OK -PYI061.pyi:42:9: PYI061 [*] Use `None` rather than `Literal[None]` +PYI061.pyi:41:9: PYI061 [*] Use `None` rather than `Literal[None]` | -41 | # From flake8-pyi -42 | Literal[None] # PYI061 None inside "Literal[]" expression. Replace with "None" +40 | # From flake8-pyi +41 | Literal[None] # PYI061 None inside "Literal[]" expression. Replace with "None" | ^^^^ PYI061 -43 | Literal[True, None] # PYI061 None inside "Literal[]" expression. Replace with "Literal[True] | None" +42 | Literal[True, None] # PYI061 None inside "Literal[]" expression. Replace with "Literal[True] | None" | = help: Replace with `None` ℹ Safe fix +38 38 | 39 39 | -40 40 | -41 41 | # From flake8-pyi -42 |-Literal[None] # PYI061 None inside "Literal[]" expression. Replace with "None" - 42 |+None # PYI061 None inside "Literal[]" expression. Replace with "None" -43 43 | Literal[True, None] # PYI061 None inside "Literal[]" expression. Replace with "Literal[True] | None" +40 40 | # From flake8-pyi +41 |-Literal[None] # PYI061 None inside "Literal[]" expression. Replace with "None" + 41 |+None # PYI061 None inside "Literal[]" expression. Replace with "None" +42 42 | Literal[True, None] # PYI061 None inside "Literal[]" expression. Replace with "Literal[True] | None" +43 43 | 44 44 | -45 45 | -PYI061.pyi:43:15: PYI061 [*] Use `Literal[...] | None` rather than `Literal[None, ...]` +PYI061.pyi:42:15: PYI061 [*] Use `Literal[...] | None` rather than `Literal[None, ...]` | -41 | # From flake8-pyi -42 | Literal[None] # PYI061 None inside "Literal[]" expression. Replace with "None" -43 | Literal[True, None] # PYI061 None inside "Literal[]" expression. Replace with "Literal[True] | None" +40 | # From flake8-pyi +41 | Literal[None] # PYI061 None inside "Literal[]" expression. Replace with "None" +42 | Literal[True, None] # PYI061 None inside "Literal[]" expression. Replace with "Literal[True] | None" | ^^^^ PYI061 | = help: Replace with `Literal[...] | None` ℹ Safe fix -40 40 | -41 41 | # From flake8-pyi -42 42 | Literal[None] # PYI061 None inside "Literal[]" expression. Replace with "None" -43 |-Literal[True, None] # PYI061 None inside "Literal[]" expression. Replace with "Literal[True] | None" - 43 |+Literal[True] | None # PYI061 None inside "Literal[]" expression. Replace with "Literal[True] | None" +39 39 | +40 40 | # From flake8-pyi +41 41 | Literal[None] # PYI061 None inside "Literal[]" expression. Replace with "None" +42 |-Literal[True, None] # PYI061 None inside "Literal[]" expression. Replace with "Literal[True] | None" + 42 |+Literal[True] | None # PYI061 None inside "Literal[]" expression. Replace with "Literal[True] | None" +43 43 | 44 44 | -45 45 | -46 46 | # Regression tests for https://github.com/astral-sh/ruff/issues/14567 +45 45 | # Regression tests for https://github.com/astral-sh/ruff/issues/14567 -PYI061.pyi:47:12: PYI061 Use `None` rather than `Literal[None]` +PYI061.pyi:46:12: PYI061 Use `None` rather than `Literal[None]` | -46 | # Regression tests for https://github.com/astral-sh/ruff/issues/14567 -47 | x: Literal[None] | None +45 | # Regression tests for https://github.com/astral-sh/ruff/issues/14567 +46 | x: Literal[None] | None | ^^^^ PYI061 -48 | y: None | Literal[None] -49 | z: Union[Literal[None], None] +47 | y: None | Literal[None] +48 | z: Union[Literal[None], None] | = help: Replace with `None` -PYI061.pyi:48:19: PYI061 Use `None` rather than `Literal[None]` +PYI061.pyi:47:19: PYI061 Use `None` rather than `Literal[None]` | -46 | # Regression tests for https://github.com/astral-sh/ruff/issues/14567 -47 | x: Literal[None] | None -48 | y: None | Literal[None] +45 | # Regression tests for https://github.com/astral-sh/ruff/issues/14567 +46 | x: Literal[None] | None +47 | y: None | Literal[None] | ^^^^ PYI061 -49 | z: Union[Literal[None], None] +48 | z: Union[Literal[None], None] | = help: Replace with `None` -PYI061.pyi:49:18: PYI061 [*] Use `None` rather than `Literal[None]` +PYI061.pyi:48:18: PYI061 [*] Use `None` rather than `Literal[None]` | -47 | x: Literal[None] | None -48 | y: None | Literal[None] -49 | z: Union[Literal[None], None] +46 | x: Literal[None] | None +47 | y: None | Literal[None] +48 | z: Union[Literal[None], None] | ^^^^ PYI061 -50 | -51 | a: int | Literal[None] | None +49 | +50 | a: int | Literal[None] | None | = help: Replace with `None` ℹ Safe fix -46 46 | # Regression tests for https://github.com/astral-sh/ruff/issues/14567 -47 47 | x: Literal[None] | None -48 48 | y: None | Literal[None] -49 |-z: Union[Literal[None], None] - 49 |+z: Union[None, None] -50 50 | -51 51 | a: int | Literal[None] | None -52 52 | b: None | Literal[None] | None - -PYI061.pyi:51:18: PYI061 Use `None` rather than `Literal[None]` - | -49 | z: Union[Literal[None], None] -50 | -51 | a: int | Literal[None] | None +45 45 | # Regression tests for https://github.com/astral-sh/ruff/issues/14567 +46 46 | x: Literal[None] | None +47 47 | y: None | Literal[None] +48 |-z: Union[Literal[None], None] + 48 |+z: Union[None, None] +49 49 | +50 50 | a: int | Literal[None] | None +51 51 | b: None | Literal[None] | None + +PYI061.pyi:50:18: PYI061 Use `None` rather than `Literal[None]` + | +48 | z: Union[Literal[None], None] +49 | +50 | a: int | Literal[None] | None | ^^^^ PYI061 -52 | b: None | Literal[None] | None -53 | c: (None | Literal[None]) | None +51 | b: None | Literal[None] | None +52 | c: (None | Literal[None]) | None | = help: Replace with `None` -PYI061.pyi:52:19: PYI061 Use `None` rather than `Literal[None]` +PYI061.pyi:51:19: PYI061 Use `None` rather than `Literal[None]` | -51 | a: int | Literal[None] | None -52 | b: None | Literal[None] | None +50 | a: int | Literal[None] | None +51 | b: None | Literal[None] | None | ^^^^ PYI061 -53 | c: (None | Literal[None]) | None -54 | d: None | (Literal[None] | None) +52 | c: (None | Literal[None]) | None +53 | d: None | (Literal[None] | None) | = help: Replace with `None` -PYI061.pyi:53:20: PYI061 Use `None` rather than `Literal[None]` +PYI061.pyi:52:20: PYI061 Use `None` rather than `Literal[None]` | -51 | a: int | Literal[None] | None -52 | b: None | Literal[None] | None -53 | c: (None | Literal[None]) | None +50 | a: int | Literal[None] | None +51 | b: None | Literal[None] | None +52 | c: (None | Literal[None]) | None | ^^^^ PYI061 -54 | d: None | (Literal[None] | None) -55 | e: None | ((None | Literal[None]) | None) | None +53 | d: None | (Literal[None] | None) +54 | e: None | ((None | Literal[None]) | None) | None | = help: Replace with `None` -PYI061.pyi:54:20: PYI061 Use `None` rather than `Literal[None]` +PYI061.pyi:53:20: PYI061 Use `None` rather than `Literal[None]` | -52 | b: None | Literal[None] | None -53 | c: (None | Literal[None]) | None -54 | d: None | (Literal[None] | None) +51 | b: None | Literal[None] | None +52 | c: (None | Literal[None]) | None +53 | d: None | (Literal[None] | None) | ^^^^ PYI061 -55 | e: None | ((None | Literal[None]) | None) | None -56 | f: Literal[None] | Literal[None] +54 | e: None | ((None | Literal[None]) | None) | None +55 | f: Literal[None] | Literal[None] | = help: Replace with `None` -PYI061.pyi:55:28: PYI061 Use `None` rather than `Literal[None]` +PYI061.pyi:54:28: PYI061 Use `None` rather than `Literal[None]` | -53 | c: (None | Literal[None]) | None -54 | d: None | (Literal[None] | None) -55 | e: None | ((None | Literal[None]) | None) | None +52 | c: (None | Literal[None]) | None +53 | d: None | (Literal[None] | None) +54 | e: None | ((None | Literal[None]) | None) | None | ^^^^ PYI061 -56 | f: Literal[None] | Literal[None] +55 | f: Literal[None] | Literal[None] | = help: Replace with `None` -PYI061.pyi:56:12: PYI061 Use `None` rather than `Literal[None]` +PYI061.pyi:55:12: PYI061 Use `None` rather than `Literal[None]` | -54 | d: None | (Literal[None] | None) -55 | e: None | ((None | Literal[None]) | None) | None -56 | f: Literal[None] | Literal[None] +53 | d: None | (Literal[None] | None) +54 | e: None | ((None | Literal[None]) | None) | None +55 | f: Literal[None] | Literal[None] | ^^^^ PYI061 | = help: Replace with `None` -PYI061.pyi:56:28: PYI061 Use `None` rather than `Literal[None]` +PYI061.pyi:55:28: PYI061 Use `None` rather than `Literal[None]` | -54 | d: None | (Literal[None] | None) -55 | e: None | ((None | Literal[None]) | None) | None -56 | f: Literal[None] | Literal[None] +53 | d: None | (Literal[None] | None) +54 | e: None | ((None | Literal[None]) | None) | None +55 | f: Literal[None] | Literal[None] | ^^^^ PYI061 | = help: Replace with `None` - -PYI061.pyi:60:12: PYI061 [*] Use `None` rather than `Literal[None]` - | -59 | # https://github.com/astral-sh/ruff/issues/16177 -60 | g: Literal[None,] | Literal[None,] - | ^^^^ PYI061 - | - = help: Replace with `None` - -ℹ Safe fix -57 57 | -58 58 | -59 59 | # https://github.com/astral-sh/ruff/issues/16177 -60 |-g: Literal[None,] | Literal[None,] - 60 |+g: None | Literal[None,] - -PYI061.pyi:60:29: PYI061 [*] Use `None` rather than `Literal[None]` - | -59 | # https://github.com/astral-sh/ruff/issues/16177 -60 | g: Literal[None,] | Literal[None,] - | ^^^^ PYI061 - | - = help: Replace with `None` - -ℹ Safe fix -57 57 | -58 58 | -59 59 | # https://github.com/astral-sh/ruff/issues/16177 -60 |-g: Literal[None,] | Literal[None,] - 60 |+g: Literal[None,] | None From 7c87bd87d1f033c143602f1e343a6ad4a2bf93f1 Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Mon, 28 Apr 2025 12:03:27 +0100 Subject: [PATCH 7/9] use inline snapshots, revert whitespace change --- crates/ruff/tests/lint.rs | 34 +- ...nt__flake8_pyi_redundant_none_literal.snap | 31 -- .../test/fixtures/flake8_pyi/PYI061.pyi | 1 + ..._flake8_pyi__tests__PYI061_PYI061.pyi.snap | 345 +++++++++--------- ...e8_pyi__tests__py38_PYI061_PYI061.pyi.snap | 345 +++++++++--------- 5 files changed, 370 insertions(+), 386 deletions(-) delete mode 100644 crates/ruff/tests/snapshots/lint__flake8_pyi_redundant_none_literal.snap diff --git a/crates/ruff/tests/lint.rs b/crates/ruff/tests/lint.rs index 77759ddf6451c..389a1f6d3be88 100644 --- a/crates/ruff/tests/lint.rs +++ b/crates/ruff/tests/lint.rs @@ -4983,22 +4983,38 @@ fn flake8_import_convention_unused_aliased_import_no_conflict() { // See: https://github.com/astral-sh/ruff/issues/16177 #[test] fn flake8_pyi_redundant_none_literal() { + let snippet = r#" +from typing import Literal + +# Ruff offers a fix for one of these, but not both of them, as if both were autofixed +# it would result in `None | None`, which leads to a `TypeError` at runtime. +x: Literal[None,] | Literal[None,] +"#; + assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME)) .args(STDIN_BASE_OPTIONS) .args(["--select", "PYI061"]) .args(["--stdin-filename", "test.py"]) .arg("--preview") - .arg("--fix") + .arg("--diff") .arg("-") - .pass_stdin( - r#" -from typing import Literal + .pass_stdin(snippet), @r" + success: false + exit_code: 1 + ----- stdout ----- + --- test.py + +++ test.py + @@ -3,4 +3,4 @@ + + # Ruff offers a fix for one of these, but not both of them, as if both were autofixed + # it would result in `None | None`, which leads to a `TypeError` at runtime. + -x: Literal[None,] | Literal[None,] + +x: None | Literal[None,] -# Ruff offers a fix for one of these, but not both of them, as if both were autofixed -# it would result in a `TypeError` at runtime. -x: Literal[None,] | Literal[None,] -"# - )); + + ----- stderr ----- + Would fix 1 error. + "); } /// Test that private, old-style `TypeVar` generics diff --git a/crates/ruff/tests/snapshots/lint__flake8_pyi_redundant_none_literal.snap b/crates/ruff/tests/snapshots/lint__flake8_pyi_redundant_none_literal.snap deleted file mode 100644 index fb2e5dc771603..0000000000000 --- a/crates/ruff/tests/snapshots/lint__flake8_pyi_redundant_none_literal.snap +++ /dev/null @@ -1,31 +0,0 @@ ---- -source: crates/ruff/tests/lint.rs -info: - program: ruff - args: - - check - - "--no-cache" - - "--output-format" - - concise - - "--select" - - PYI061 - - "--stdin-filename" - - test.py - - "--preview" - - "--fix" - - "-" - stdin: "\nfrom typing import Literal\n\n# Ruff offers a fix for one of these, but not both of them, as if both were autofixed\n# it would result in a `TypeError` at runtime.\nx: Literal[None,] | Literal[None,]\n" ---- -success: false -exit_code: 1 ------ stdout ----- - -from typing import Literal - -# Ruff offers a fix for one of these, but not both of them, as if both were autofixed -# it would result in a `TypeError` at runtime. -x: None | Literal[None,] - ------ stderr ----- -test.py:6:19: PYI061 Use `None` rather than `Literal[None]` -Found 2 errors (1 fixed, 1 remaining). diff --git a/crates/ruff_linter/resources/test/fixtures/flake8_pyi/PYI061.pyi b/crates/ruff_linter/resources/test/fixtures/flake8_pyi/PYI061.pyi index 2fb9b211490a7..ed879dd646e34 100644 --- a/crates/ruff_linter/resources/test/fixtures/flake8_pyi/PYI061.pyi +++ b/crates/ruff_linter/resources/test/fixtures/flake8_pyi/PYI061.pyi @@ -1,5 +1,6 @@ from typing import Literal, Union + def func1(arg1: Literal[None]): ... diff --git a/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI061_PYI061.pyi.snap b/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI061_PYI061.pyi.snap index 6424e502ec178..a2c845c0649b8 100644 --- a/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI061_PYI061.pyi.snap +++ b/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI061_PYI061.pyi.snap @@ -1,11 +1,9 @@ --- source: crates/ruff_linter/src/rules/flake8_pyi/mod.rs --- -PYI061.pyi:3:25: PYI061 [*] Use `None` rather than `Literal[None]` +PYI061.pyi:4:25: PYI061 [*] Use `None` rather than `Literal[None]` | -1 | from typing import Literal, Union -2 | -3 | def func1(arg1: Literal[None]): ... +4 | def func1(arg1: Literal[None]): ... | ^^^^ PYI061 | = help: Replace with `None` @@ -13,313 +11,314 @@ PYI061.pyi:3:25: PYI061 [*] Use `None` rather than `Literal[None]` ℹ Safe fix 1 1 | from typing import Literal, Union 2 2 | -3 |-def func1(arg1: Literal[None]): ... - 3 |+def func1(arg1: None): ... -4 4 | +3 3 | +4 |-def func1(arg1: Literal[None]): ... + 4 |+def func1(arg1: None): ... 5 5 | -6 6 | def func2(arg1: Literal[None] | int): ... +6 6 | +7 7 | def func2(arg1: Literal[None] | int): ... -PYI061.pyi:6:25: PYI061 [*] Use `None` rather than `Literal[None]` +PYI061.pyi:7:25: PYI061 [*] Use `None` rather than `Literal[None]` | -6 | def func2(arg1: Literal[None] | int): ... +7 | def func2(arg1: Literal[None] | int): ... | ^^^^ PYI061 | = help: Replace with `None` ℹ Safe fix -3 3 | def func1(arg1: Literal[None]): ... -4 4 | +4 4 | def func1(arg1: Literal[None]): ... 5 5 | -6 |-def func2(arg1: Literal[None] | int): ... - 6 |+def func2(arg1: None | int): ... -7 7 | +6 6 | +7 |-def func2(arg1: Literal[None] | int): ... + 7 |+def func2(arg1: None | int): ... 8 8 | -9 9 | def func3() -> Literal[None]: ... +9 9 | +10 10 | def func3() -> Literal[None]: ... -PYI061.pyi:9:24: PYI061 [*] Use `None` rather than `Literal[None]` - | -9 | def func3() -> Literal[None]: ... - | ^^^^ PYI061 - | - = help: Replace with `None` +PYI061.pyi:10:24: PYI061 [*] Use `None` rather than `Literal[None]` + | +10 | def func3() -> Literal[None]: ... + | ^^^^ PYI061 + | + = help: Replace with `None` ℹ Safe fix -6 6 | def func2(arg1: Literal[None] | int): ... -7 7 | +7 7 | def func2(arg1: Literal[None] | int): ... 8 8 | -9 |-def func3() -> Literal[None]: ... - 9 |+def func3() -> None: ... -10 10 | +9 9 | +10 |-def func3() -> Literal[None]: ... + 10 |+def func3() -> None: ... 11 11 | -12 12 | def func4(arg1: Literal[int, None, float]): ... +12 12 | +13 13 | def func4(arg1: Literal[int, None, float]): ... -PYI061.pyi:12:30: PYI061 [*] Use `Literal[...] | None` rather than `Literal[None, ...]` +PYI061.pyi:13:30: PYI061 [*] Use `Literal[...] | None` rather than `Literal[None, ...]` | -12 | def func4(arg1: Literal[int, None, float]): ... +13 | def func4(arg1: Literal[int, None, float]): ... | ^^^^ PYI061 | = help: Replace with `Literal[...] | None` ℹ Safe fix -9 9 | def func3() -> Literal[None]: ... -10 10 | +10 10 | def func3() -> Literal[None]: ... 11 11 | -12 |-def func4(arg1: Literal[int, None, float]): ... - 12 |+def func4(arg1: Literal[int, float] | None): ... -13 13 | +12 12 | +13 |-def func4(arg1: Literal[int, None, float]): ... + 13 |+def func4(arg1: Literal[int, float] | None): ... 14 14 | -15 15 | def func5(arg1: Literal[None, None]): ... +15 15 | +16 16 | def func5(arg1: Literal[None, None]): ... -PYI061.pyi:15:25: PYI061 [*] Use `None` rather than `Literal[None]` +PYI061.pyi:16:25: PYI061 [*] Use `None` rather than `Literal[None]` | -15 | def func5(arg1: Literal[None, None]): ... +16 | def func5(arg1: Literal[None, None]): ... | ^^^^ PYI061 | = help: Replace with `None` ℹ Safe fix -12 12 | def func4(arg1: Literal[int, None, float]): ... -13 13 | +13 13 | def func4(arg1: Literal[int, None, float]): ... 14 14 | -15 |-def func5(arg1: Literal[None, None]): ... - 15 |+def func5(arg1: None): ... -16 16 | +15 15 | +16 |-def func5(arg1: Literal[None, None]): ... + 16 |+def func5(arg1: None): ... 17 17 | -18 18 | def func6(arg1: Literal[ +18 18 | +19 19 | def func6(arg1: Literal[ -PYI061.pyi:15:31: PYI061 [*] Use `None` rather than `Literal[None]` +PYI061.pyi:16:31: PYI061 [*] Use `None` rather than `Literal[None]` | -15 | def func5(arg1: Literal[None, None]): ... +16 | def func5(arg1: Literal[None, None]): ... | ^^^^ PYI061 | = help: Replace with `None` ℹ Safe fix -12 12 | def func4(arg1: Literal[int, None, float]): ... -13 13 | +13 13 | def func4(arg1: Literal[int, None, float]): ... 14 14 | -15 |-def func5(arg1: Literal[None, None]): ... - 15 |+def func5(arg1: None): ... -16 16 | +15 15 | +16 |-def func5(arg1: Literal[None, None]): ... + 16 |+def func5(arg1: None): ... 17 17 | -18 18 | def func6(arg1: Literal[ +18 18 | +19 19 | def func6(arg1: Literal[ -PYI061.pyi:20:5: PYI061 [*] Use `Literal[...] | None` rather than `Literal[None, ...]` +PYI061.pyi:21:5: PYI061 [*] Use `Literal[...] | None` rather than `Literal[None, ...]` | -18 | def func6(arg1: Literal[ -19 | "hello", -20 | None # Comment 1 +19 | def func6(arg1: Literal[ +20 | "hello", +21 | None # Comment 1 | ^^^^ PYI061 -21 | , "world" -22 | ]): ... +22 | , "world" +23 | ]): ... | = help: Replace with `Literal[...] | None` ℹ Unsafe fix -15 15 | def func5(arg1: Literal[None, None]): ... -16 16 | +16 16 | def func5(arg1: Literal[None, None]): ... 17 17 | -18 |-def func6(arg1: Literal[ -19 |- "hello", -20 |- None # Comment 1 -21 |- , "world" -22 |-]): ... - 18 |+def func6(arg1: Literal["hello", "world"] | None): ... -23 19 | +18 18 | +19 |-def func6(arg1: Literal[ +20 |- "hello", +21 |- None # Comment 1 +22 |- , "world" +23 |-]): ... + 19 |+def func6(arg1: Literal["hello", "world"] | None): ... 24 20 | -25 21 | def func7(arg1: Literal[ +25 21 | +26 22 | def func7(arg1: Literal[ -PYI061.pyi:26:5: PYI061 [*] Use `None` rather than `Literal[None]` +PYI061.pyi:27:5: PYI061 [*] Use `None` rather than `Literal[None]` | -25 | def func7(arg1: Literal[ -26 | None # Comment 1 +26 | def func7(arg1: Literal[ +27 | None # Comment 1 | ^^^^ PYI061 -27 | ]): ... +28 | ]): ... | = help: Replace with `None` ℹ Unsafe fix -22 22 | ]): ... -23 23 | +23 23 | ]): ... 24 24 | -25 |-def func7(arg1: Literal[ -26 |- None # Comment 1 -27 |-]): ... - 25 |+def func7(arg1: None): ... -28 26 | +25 25 | +26 |-def func7(arg1: Literal[ +27 |- None # Comment 1 +28 |-]): ... + 26 |+def func7(arg1: None): ... 29 27 | -30 28 | def func8(arg1: Literal[None] | None):... +30 28 | +31 29 | def func8(arg1: Literal[None] | None):... -PYI061.pyi:30:25: PYI061 Use `None` rather than `Literal[None]` +PYI061.pyi:31:25: PYI061 Use `None` rather than `Literal[None]` | -30 | def func8(arg1: Literal[None] | None):... +31 | def func8(arg1: Literal[None] | None):... | ^^^^ PYI061 | = help: Replace with `None` -PYI061.pyi:33:31: PYI061 [*] Use `None` rather than `Literal[None]` +PYI061.pyi:34:31: PYI061 [*] Use `None` rather than `Literal[None]` | -33 | def func9(arg1: Union[Literal[None], None]): ... +34 | def func9(arg1: Union[Literal[None], None]): ... | ^^^^ PYI061 | = help: Replace with `None` ℹ Safe fix -30 30 | def func8(arg1: Literal[None] | None):... -31 31 | +31 31 | def func8(arg1: Literal[None] | None):... 32 32 | -33 |-def func9(arg1: Union[Literal[None], None]): ... - 33 |+def func9(arg1: Union[None, None]): ... -34 34 | +33 33 | +34 |-def func9(arg1: Union[Literal[None], None]): ... + 34 |+def func9(arg1: Union[None, None]): ... 35 35 | -36 36 | # OK +36 36 | +37 37 | # OK -PYI061.pyi:41:9: PYI061 [*] Use `None` rather than `Literal[None]` +PYI061.pyi:42:9: PYI061 [*] Use `None` rather than `Literal[None]` | -40 | # From flake8-pyi -41 | Literal[None] # PYI061 None inside "Literal[]" expression. Replace with "None" +41 | # From flake8-pyi +42 | Literal[None] # PYI061 None inside "Literal[]" expression. Replace with "None" | ^^^^ PYI061 -42 | Literal[True, None] # PYI061 None inside "Literal[]" expression. Replace with "Literal[True] | None" +43 | Literal[True, None] # PYI061 None inside "Literal[]" expression. Replace with "Literal[True] | None" | = help: Replace with `None` ℹ Safe fix -38 38 | 39 39 | -40 40 | # From flake8-pyi -41 |-Literal[None] # PYI061 None inside "Literal[]" expression. Replace with "None" - 41 |+None # PYI061 None inside "Literal[]" expression. Replace with "None" -42 42 | Literal[True, None] # PYI061 None inside "Literal[]" expression. Replace with "Literal[True] | None" -43 43 | +40 40 | +41 41 | # From flake8-pyi +42 |-Literal[None] # PYI061 None inside "Literal[]" expression. Replace with "None" + 42 |+None # PYI061 None inside "Literal[]" expression. Replace with "None" +43 43 | Literal[True, None] # PYI061 None inside "Literal[]" expression. Replace with "Literal[True] | None" 44 44 | +45 45 | -PYI061.pyi:42:15: PYI061 [*] Use `Literal[...] | None` rather than `Literal[None, ...]` +PYI061.pyi:43:15: PYI061 [*] Use `Literal[...] | None` rather than `Literal[None, ...]` | -40 | # From flake8-pyi -41 | Literal[None] # PYI061 None inside "Literal[]" expression. Replace with "None" -42 | Literal[True, None] # PYI061 None inside "Literal[]" expression. Replace with "Literal[True] | None" +41 | # From flake8-pyi +42 | Literal[None] # PYI061 None inside "Literal[]" expression. Replace with "None" +43 | Literal[True, None] # PYI061 None inside "Literal[]" expression. Replace with "Literal[True] | None" | ^^^^ PYI061 | = help: Replace with `Literal[...] | None` ℹ Safe fix -39 39 | -40 40 | # From flake8-pyi -41 41 | Literal[None] # PYI061 None inside "Literal[]" expression. Replace with "None" -42 |-Literal[True, None] # PYI061 None inside "Literal[]" expression. Replace with "Literal[True] | None" - 42 |+Literal[True] | None # PYI061 None inside "Literal[]" expression. Replace with "Literal[True] | None" -43 43 | +40 40 | +41 41 | # From flake8-pyi +42 42 | Literal[None] # PYI061 None inside "Literal[]" expression. Replace with "None" +43 |-Literal[True, None] # PYI061 None inside "Literal[]" expression. Replace with "Literal[True] | None" + 43 |+Literal[True] | None # PYI061 None inside "Literal[]" expression. Replace with "Literal[True] | None" 44 44 | -45 45 | # Regression tests for https://github.com/astral-sh/ruff/issues/14567 +45 45 | +46 46 | # Regression tests for https://github.com/astral-sh/ruff/issues/14567 -PYI061.pyi:46:12: PYI061 Use `None` rather than `Literal[None]` +PYI061.pyi:47:12: PYI061 Use `None` rather than `Literal[None]` | -45 | # Regression tests for https://github.com/astral-sh/ruff/issues/14567 -46 | x: Literal[None] | None +46 | # Regression tests for https://github.com/astral-sh/ruff/issues/14567 +47 | x: Literal[None] | None | ^^^^ PYI061 -47 | y: None | Literal[None] -48 | z: Union[Literal[None], None] +48 | y: None | Literal[None] +49 | z: Union[Literal[None], None] | = help: Replace with `None` -PYI061.pyi:47:19: PYI061 Use `None` rather than `Literal[None]` +PYI061.pyi:48:19: PYI061 Use `None` rather than `Literal[None]` | -45 | # Regression tests for https://github.com/astral-sh/ruff/issues/14567 -46 | x: Literal[None] | None -47 | y: None | Literal[None] +46 | # Regression tests for https://github.com/astral-sh/ruff/issues/14567 +47 | x: Literal[None] | None +48 | y: None | Literal[None] | ^^^^ PYI061 -48 | z: Union[Literal[None], None] +49 | z: Union[Literal[None], None] | = help: Replace with `None` -PYI061.pyi:48:18: PYI061 [*] Use `None` rather than `Literal[None]` +PYI061.pyi:49:18: PYI061 [*] Use `None` rather than `Literal[None]` | -46 | x: Literal[None] | None -47 | y: None | Literal[None] -48 | z: Union[Literal[None], None] +47 | x: Literal[None] | None +48 | y: None | Literal[None] +49 | z: Union[Literal[None], None] | ^^^^ PYI061 -49 | -50 | a: int | Literal[None] | None +50 | +51 | a: int | Literal[None] | None | = help: Replace with `None` ℹ Safe fix -45 45 | # Regression tests for https://github.com/astral-sh/ruff/issues/14567 -46 46 | x: Literal[None] | None -47 47 | y: None | Literal[None] -48 |-z: Union[Literal[None], None] - 48 |+z: Union[None, None] -49 49 | -50 50 | a: int | Literal[None] | None -51 51 | b: None | Literal[None] | None - -PYI061.pyi:50:18: PYI061 Use `None` rather than `Literal[None]` - | -48 | z: Union[Literal[None], None] -49 | -50 | a: int | Literal[None] | None +46 46 | # Regression tests for https://github.com/astral-sh/ruff/issues/14567 +47 47 | x: Literal[None] | None +48 48 | y: None | Literal[None] +49 |-z: Union[Literal[None], None] + 49 |+z: Union[None, None] +50 50 | +51 51 | a: int | Literal[None] | None +52 52 | b: None | Literal[None] | None + +PYI061.pyi:51:18: PYI061 Use `None` rather than `Literal[None]` + | +49 | z: Union[Literal[None], None] +50 | +51 | a: int | Literal[None] | None | ^^^^ PYI061 -51 | b: None | Literal[None] | None -52 | c: (None | Literal[None]) | None +52 | b: None | Literal[None] | None +53 | c: (None | Literal[None]) | None | = help: Replace with `None` -PYI061.pyi:51:19: PYI061 Use `None` rather than `Literal[None]` +PYI061.pyi:52:19: PYI061 Use `None` rather than `Literal[None]` | -50 | a: int | Literal[None] | None -51 | b: None | Literal[None] | None +51 | a: int | Literal[None] | None +52 | b: None | Literal[None] | None | ^^^^ PYI061 -52 | c: (None | Literal[None]) | None -53 | d: None | (Literal[None] | None) +53 | c: (None | Literal[None]) | None +54 | d: None | (Literal[None] | None) | = help: Replace with `None` -PYI061.pyi:52:20: PYI061 Use `None` rather than `Literal[None]` +PYI061.pyi:53:20: PYI061 Use `None` rather than `Literal[None]` | -50 | a: int | Literal[None] | None -51 | b: None | Literal[None] | None -52 | c: (None | Literal[None]) | None +51 | a: int | Literal[None] | None +52 | b: None | Literal[None] | None +53 | c: (None | Literal[None]) | None | ^^^^ PYI061 -53 | d: None | (Literal[None] | None) -54 | e: None | ((None | Literal[None]) | None) | None +54 | d: None | (Literal[None] | None) +55 | e: None | ((None | Literal[None]) | None) | None | = help: Replace with `None` -PYI061.pyi:53:20: PYI061 Use `None` rather than `Literal[None]` +PYI061.pyi:54:20: PYI061 Use `None` rather than `Literal[None]` | -51 | b: None | Literal[None] | None -52 | c: (None | Literal[None]) | None -53 | d: None | (Literal[None] | None) +52 | b: None | Literal[None] | None +53 | c: (None | Literal[None]) | None +54 | d: None | (Literal[None] | None) | ^^^^ PYI061 -54 | e: None | ((None | Literal[None]) | None) | None -55 | f: Literal[None] | Literal[None] +55 | e: None | ((None | Literal[None]) | None) | None +56 | f: Literal[None] | Literal[None] | = help: Replace with `None` -PYI061.pyi:54:28: PYI061 Use `None` rather than `Literal[None]` +PYI061.pyi:55:28: PYI061 Use `None` rather than `Literal[None]` | -52 | c: (None | Literal[None]) | None -53 | d: None | (Literal[None] | None) -54 | e: None | ((None | Literal[None]) | None) | None +53 | c: (None | Literal[None]) | None +54 | d: None | (Literal[None] | None) +55 | e: None | ((None | Literal[None]) | None) | None | ^^^^ PYI061 -55 | f: Literal[None] | Literal[None] +56 | f: Literal[None] | Literal[None] | = help: Replace with `None` -PYI061.pyi:55:12: PYI061 Use `None` rather than `Literal[None]` +PYI061.pyi:56:12: PYI061 Use `None` rather than `Literal[None]` | -53 | d: None | (Literal[None] | None) -54 | e: None | ((None | Literal[None]) | None) | None -55 | f: Literal[None] | Literal[None] +54 | d: None | (Literal[None] | None) +55 | e: None | ((None | Literal[None]) | None) | None +56 | f: Literal[None] | Literal[None] | ^^^^ PYI061 | = help: Replace with `None` -PYI061.pyi:55:28: PYI061 Use `None` rather than `Literal[None]` +PYI061.pyi:56:28: PYI061 Use `None` rather than `Literal[None]` | -53 | d: None | (Literal[None] | None) -54 | e: None | ((None | Literal[None]) | None) | None -55 | f: Literal[None] | Literal[None] +54 | d: None | (Literal[None] | None) +55 | e: None | ((None | Literal[None]) | None) | None +56 | f: Literal[None] | Literal[None] | ^^^^ PYI061 | = help: Replace with `None` diff --git a/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__py38_PYI061_PYI061.pyi.snap b/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__py38_PYI061_PYI061.pyi.snap index 6424e502ec178..a2c845c0649b8 100644 --- a/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__py38_PYI061_PYI061.pyi.snap +++ b/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__py38_PYI061_PYI061.pyi.snap @@ -1,11 +1,9 @@ --- source: crates/ruff_linter/src/rules/flake8_pyi/mod.rs --- -PYI061.pyi:3:25: PYI061 [*] Use `None` rather than `Literal[None]` +PYI061.pyi:4:25: PYI061 [*] Use `None` rather than `Literal[None]` | -1 | from typing import Literal, Union -2 | -3 | def func1(arg1: Literal[None]): ... +4 | def func1(arg1: Literal[None]): ... | ^^^^ PYI061 | = help: Replace with `None` @@ -13,313 +11,314 @@ PYI061.pyi:3:25: PYI061 [*] Use `None` rather than `Literal[None]` ℹ Safe fix 1 1 | from typing import Literal, Union 2 2 | -3 |-def func1(arg1: Literal[None]): ... - 3 |+def func1(arg1: None): ... -4 4 | +3 3 | +4 |-def func1(arg1: Literal[None]): ... + 4 |+def func1(arg1: None): ... 5 5 | -6 6 | def func2(arg1: Literal[None] | int): ... +6 6 | +7 7 | def func2(arg1: Literal[None] | int): ... -PYI061.pyi:6:25: PYI061 [*] Use `None` rather than `Literal[None]` +PYI061.pyi:7:25: PYI061 [*] Use `None` rather than `Literal[None]` | -6 | def func2(arg1: Literal[None] | int): ... +7 | def func2(arg1: Literal[None] | int): ... | ^^^^ PYI061 | = help: Replace with `None` ℹ Safe fix -3 3 | def func1(arg1: Literal[None]): ... -4 4 | +4 4 | def func1(arg1: Literal[None]): ... 5 5 | -6 |-def func2(arg1: Literal[None] | int): ... - 6 |+def func2(arg1: None | int): ... -7 7 | +6 6 | +7 |-def func2(arg1: Literal[None] | int): ... + 7 |+def func2(arg1: None | int): ... 8 8 | -9 9 | def func3() -> Literal[None]: ... +9 9 | +10 10 | def func3() -> Literal[None]: ... -PYI061.pyi:9:24: PYI061 [*] Use `None` rather than `Literal[None]` - | -9 | def func3() -> Literal[None]: ... - | ^^^^ PYI061 - | - = help: Replace with `None` +PYI061.pyi:10:24: PYI061 [*] Use `None` rather than `Literal[None]` + | +10 | def func3() -> Literal[None]: ... + | ^^^^ PYI061 + | + = help: Replace with `None` ℹ Safe fix -6 6 | def func2(arg1: Literal[None] | int): ... -7 7 | +7 7 | def func2(arg1: Literal[None] | int): ... 8 8 | -9 |-def func3() -> Literal[None]: ... - 9 |+def func3() -> None: ... -10 10 | +9 9 | +10 |-def func3() -> Literal[None]: ... + 10 |+def func3() -> None: ... 11 11 | -12 12 | def func4(arg1: Literal[int, None, float]): ... +12 12 | +13 13 | def func4(arg1: Literal[int, None, float]): ... -PYI061.pyi:12:30: PYI061 [*] Use `Literal[...] | None` rather than `Literal[None, ...]` +PYI061.pyi:13:30: PYI061 [*] Use `Literal[...] | None` rather than `Literal[None, ...]` | -12 | def func4(arg1: Literal[int, None, float]): ... +13 | def func4(arg1: Literal[int, None, float]): ... | ^^^^ PYI061 | = help: Replace with `Literal[...] | None` ℹ Safe fix -9 9 | def func3() -> Literal[None]: ... -10 10 | +10 10 | def func3() -> Literal[None]: ... 11 11 | -12 |-def func4(arg1: Literal[int, None, float]): ... - 12 |+def func4(arg1: Literal[int, float] | None): ... -13 13 | +12 12 | +13 |-def func4(arg1: Literal[int, None, float]): ... + 13 |+def func4(arg1: Literal[int, float] | None): ... 14 14 | -15 15 | def func5(arg1: Literal[None, None]): ... +15 15 | +16 16 | def func5(arg1: Literal[None, None]): ... -PYI061.pyi:15:25: PYI061 [*] Use `None` rather than `Literal[None]` +PYI061.pyi:16:25: PYI061 [*] Use `None` rather than `Literal[None]` | -15 | def func5(arg1: Literal[None, None]): ... +16 | def func5(arg1: Literal[None, None]): ... | ^^^^ PYI061 | = help: Replace with `None` ℹ Safe fix -12 12 | def func4(arg1: Literal[int, None, float]): ... -13 13 | +13 13 | def func4(arg1: Literal[int, None, float]): ... 14 14 | -15 |-def func5(arg1: Literal[None, None]): ... - 15 |+def func5(arg1: None): ... -16 16 | +15 15 | +16 |-def func5(arg1: Literal[None, None]): ... + 16 |+def func5(arg1: None): ... 17 17 | -18 18 | def func6(arg1: Literal[ +18 18 | +19 19 | def func6(arg1: Literal[ -PYI061.pyi:15:31: PYI061 [*] Use `None` rather than `Literal[None]` +PYI061.pyi:16:31: PYI061 [*] Use `None` rather than `Literal[None]` | -15 | def func5(arg1: Literal[None, None]): ... +16 | def func5(arg1: Literal[None, None]): ... | ^^^^ PYI061 | = help: Replace with `None` ℹ Safe fix -12 12 | def func4(arg1: Literal[int, None, float]): ... -13 13 | +13 13 | def func4(arg1: Literal[int, None, float]): ... 14 14 | -15 |-def func5(arg1: Literal[None, None]): ... - 15 |+def func5(arg1: None): ... -16 16 | +15 15 | +16 |-def func5(arg1: Literal[None, None]): ... + 16 |+def func5(arg1: None): ... 17 17 | -18 18 | def func6(arg1: Literal[ +18 18 | +19 19 | def func6(arg1: Literal[ -PYI061.pyi:20:5: PYI061 [*] Use `Literal[...] | None` rather than `Literal[None, ...]` +PYI061.pyi:21:5: PYI061 [*] Use `Literal[...] | None` rather than `Literal[None, ...]` | -18 | def func6(arg1: Literal[ -19 | "hello", -20 | None # Comment 1 +19 | def func6(arg1: Literal[ +20 | "hello", +21 | None # Comment 1 | ^^^^ PYI061 -21 | , "world" -22 | ]): ... +22 | , "world" +23 | ]): ... | = help: Replace with `Literal[...] | None` ℹ Unsafe fix -15 15 | def func5(arg1: Literal[None, None]): ... -16 16 | +16 16 | def func5(arg1: Literal[None, None]): ... 17 17 | -18 |-def func6(arg1: Literal[ -19 |- "hello", -20 |- None # Comment 1 -21 |- , "world" -22 |-]): ... - 18 |+def func6(arg1: Literal["hello", "world"] | None): ... -23 19 | +18 18 | +19 |-def func6(arg1: Literal[ +20 |- "hello", +21 |- None # Comment 1 +22 |- , "world" +23 |-]): ... + 19 |+def func6(arg1: Literal["hello", "world"] | None): ... 24 20 | -25 21 | def func7(arg1: Literal[ +25 21 | +26 22 | def func7(arg1: Literal[ -PYI061.pyi:26:5: PYI061 [*] Use `None` rather than `Literal[None]` +PYI061.pyi:27:5: PYI061 [*] Use `None` rather than `Literal[None]` | -25 | def func7(arg1: Literal[ -26 | None # Comment 1 +26 | def func7(arg1: Literal[ +27 | None # Comment 1 | ^^^^ PYI061 -27 | ]): ... +28 | ]): ... | = help: Replace with `None` ℹ Unsafe fix -22 22 | ]): ... -23 23 | +23 23 | ]): ... 24 24 | -25 |-def func7(arg1: Literal[ -26 |- None # Comment 1 -27 |-]): ... - 25 |+def func7(arg1: None): ... -28 26 | +25 25 | +26 |-def func7(arg1: Literal[ +27 |- None # Comment 1 +28 |-]): ... + 26 |+def func7(arg1: None): ... 29 27 | -30 28 | def func8(arg1: Literal[None] | None):... +30 28 | +31 29 | def func8(arg1: Literal[None] | None):... -PYI061.pyi:30:25: PYI061 Use `None` rather than `Literal[None]` +PYI061.pyi:31:25: PYI061 Use `None` rather than `Literal[None]` | -30 | def func8(arg1: Literal[None] | None):... +31 | def func8(arg1: Literal[None] | None):... | ^^^^ PYI061 | = help: Replace with `None` -PYI061.pyi:33:31: PYI061 [*] Use `None` rather than `Literal[None]` +PYI061.pyi:34:31: PYI061 [*] Use `None` rather than `Literal[None]` | -33 | def func9(arg1: Union[Literal[None], None]): ... +34 | def func9(arg1: Union[Literal[None], None]): ... | ^^^^ PYI061 | = help: Replace with `None` ℹ Safe fix -30 30 | def func8(arg1: Literal[None] | None):... -31 31 | +31 31 | def func8(arg1: Literal[None] | None):... 32 32 | -33 |-def func9(arg1: Union[Literal[None], None]): ... - 33 |+def func9(arg1: Union[None, None]): ... -34 34 | +33 33 | +34 |-def func9(arg1: Union[Literal[None], None]): ... + 34 |+def func9(arg1: Union[None, None]): ... 35 35 | -36 36 | # OK +36 36 | +37 37 | # OK -PYI061.pyi:41:9: PYI061 [*] Use `None` rather than `Literal[None]` +PYI061.pyi:42:9: PYI061 [*] Use `None` rather than `Literal[None]` | -40 | # From flake8-pyi -41 | Literal[None] # PYI061 None inside "Literal[]" expression. Replace with "None" +41 | # From flake8-pyi +42 | Literal[None] # PYI061 None inside "Literal[]" expression. Replace with "None" | ^^^^ PYI061 -42 | Literal[True, None] # PYI061 None inside "Literal[]" expression. Replace with "Literal[True] | None" +43 | Literal[True, None] # PYI061 None inside "Literal[]" expression. Replace with "Literal[True] | None" | = help: Replace with `None` ℹ Safe fix -38 38 | 39 39 | -40 40 | # From flake8-pyi -41 |-Literal[None] # PYI061 None inside "Literal[]" expression. Replace with "None" - 41 |+None # PYI061 None inside "Literal[]" expression. Replace with "None" -42 42 | Literal[True, None] # PYI061 None inside "Literal[]" expression. Replace with "Literal[True] | None" -43 43 | +40 40 | +41 41 | # From flake8-pyi +42 |-Literal[None] # PYI061 None inside "Literal[]" expression. Replace with "None" + 42 |+None # PYI061 None inside "Literal[]" expression. Replace with "None" +43 43 | Literal[True, None] # PYI061 None inside "Literal[]" expression. Replace with "Literal[True] | None" 44 44 | +45 45 | -PYI061.pyi:42:15: PYI061 [*] Use `Literal[...] | None` rather than `Literal[None, ...]` +PYI061.pyi:43:15: PYI061 [*] Use `Literal[...] | None` rather than `Literal[None, ...]` | -40 | # From flake8-pyi -41 | Literal[None] # PYI061 None inside "Literal[]" expression. Replace with "None" -42 | Literal[True, None] # PYI061 None inside "Literal[]" expression. Replace with "Literal[True] | None" +41 | # From flake8-pyi +42 | Literal[None] # PYI061 None inside "Literal[]" expression. Replace with "None" +43 | Literal[True, None] # PYI061 None inside "Literal[]" expression. Replace with "Literal[True] | None" | ^^^^ PYI061 | = help: Replace with `Literal[...] | None` ℹ Safe fix -39 39 | -40 40 | # From flake8-pyi -41 41 | Literal[None] # PYI061 None inside "Literal[]" expression. Replace with "None" -42 |-Literal[True, None] # PYI061 None inside "Literal[]" expression. Replace with "Literal[True] | None" - 42 |+Literal[True] | None # PYI061 None inside "Literal[]" expression. Replace with "Literal[True] | None" -43 43 | +40 40 | +41 41 | # From flake8-pyi +42 42 | Literal[None] # PYI061 None inside "Literal[]" expression. Replace with "None" +43 |-Literal[True, None] # PYI061 None inside "Literal[]" expression. Replace with "Literal[True] | None" + 43 |+Literal[True] | None # PYI061 None inside "Literal[]" expression. Replace with "Literal[True] | None" 44 44 | -45 45 | # Regression tests for https://github.com/astral-sh/ruff/issues/14567 +45 45 | +46 46 | # Regression tests for https://github.com/astral-sh/ruff/issues/14567 -PYI061.pyi:46:12: PYI061 Use `None` rather than `Literal[None]` +PYI061.pyi:47:12: PYI061 Use `None` rather than `Literal[None]` | -45 | # Regression tests for https://github.com/astral-sh/ruff/issues/14567 -46 | x: Literal[None] | None +46 | # Regression tests for https://github.com/astral-sh/ruff/issues/14567 +47 | x: Literal[None] | None | ^^^^ PYI061 -47 | y: None | Literal[None] -48 | z: Union[Literal[None], None] +48 | y: None | Literal[None] +49 | z: Union[Literal[None], None] | = help: Replace with `None` -PYI061.pyi:47:19: PYI061 Use `None` rather than `Literal[None]` +PYI061.pyi:48:19: PYI061 Use `None` rather than `Literal[None]` | -45 | # Regression tests for https://github.com/astral-sh/ruff/issues/14567 -46 | x: Literal[None] | None -47 | y: None | Literal[None] +46 | # Regression tests for https://github.com/astral-sh/ruff/issues/14567 +47 | x: Literal[None] | None +48 | y: None | Literal[None] | ^^^^ PYI061 -48 | z: Union[Literal[None], None] +49 | z: Union[Literal[None], None] | = help: Replace with `None` -PYI061.pyi:48:18: PYI061 [*] Use `None` rather than `Literal[None]` +PYI061.pyi:49:18: PYI061 [*] Use `None` rather than `Literal[None]` | -46 | x: Literal[None] | None -47 | y: None | Literal[None] -48 | z: Union[Literal[None], None] +47 | x: Literal[None] | None +48 | y: None | Literal[None] +49 | z: Union[Literal[None], None] | ^^^^ PYI061 -49 | -50 | a: int | Literal[None] | None +50 | +51 | a: int | Literal[None] | None | = help: Replace with `None` ℹ Safe fix -45 45 | # Regression tests for https://github.com/astral-sh/ruff/issues/14567 -46 46 | x: Literal[None] | None -47 47 | y: None | Literal[None] -48 |-z: Union[Literal[None], None] - 48 |+z: Union[None, None] -49 49 | -50 50 | a: int | Literal[None] | None -51 51 | b: None | Literal[None] | None - -PYI061.pyi:50:18: PYI061 Use `None` rather than `Literal[None]` - | -48 | z: Union[Literal[None], None] -49 | -50 | a: int | Literal[None] | None +46 46 | # Regression tests for https://github.com/astral-sh/ruff/issues/14567 +47 47 | x: Literal[None] | None +48 48 | y: None | Literal[None] +49 |-z: Union[Literal[None], None] + 49 |+z: Union[None, None] +50 50 | +51 51 | a: int | Literal[None] | None +52 52 | b: None | Literal[None] | None + +PYI061.pyi:51:18: PYI061 Use `None` rather than `Literal[None]` + | +49 | z: Union[Literal[None], None] +50 | +51 | a: int | Literal[None] | None | ^^^^ PYI061 -51 | b: None | Literal[None] | None -52 | c: (None | Literal[None]) | None +52 | b: None | Literal[None] | None +53 | c: (None | Literal[None]) | None | = help: Replace with `None` -PYI061.pyi:51:19: PYI061 Use `None` rather than `Literal[None]` +PYI061.pyi:52:19: PYI061 Use `None` rather than `Literal[None]` | -50 | a: int | Literal[None] | None -51 | b: None | Literal[None] | None +51 | a: int | Literal[None] | None +52 | b: None | Literal[None] | None | ^^^^ PYI061 -52 | c: (None | Literal[None]) | None -53 | d: None | (Literal[None] | None) +53 | c: (None | Literal[None]) | None +54 | d: None | (Literal[None] | None) | = help: Replace with `None` -PYI061.pyi:52:20: PYI061 Use `None` rather than `Literal[None]` +PYI061.pyi:53:20: PYI061 Use `None` rather than `Literal[None]` | -50 | a: int | Literal[None] | None -51 | b: None | Literal[None] | None -52 | c: (None | Literal[None]) | None +51 | a: int | Literal[None] | None +52 | b: None | Literal[None] | None +53 | c: (None | Literal[None]) | None | ^^^^ PYI061 -53 | d: None | (Literal[None] | None) -54 | e: None | ((None | Literal[None]) | None) | None +54 | d: None | (Literal[None] | None) +55 | e: None | ((None | Literal[None]) | None) | None | = help: Replace with `None` -PYI061.pyi:53:20: PYI061 Use `None` rather than `Literal[None]` +PYI061.pyi:54:20: PYI061 Use `None` rather than `Literal[None]` | -51 | b: None | Literal[None] | None -52 | c: (None | Literal[None]) | None -53 | d: None | (Literal[None] | None) +52 | b: None | Literal[None] | None +53 | c: (None | Literal[None]) | None +54 | d: None | (Literal[None] | None) | ^^^^ PYI061 -54 | e: None | ((None | Literal[None]) | None) | None -55 | f: Literal[None] | Literal[None] +55 | e: None | ((None | Literal[None]) | None) | None +56 | f: Literal[None] | Literal[None] | = help: Replace with `None` -PYI061.pyi:54:28: PYI061 Use `None` rather than `Literal[None]` +PYI061.pyi:55:28: PYI061 Use `None` rather than `Literal[None]` | -52 | c: (None | Literal[None]) | None -53 | d: None | (Literal[None] | None) -54 | e: None | ((None | Literal[None]) | None) | None +53 | c: (None | Literal[None]) | None +54 | d: None | (Literal[None] | None) +55 | e: None | ((None | Literal[None]) | None) | None | ^^^^ PYI061 -55 | f: Literal[None] | Literal[None] +56 | f: Literal[None] | Literal[None] | = help: Replace with `None` -PYI061.pyi:55:12: PYI061 Use `None` rather than `Literal[None]` +PYI061.pyi:56:12: PYI061 Use `None` rather than `Literal[None]` | -53 | d: None | (Literal[None] | None) -54 | e: None | ((None | Literal[None]) | None) | None -55 | f: Literal[None] | Literal[None] +54 | d: None | (Literal[None] | None) +55 | e: None | ((None | Literal[None]) | None) | None +56 | f: Literal[None] | Literal[None] | ^^^^ PYI061 | = help: Replace with `None` -PYI061.pyi:55:28: PYI061 Use `None` rather than `Literal[None]` +PYI061.pyi:56:28: PYI061 Use `None` rather than `Literal[None]` | -53 | d: None | (Literal[None] | None) -54 | e: None | ((None | Literal[None]) | None) | None -55 | f: Literal[None] | Literal[None] +54 | d: None | (Literal[None] | None) +55 | e: None | ((None | Literal[None]) | None) | None +56 | f: Literal[None] | Literal[None] | ^^^^ PYI061 | = help: Replace with `None` From 9149df70ba4f409b817f9902cd0f1b9d9442915e Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Mon, 28 Apr 2025 12:10:19 +0100 Subject: [PATCH 8/9] remove redundant check elsewhere, now that we have a more general fix --- crates/ruff/tests/lint.rs | 7 +++++-- .../test/fixtures/flake8_pyi/PYI061.py | 1 - .../test/fixtures/flake8_pyi/PYI061.pyi | 1 - .../rules/redundant_none_literal.rs | 13 ++---------- ...__flake8_pyi__tests__PYI061_PYI061.py.snap | 20 ------------------- ..._flake8_pyi__tests__PYI061_PYI061.pyi.snap | 20 ------------------- ...ke8_pyi__tests__py38_PYI061_PYI061.py.snap | 20 ------------------- ...e8_pyi__tests__py38_PYI061_PYI061.pyi.snap | 20 ------------------- 8 files changed, 7 insertions(+), 95 deletions(-) diff --git a/crates/ruff/tests/lint.rs b/crates/ruff/tests/lint.rs index 389a1f6d3be88..acc756b4fc0c3 100644 --- a/crates/ruff/tests/lint.rs +++ b/crates/ruff/tests/lint.rs @@ -4989,6 +4989,7 @@ from typing import Literal # Ruff offers a fix for one of these, but not both of them, as if both were autofixed # it would result in `None | None`, which leads to a `TypeError` at runtime. x: Literal[None,] | Literal[None,] +y: Literal[None] | Literal[None] "#; assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME)) @@ -5004,16 +5005,18 @@ x: Literal[None,] | Literal[None,] ----- stdout ----- --- test.py +++ test.py - @@ -3,4 +3,4 @@ + @@ -3,5 +3,5 @@ # Ruff offers a fix for one of these, but not both of them, as if both were autofixed # it would result in `None | None`, which leads to a `TypeError` at runtime. -x: Literal[None,] | Literal[None,] + -y: Literal[None] | Literal[None] +x: None | Literal[None,] + +y: None | Literal[None] ----- stderr ----- - Would fix 1 error. + Would fix 2 errors. "); } diff --git a/crates/ruff_linter/resources/test/fixtures/flake8_pyi/PYI061.py b/crates/ruff_linter/resources/test/fixtures/flake8_pyi/PYI061.py index 51baabe892107..4bdc3d98799b9 100644 --- a/crates/ruff_linter/resources/test/fixtures/flake8_pyi/PYI061.py +++ b/crates/ruff_linter/resources/test/fixtures/flake8_pyi/PYI061.py @@ -78,4 +78,3 @@ def good_func(arg1: Literal[int] | None): c: (None | Literal[None]) | None d: None | (Literal[None] | None) e: None | ((None | Literal[None]) | None) | None -f: Literal[None] | Literal[None] diff --git a/crates/ruff_linter/resources/test/fixtures/flake8_pyi/PYI061.pyi b/crates/ruff_linter/resources/test/fixtures/flake8_pyi/PYI061.pyi index ed879dd646e34..404ac1157edb9 100644 --- a/crates/ruff_linter/resources/test/fixtures/flake8_pyi/PYI061.pyi +++ b/crates/ruff_linter/resources/test/fixtures/flake8_pyi/PYI061.pyi @@ -53,4 +53,3 @@ b: None | Literal[None] | None c: (None | Literal[None]) | None d: None | (Literal[None] | None) e: None | ((None | Literal[None]) | None) | None -f: Literal[None] | Literal[None] diff --git a/crates/ruff_linter/src/rules/flake8_pyi/rules/redundant_none_literal.rs b/crates/ruff_linter/src/rules/flake8_pyi/rules/redundant_none_literal.rs index 9f03196df53aa..07ca4a96c2662 100644 --- a/crates/ruff_linter/src/rules/flake8_pyi/rules/redundant_none_literal.rs +++ b/crates/ruff_linter/src/rules/flake8_pyi/rules/redundant_none_literal.rs @@ -5,7 +5,7 @@ use ruff_python_ast::{ self as ast, helpers::{pep_604_union, typing_optional}, name::Name, - Expr, ExprBinOp, ExprContext, ExprNoneLiteral, ExprSubscript, Operator, PythonVersion, + Expr, ExprBinOp, ExprContext, ExprNoneLiteral, Operator, PythonVersion, }; use ruff_python_semantic::analyze::typing::{traverse_literal, traverse_union}; use ruff_text_size::{Ranged, TextRange}; @@ -178,18 +178,9 @@ fn create_fix( traverse_union( &mut |expr, _| { - if matches!(expr, Expr::NoneLiteral(_)) { + if expr.is_none_literal_expr() { is_fixable = false; } - if expr != literal_expr { - if let Expr::Subscript(ExprSubscript { value, slice, .. }) = expr { - if semantic.match_typing_expr(value, "Literal") - && matches!(**slice, Expr::NoneLiteral(_)) - { - is_fixable = false; - } - } - } }, semantic, enclosing_pep604_union, diff --git a/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI061_PYI061.py.snap b/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI061_PYI061.py.snap index 33bf166e8b796..32cfb3f84c2e4 100644 --- a/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI061_PYI061.py.snap +++ b/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI061_PYI061.py.snap @@ -422,7 +422,6 @@ PYI061.py:79:20: PYI061 Use `None` rather than `Literal[None]` 79 | d: None | (Literal[None] | None) | ^^^^ PYI061 80 | e: None | ((None | Literal[None]) | None) | None -81 | f: Literal[None] | Literal[None] | = help: Replace with `None` @@ -432,24 +431,5 @@ PYI061.py:80:28: PYI061 Use `None` rather than `Literal[None]` 79 | d: None | (Literal[None] | None) 80 | e: None | ((None | Literal[None]) | None) | None | ^^^^ PYI061 -81 | f: Literal[None] | Literal[None] - | - = help: Replace with `None` - -PYI061.py:81:12: PYI061 Use `None` rather than `Literal[None]` - | -79 | d: None | (Literal[None] | None) -80 | e: None | ((None | Literal[None]) | None) | None -81 | f: Literal[None] | Literal[None] - | ^^^^ PYI061 - | - = help: Replace with `None` - -PYI061.py:81:28: PYI061 Use `None` rather than `Literal[None]` - | -79 | d: None | (Literal[None] | None) -80 | e: None | ((None | Literal[None]) | None) | None -81 | f: Literal[None] | Literal[None] - | ^^^^ PYI061 | = help: Replace with `None` diff --git a/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI061_PYI061.pyi.snap b/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI061_PYI061.pyi.snap index a2c845c0649b8..6a48501c8dad8 100644 --- a/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI061_PYI061.pyi.snap +++ b/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI061_PYI061.pyi.snap @@ -291,7 +291,6 @@ PYI061.pyi:54:20: PYI061 Use `None` rather than `Literal[None]` 54 | d: None | (Literal[None] | None) | ^^^^ PYI061 55 | e: None | ((None | Literal[None]) | None) | None -56 | f: Literal[None] | Literal[None] | = help: Replace with `None` @@ -301,24 +300,5 @@ PYI061.pyi:55:28: PYI061 Use `None` rather than `Literal[None]` 54 | d: None | (Literal[None] | None) 55 | e: None | ((None | Literal[None]) | None) | None | ^^^^ PYI061 -56 | f: Literal[None] | Literal[None] - | - = help: Replace with `None` - -PYI061.pyi:56:12: PYI061 Use `None` rather than `Literal[None]` - | -54 | d: None | (Literal[None] | None) -55 | e: None | ((None | Literal[None]) | None) | None -56 | f: Literal[None] | Literal[None] - | ^^^^ PYI061 - | - = help: Replace with `None` - -PYI061.pyi:56:28: PYI061 Use `None` rather than `Literal[None]` - | -54 | d: None | (Literal[None] | None) -55 | e: None | ((None | Literal[None]) | None) | None -56 | f: Literal[None] | Literal[None] - | ^^^^ PYI061 | = help: Replace with `None` diff --git a/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__py38_PYI061_PYI061.py.snap b/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__py38_PYI061_PYI061.py.snap index 99f4969598fa9..bbee6e9da58cd 100644 --- a/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__py38_PYI061_PYI061.py.snap +++ b/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__py38_PYI061_PYI061.py.snap @@ -464,7 +464,6 @@ PYI061.py:79:20: PYI061 Use `None` rather than `Literal[None]` 79 | d: None | (Literal[None] | None) | ^^^^ PYI061 80 | e: None | ((None | Literal[None]) | None) | None -81 | f: Literal[None] | Literal[None] | = help: Replace with `None` @@ -474,24 +473,5 @@ PYI061.py:80:28: PYI061 Use `None` rather than `Literal[None]` 79 | d: None | (Literal[None] | None) 80 | e: None | ((None | Literal[None]) | None) | None | ^^^^ PYI061 -81 | f: Literal[None] | Literal[None] - | - = help: Replace with `None` - -PYI061.py:81:12: PYI061 Use `None` rather than `Literal[None]` - | -79 | d: None | (Literal[None] | None) -80 | e: None | ((None | Literal[None]) | None) | None -81 | f: Literal[None] | Literal[None] - | ^^^^ PYI061 - | - = help: Replace with `None` - -PYI061.py:81:28: PYI061 Use `None` rather than `Literal[None]` - | -79 | d: None | (Literal[None] | None) -80 | e: None | ((None | Literal[None]) | None) | None -81 | f: Literal[None] | Literal[None] - | ^^^^ PYI061 | = help: Replace with `None` diff --git a/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__py38_PYI061_PYI061.pyi.snap b/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__py38_PYI061_PYI061.pyi.snap index a2c845c0649b8..6a48501c8dad8 100644 --- a/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__py38_PYI061_PYI061.pyi.snap +++ b/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__py38_PYI061_PYI061.pyi.snap @@ -291,7 +291,6 @@ PYI061.pyi:54:20: PYI061 Use `None` rather than `Literal[None]` 54 | d: None | (Literal[None] | None) | ^^^^ PYI061 55 | e: None | ((None | Literal[None]) | None) | None -56 | f: Literal[None] | Literal[None] | = help: Replace with `None` @@ -301,24 +300,5 @@ PYI061.pyi:55:28: PYI061 Use `None` rather than `Literal[None]` 54 | d: None | (Literal[None] | None) 55 | e: None | ((None | Literal[None]) | None) | None | ^^^^ PYI061 -56 | f: Literal[None] | Literal[None] - | - = help: Replace with `None` - -PYI061.pyi:56:12: PYI061 Use `None` rather than `Literal[None]` - | -54 | d: None | (Literal[None] | None) -55 | e: None | ((None | Literal[None]) | None) | None -56 | f: Literal[None] | Literal[None] - | ^^^^ PYI061 - | - = help: Replace with `None` - -PYI061.pyi:56:28: PYI061 Use `None` rather than `Literal[None]` - | -54 | d: None | (Literal[None] | None) -55 | e: None | ((None | Literal[None]) | None) | None -56 | f: Literal[None] | Literal[None] - | ^^^^ PYI061 | = help: Replace with `None` From 2d4d484594d6df60d6dbd0f2268139bac6209c99 Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Mon, 28 Apr 2025 12:16:20 +0100 Subject: [PATCH 9/9] more comprehensive integration test --- crates/ruff/tests/lint.rs | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/crates/ruff/tests/lint.rs b/crates/ruff/tests/lint.rs index acc756b4fc0c3..d58e85a468612 100644 --- a/crates/ruff/tests/lint.rs +++ b/crates/ruff/tests/lint.rs @@ -4986,10 +4986,13 @@ fn flake8_pyi_redundant_none_literal() { let snippet = r#" from typing import Literal -# Ruff offers a fix for one of these, but not both of them, as if both were autofixed -# it would result in `None | None`, which leads to a `TypeError` at runtime. -x: Literal[None,] | Literal[None,] -y: Literal[None] | Literal[None] +# For each of these expressions, Ruff provides a fix for one of the `Literal[None]` elements +# but not both, as if both were autofixed it would result in `None | None`, +# which leads to a `TypeError` at runtime. +a: Literal[None,] | Literal[None,] +b: Literal[None] | Literal[None] +c: Literal[None] | Literal[None,] +d: Literal[None,] | Literal[None] "#; assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME)) @@ -5005,18 +5008,22 @@ y: Literal[None] | Literal[None] ----- stdout ----- --- test.py +++ test.py - @@ -3,5 +3,5 @@ - - # Ruff offers a fix for one of these, but not both of them, as if both were autofixed - # it would result in `None | None`, which leads to a `TypeError` at runtime. - -x: Literal[None,] | Literal[None,] - -y: Literal[None] | Literal[None] - +x: None | Literal[None,] - +y: None | Literal[None] + @@ -4,7 +4,7 @@ + # For each of these expressions, Ruff provides a fix for one of the `Literal[None]` elements + # but not both, as if both were autofixed it would result in `None | None`, + # which leads to a `TypeError` at runtime. + -a: Literal[None,] | Literal[None,] + -b: Literal[None] | Literal[None] + -c: Literal[None] | Literal[None,] + -d: Literal[None,] | Literal[None] + +a: None | Literal[None,] + +b: None | Literal[None] + +c: None | Literal[None,] + +d: None | Literal[None] ----- stderr ----- - Would fix 2 errors. + Would fix 4 errors. "); }