From 1f2e93b8af4734bd0dba14586603e34d8d4e6b22 Mon Sep 17 00:00:00 2001 From: Chandra Kiran G Date: Mon, 9 Dec 2024 19:14:46 +0530 Subject: [PATCH 1/8] fix: Correct implementation for PYI061 --- .../rules/redundant_none_literal.rs | 103 +++++++++++++++++- 1 file changed, 98 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 f9e37c7840afce..fb0de4a96d892d 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 @@ -1,11 +1,13 @@ +use std::borrow::BorrowMut; + use ruff_diagnostics::{Applicability, Diagnostic, Edit, Fix, FixAvailability, Violation}; use ruff_macros::{derive_message_formats, ViolationMetadata}; -use ruff_python_ast::{Expr, ExprBinOp, ExprNoneLiteral, ExprSubscript, Operator}; +use ruff_python_ast::{self as ast, name::Name, Expr, ExprBinOp, ExprContext, ExprNoneLiteral, ExprSubscript, Operator}; use ruff_python_semantic::{ analyze::typing::{traverse_literal, traverse_union}, SemanticModel, }; -use ruff_text_size::Ranged; +use ruff_text_size::{Ranged, TextRange}; use smallvec::SmallVec; @@ -73,16 +75,19 @@ pub(crate) fn redundant_none_literal<'a>(checker: &mut Checker, literal_expr: &' let mut none_exprs: SmallVec<[&ExprNoneLiteral; 1]> = SmallVec::new(); let mut other_literal_elements_seen = false; + let mut literal_elements: Vec<&Expr> = Vec::new(); - let mut find_none = |expr: &'a Expr, _parent: &'a Expr| { + let mut find_literal_elements = |expr: &'a Expr, _parent: &'a Expr| { if let Expr::NoneLiteral(none_expr) = expr { none_exprs.push(none_expr); } else { other_literal_elements_seen = true; + literal_elements.push(expr); } }; - traverse_literal(&mut find_none, checker.semantic(), literal_expr); + + traverse_literal(&mut find_literal_elements, checker.semantic(), literal_expr); if none_exprs.is_empty() { return; @@ -91,7 +96,16 @@ pub(crate) fn redundant_none_literal<'a>(checker: &mut Checker, literal_expr: &' // Provide a [`Fix`] when the complete `Literal` can be replaced. Applying the fix // can leave an unused import to be fixed by the `unused-import` rule. let fix = if other_literal_elements_seen { - None + create_fix_edit_2(checker.borrow_mut(), literal_expr, &literal_elements).map(|edit| { + Fix::applicable_edit( + edit, + if checker.comment_ranges().intersects(literal_expr.range()) { + Applicability::Unsafe + } else { + Applicability::Safe + }, + ) + }) } else { create_fix_edit(checker.semantic(), literal_expr).map(|edit| { Fix::applicable_edit( @@ -167,3 +181,82 @@ fn create_fix_edit(semantic: &SemanticModel, literal_expr: &Expr) -> Option Option { + let enclosing_pep604_union = checker.semantic() + .current_expressions() + .skip(1) + .take_while(|expr| { + matches!( + expr, + Expr::BinOp(ExprBinOp { + op: Operator::BitOr, + .. + }) + ) + }) + .last(); + + let mut is_fixable = true; + if let Some(enclosing_pep604_union) = enclosing_pep604_union { + traverse_union( + &mut |expr, _| { + if matches!(expr, Expr::NoneLiteral(_)) { + is_fixable = false; + } + if expr != literal_expr { + if let Expr::Subscript(ExprSubscript { value, slice, .. }) = expr { + if checker.semantic().match_typing_expr(value, "Literal") + && matches!(**slice, Expr::NoneLiteral(_)) + { + is_fixable = false; + } + } + } + }, + checker.semantic(), + enclosing_pep604_union, + ); + } + + let bin_or = Expr::BinOp( + ExprBinOp { + range: TextRange::default(), + left: Box::new(Expr::Subscript(ast::ExprSubscript { + value: Box::new(ruff_python_ast::Expr::Name(ast::ExprName{ + range: TextRange::default(), + id: Name::new("Literal"), + ctx: ExprContext::Load, + })), + range: TextRange::default(), + ctx: ExprContext::Load, + slice: Box::new(Expr::Tuple(ast::ExprTuple { + elts: literal_elements.iter().copied().cloned().collect(), + range: TextRange::default(), + ctx: ExprContext::Load, + parenthesized: true, + })), + })), + op: ruff_python_ast::Operator::BitOr, + right: Box::new(Expr::Subscript(ast::ExprSubscript { + value: Box::new(ruff_python_ast::Expr::Name(ast::ExprName{ + range: TextRange::default(), + id: Name::new("Literal"), + ctx: ExprContext::Load, + })), + range: TextRange::default(), + ctx: ExprContext::Load, + slice: Box::new(Expr::NoneLiteral( + ExprNoneLiteral { + range: TextRange::default(), + } + )), + })), + } + ); + + let content = checker.generator().expr(&bin_or); + + is_fixable.then(|| Edit::range_replacement(content, literal_expr.range())) +} From 2363e7f6ef50136da9713073aabbeb226c28f59c Mon Sep 17 00:00:00 2001 From: Chandra Kiran G Date: Tue, 10 Dec 2024 11:26:17 +0530 Subject: [PATCH 2/8] fix: use first Literal Expr instead of creating one --- .../rules/redundant_none_literal.rs | 74 +++++++++---------- 1 file changed, 35 insertions(+), 39 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 fb0de4a96d892d..f4e85da8beb483 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 @@ -1,8 +1,8 @@ -use std::borrow::BorrowMut; - use ruff_diagnostics::{Applicability, Diagnostic, Edit, Fix, FixAvailability, Violation}; use ruff_macros::{derive_message_formats, ViolationMetadata}; -use ruff_python_ast::{self as ast, name::Name, Expr, ExprBinOp, ExprContext, ExprNoneLiteral, ExprSubscript, Operator}; +use ruff_python_ast::{ + self as ast, Expr, ExprBinOp, ExprContext, ExprNoneLiteral, ExprSubscript, Operator, +}; use ruff_python_semantic::{ analyze::typing::{traverse_literal, traverse_union}, SemanticModel, @@ -76,6 +76,12 @@ pub(crate) fn redundant_none_literal<'a>(checker: &mut Checker, literal_expr: &' let mut none_exprs: SmallVec<[&ExprNoneLiteral; 1]> = SmallVec::new(); let mut other_literal_elements_seen = false; let mut literal_elements: Vec<&Expr> = Vec::new(); + let mut literal_subscript = None; + if let Expr::Subscript(ast::ExprSubscript { value, .. }) = literal_expr { + if checker.semantic().match_typing_expr(value, "Literal") { + literal_subscript = Some(value.as_ref()); + } + }; let mut find_literal_elements = |expr: &'a Expr, _parent: &'a Expr| { if let Expr::NoneLiteral(none_expr) = expr { @@ -86,9 +92,11 @@ pub(crate) fn redundant_none_literal<'a>(checker: &mut Checker, literal_expr: &' } }; + let Some(literal_subscript) = literal_subscript else { + return; + }; traverse_literal(&mut find_literal_elements, checker.semantic(), literal_expr); - if none_exprs.is_empty() { return; } @@ -96,7 +104,7 @@ pub(crate) fn redundant_none_literal<'a>(checker: &mut Checker, literal_expr: &' // Provide a [`Fix`] when the complete `Literal` can be replaced. Applying the fix // can leave an unused import to be fixed by the `unused-import` rule. let fix = if other_literal_elements_seen { - create_fix_edit_2(checker.borrow_mut(), literal_expr, &literal_elements).map(|edit| { + create_fix_edit_2(checker, literal_expr, literal_elements, literal_subscript).map(|edit| { Fix::applicable_edit( edit, if checker.comment_ranges().intersects(literal_expr.range()) { @@ -182,9 +190,14 @@ fn create_fix_edit(semantic: &SemanticModel, literal_expr: &Expr) -> Option Option { - let enclosing_pep604_union = checker.semantic() +fn create_fix_edit_2( + checker: &mut Checker, + literal_expr: &Expr, + literal_elements: Vec<&Expr>, + literal_subscript: &Expr, +) -> Option { + let enclosing_pep604_union = checker + .semantic() .current_expressions() .skip(1) .take_while(|expr| { @@ -220,41 +233,24 @@ fn create_fix_edit_2(checker: &mut Checker, literal_expr: &Expr, literal_element ); } - let bin_or = Expr::BinOp( - ExprBinOp { + let bin_or = Expr::BinOp(ExprBinOp { + range: TextRange::default(), + left: Box::new(Expr::Subscript(ast::ExprSubscript { + value: Box::new(literal_subscript.clone()), range: TextRange::default(), - left: Box::new(Expr::Subscript(ast::ExprSubscript { - value: Box::new(ruff_python_ast::Expr::Name(ast::ExprName{ - range: TextRange::default(), - id: Name::new("Literal"), - ctx: ExprContext::Load, - })), + ctx: ExprContext::Load, + slice: Box::new(Expr::Tuple(ast::ExprTuple { + elts: literal_elements.into_iter().cloned().collect(), range: TextRange::default(), ctx: ExprContext::Load, - slice: Box::new(Expr::Tuple(ast::ExprTuple { - elts: literal_elements.iter().copied().cloned().collect(), - range: TextRange::default(), - ctx: ExprContext::Load, - parenthesized: true, - })), + parenthesized: true, })), - op: ruff_python_ast::Operator::BitOr, - right: Box::new(Expr::Subscript(ast::ExprSubscript { - value: Box::new(ruff_python_ast::Expr::Name(ast::ExprName{ - range: TextRange::default(), - id: Name::new("Literal"), - ctx: ExprContext::Load, - })), - range: TextRange::default(), - ctx: ExprContext::Load, - slice: Box::new(Expr::NoneLiteral( - ExprNoneLiteral { - range: TextRange::default(), - } - )), - })), - } - ); + })), + op: ruff_python_ast::Operator::BitOr, + right: Box::new(Expr::NoneLiteral(ExprNoneLiteral { + range: TextRange::default(), + })), + }); let content = checker.generator().expr(&bin_or); From baf46c9a7a07707862f7937d85d167badf319770 Mon Sep 17 00:00:00 2001 From: Chandra Kiran G Date: Wed, 11 Dec 2024 11:19:18 +0530 Subject: [PATCH 3/8] fix: handle single element Literal seperately from multi-element Literal --- .../flake8_pyi/rules/redundant_none_literal.rs | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 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 f4e85da8beb483..9fa5ae75d078b2 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 @@ -239,12 +239,16 @@ fn create_fix_edit_2( value: Box::new(literal_subscript.clone()), range: TextRange::default(), ctx: ExprContext::Load, - slice: Box::new(Expr::Tuple(ast::ExprTuple { - elts: literal_elements.into_iter().cloned().collect(), - range: TextRange::default(), - ctx: ExprContext::Load, - parenthesized: true, - })), + slice: Box::new(if literal_elements.len() > 1 { + Expr::Tuple(ast::ExprTuple { + elts: literal_elements.clone().into_iter().cloned().collect(), + range: TextRange::default(), + ctx: ExprContext::Load, + parenthesized: true, + }) + } else { + literal_elements[0].clone() + }), })), op: ruff_python_ast::Operator::BitOr, right: Box::new(Expr::NoneLiteral(ExprNoneLiteral { From 0f277659e833d4cf0d5189dfc0cc605f3e40834b Mon Sep 17 00:00:00 2001 From: Chandra Kiran G Date: Wed, 11 Dec 2024 15:01:02 +0530 Subject: [PATCH 4/8] chore: update snapshots --- ...ake8_pyi__tests__PYI061_PYI061.py.snap.new | 457 ++++++++++++++++++ ...ke8_pyi__tests__PYI061_PYI061.pyi.snap.new | 326 +++++++++++++ 2 files changed, 783 insertions(+) create mode 100644 crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI061_PYI061.py.snap.new create mode 100644 crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI061_PYI061.pyi.snap.new diff --git a/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI061_PYI061.py.snap.new b/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI061_PYI061.py.snap.new new file mode 100644 index 00000000000000..606be0f7dc0af0 --- /dev/null +++ b/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI061_PYI061.py.snap.new @@ -0,0 +1,457 @@ +--- +source: crates/ruff_linter/src/rules/flake8_pyi/mod.rs +assertion_line: 135 +snapshot_kind: text +--- +PYI061.py:4:25: PYI061 [*] `Literal[None]` can be replaced with `None` + | +4 | def func1(arg1: Literal[None]): + | ^^^^ PYI061 +5 | ... + | + = help: Replace with `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): +5 5 | ... +6 6 | +7 7 | + +PYI061.py:8:25: PYI061 [*] `Literal[None]` can be replaced with `None` + | +8 | def func2(arg1: Literal[None] | int): + | ^^^^ PYI061 +9 | ... + | + = help: Replace with `None` + +ℹ Safe fix +5 5 | ... +6 6 | +7 7 | +8 |-def func2(arg1: Literal[None] | int): + 8 |+def func2(arg1: None | int): +9 9 | ... +10 10 | +11 11 | + +PYI061.py:12:24: PYI061 [*] `Literal[None]` can be replaced with `None` + | +12 | def func3() -> Literal[None]: + | ^^^^ PYI061 +13 | ... + | + = help: Replace with `None` + +ℹ Safe fix +9 9 | ... +10 10 | +11 11 | +12 |-def func3() -> Literal[None]: + 12 |+def func3() -> None: +13 13 | ... +14 14 | +15 15 | + +PYI061.py:16:30: PYI061 [*] `Literal[None, ...]` can be replaced with `Literal[...] | None` + | +16 | def func4(arg1: Literal[int, None, float]): + | ^^^^ PYI061 +17 | ... + | + = help: Replace with `Literal[...] | None` + +ℹ Safe fix +13 13 | ... +14 14 | +15 15 | +16 |-def func4(arg1: Literal[int, None, float]): + 16 |+def func4(arg1: Literal[int, float] | Literal[None]): +17 17 | ... +18 18 | +19 19 | + +PYI061.py:20:25: PYI061 [*] `Literal[None]` can be replaced with `None` + | +20 | def func5(arg1: Literal[None, None]): + | ^^^^ PYI061 +21 | ... + | + = help: Replace with `None` + +ℹ Safe fix +17 17 | ... +18 18 | +19 19 | +20 |-def func5(arg1: Literal[None, None]): + 20 |+def func5(arg1: None): +21 21 | ... +22 22 | +23 23 | + +PYI061.py:20:31: PYI061 [*] `Literal[None]` can be replaced with `None` + | +20 | def func5(arg1: Literal[None, None]): + | ^^^^ PYI061 +21 | ... + | + = help: Replace with `None` + +ℹ Safe fix +17 17 | ... +18 18 | +19 19 | +20 |-def func5(arg1: Literal[None, None]): + 20 |+def func5(arg1: None): +21 21 | ... +22 22 | +23 23 | + +PYI061.py:26:5: PYI061 [*] `Literal[None, ...]` can be replaced with `Literal[...] | None` + | +24 | def func6(arg1: Literal[ +25 | "hello", +26 | None # Comment 1 + | ^^^^ PYI061 +27 | , "world" +28 | ]): + | + = help: Replace with `Literal[...] | None` + +ℹ Unsafe fix +21 21 | ... +22 22 | +23 23 | +24 |-def func6(arg1: Literal[ +25 |- "hello", +26 |- None # Comment 1 +27 |- , "world" +28 |- ]): + 24 |+def func6(arg1: Literal["hello", "world"] | Literal[None]): +29 25 | ... +30 26 | +31 27 | + +PYI061.py:33:5: PYI061 [*] `Literal[None]` can be replaced with `None` + | +32 | def func7(arg1: Literal[ +33 | None # Comment 1 + | ^^^^ PYI061 +34 | ]): +35 | ... + | + = help: Replace with `None` + +ℹ Unsafe fix +29 29 | ... +30 30 | +31 31 | +32 |-def func7(arg1: Literal[ +33 |- None # Comment 1 +34 |- ]): + 32 |+def func7(arg1: None): +35 33 | ... +36 34 | +37 35 | + +PYI061.py:38:25: PYI061 `Literal[None]` can be replaced with `None` + | +38 | def func8(arg1: Literal[None] | None): + | ^^^^ PYI061 +39 | ... + | + = help: Replace with `None` + +PYI061.py:42:31: PYI061 [*] `Literal[None]` can be replaced with `None` + | +42 | def func9(arg1: Union[Literal[None], None]): + | ^^^^ PYI061 +43 | ... + | + = help: Replace with `None` + +ℹ Safe fix +39 39 | ... +40 40 | +41 41 | +42 |-def func9(arg1: Union[Literal[None], None]): + 42 |+def func9(arg1: Union[None, None]): +43 43 | ... +44 44 | +45 45 | + +PYI061.py:52:9: PYI061 [*] `Literal[None]` can be replaced with `None` + | +51 | # From flake8-pyi +52 | Literal[None] # Y061 None inside "Literal[]" expression. Replace with "None" + | ^^^^ PYI061 +53 | Literal[True, None] # Y061 None inside "Literal[]" expression. Replace with "Literal[True] | None" + | + = help: Replace with `None` + +ℹ Safe fix +49 49 | +50 50 | +51 51 | # From flake8-pyi +52 |-Literal[None] # Y061 None inside "Literal[]" expression. Replace with "None" + 52 |+None # Y061 None inside "Literal[]" expression. Replace with "None" +53 53 | Literal[True, None] # Y061 None inside "Literal[]" expression. Replace with "Literal[True] | None" +54 54 | +55 55 | ### + +PYI061.py:53:15: PYI061 [*] `Literal[None, ...]` can be replaced with `Literal[...] | None` + | +51 | # From flake8-pyi +52 | Literal[None] # Y061 None inside "Literal[]" expression. Replace with "None" +53 | Literal[True, None] # Y061 None inside "Literal[]" expression. Replace with "Literal[True] | None" + | ^^^^ PYI061 +54 | +55 | ### + | + = help: Replace with `Literal[...] | None` + +ℹ Safe fix +50 50 | +51 51 | # From flake8-pyi +52 52 | Literal[None] # Y061 None inside "Literal[]" expression. Replace with "None" +53 |-Literal[True, None] # Y061 None inside "Literal[]" expression. Replace with "Literal[True] | None" + 53 |+Literal[True,] | Literal[None] # Y061 None inside "Literal[]" expression. Replace with "Literal[True] | None" +54 54 | +55 55 | ### +56 56 | # The following rules here are slightly subtle, + +PYI061.py:62:9: PYI061 [*] `Literal[None]` can be replaced with `None` + | +60 | # If Y061 and Y062 both apply, but all the duplicate members are None, +61 | # only emit Y061... +62 | Literal[None, None] # Y061 None inside "Literal[]" expression. Replace with "None" + | ^^^^ PYI061 +63 | Literal[1, None, "foo", None] # Y061 None inside "Literal[]" expression. Replace with "Literal[1, 'foo'] | None" + | + = help: Replace with `None` + +ℹ Safe fix +59 59 | +60 60 | # If Y061 and Y062 both apply, but all the duplicate members are None, +61 61 | # only emit Y061... +62 |-Literal[None, None] # Y061 None inside "Literal[]" expression. Replace with "None" + 62 |+None # Y061 None inside "Literal[]" expression. Replace with "None" +63 63 | Literal[1, None, "foo", None] # Y061 None inside "Literal[]" expression. Replace with "Literal[1, 'foo'] | None" +64 64 | +65 65 | # ... but if Y061 and Y062 both apply + +PYI061.py:62:15: PYI061 [*] `Literal[None]` can be replaced with `None` + | +60 | # If Y061 and Y062 both apply, but all the duplicate members are None, +61 | # only emit Y061... +62 | Literal[None, None] # Y061 None inside "Literal[]" expression. Replace with "None" + | ^^^^ PYI061 +63 | Literal[1, None, "foo", None] # Y061 None inside "Literal[]" expression. Replace with "Literal[1, 'foo'] | None" + | + = help: Replace with `None` + +ℹ Safe fix +59 59 | +60 60 | # If Y061 and Y062 both apply, but all the duplicate members are None, +61 61 | # only emit Y061... +62 |-Literal[None, None] # Y061 None inside "Literal[]" expression. Replace with "None" + 62 |+None # Y061 None inside "Literal[]" expression. Replace with "None" +63 63 | Literal[1, None, "foo", None] # Y061 None inside "Literal[]" expression. Replace with "Literal[1, 'foo'] | None" +64 64 | +65 65 | # ... but if Y061 and Y062 both apply + +PYI061.py:63:12: PYI061 [*] `Literal[None, ...]` can be replaced with `Literal[...] | None` + | +61 | # only emit Y061... +62 | Literal[None, None] # Y061 None inside "Literal[]" expression. Replace with "None" +63 | Literal[1, None, "foo", None] # Y061 None inside "Literal[]" expression. Replace with "Literal[1, 'foo'] | None" + | ^^^^ PYI061 +64 | +65 | # ... but if Y061 and Y062 both apply + | + = help: Replace with `Literal[...] | None` + +ℹ Safe fix +60 60 | # If Y061 and Y062 both apply, but all the duplicate members are None, +61 61 | # only emit Y061... +62 62 | Literal[None, None] # Y061 None inside "Literal[]" expression. Replace with "None" +63 |-Literal[1, None, "foo", None] # Y061 None inside "Literal[]" expression. Replace with "Literal[1, 'foo'] | None" + 63 |+Literal[1, "foo"] | Literal[None] # Y061 None inside "Literal[]" expression. Replace with "Literal[1, 'foo'] | None" +64 64 | +65 65 | # ... but if Y061 and Y062 both apply +66 66 | # and there are no None members in the Literal[] slice, + +PYI061.py:63:25: PYI061 [*] `Literal[None, ...]` can be replaced with `Literal[...] | None` + | +61 | # only emit Y061... +62 | Literal[None, None] # Y061 None inside "Literal[]" expression. Replace with "None" +63 | Literal[1, None, "foo", None] # Y061 None inside "Literal[]" expression. Replace with "Literal[1, 'foo'] | None" + | ^^^^ PYI061 +64 | +65 | # ... but if Y061 and Y062 both apply + | + = help: Replace with `Literal[...] | None` + +ℹ Safe fix +60 60 | # If Y061 and Y062 both apply, but all the duplicate members are None, +61 61 | # only emit Y061... +62 62 | Literal[None, None] # Y061 None inside "Literal[]" expression. Replace with "None" +63 |-Literal[1, None, "foo", None] # Y061 None inside "Literal[]" expression. Replace with "Literal[1, 'foo'] | None" + 63 |+Literal[1, "foo"] | Literal[None] # Y061 None inside "Literal[]" expression. Replace with "Literal[1, 'foo'] | None" +64 64 | +65 65 | # ... but if Y061 and Y062 both apply +66 66 | # and there are no None members in the Literal[] slice, + +PYI061.py:68:9: PYI061 [*] `Literal[None, ...]` can be replaced with `Literal[...] | None` + | +66 | # and there are no None members in the Literal[] slice, +67 | # only emit Y062: +68 | Literal[None, True, None, True] # Y062 Duplicate "Literal[]" member "True" + | ^^^^ PYI061 + | + = help: Replace with `Literal[...] | None` + +ℹ Safe fix +65 65 | # ... but if Y061 and Y062 both apply +66 66 | # and there are no None members in the Literal[] slice, +67 67 | # only emit Y062: +68 |-Literal[None, True, None, True] # Y062 Duplicate "Literal[]" member "True" + 68 |+Literal[True, True] | Literal[None] # Y062 Duplicate "Literal[]" member "True" +69 69 | +70 70 | +71 71 | # Regression tests for https://github.com/astral-sh/ruff/issues/14567 + +PYI061.py:68:21: PYI061 [*] `Literal[None, ...]` can be replaced with `Literal[...] | None` + | +66 | # and there are no None members in the Literal[] slice, +67 | # only emit Y062: +68 | Literal[None, True, None, True] # Y062 Duplicate "Literal[]" member "True" + | ^^^^ PYI061 + | + = help: Replace with `Literal[...] | None` + +ℹ Safe fix +65 65 | # ... but if Y061 and Y062 both apply +66 66 | # and there are no None members in the Literal[] slice, +67 67 | # only emit Y062: +68 |-Literal[None, True, None, True] # Y062 Duplicate "Literal[]" member "True" + 68 |+Literal[True, True] | Literal[None] # Y062 Duplicate "Literal[]" member "True" +69 69 | +70 70 | +71 71 | # Regression tests for https://github.com/astral-sh/ruff/issues/14567 + +PYI061.py:72:12: PYI061 `Literal[None]` can be replaced with `None` + | +71 | # Regression tests for https://github.com/astral-sh/ruff/issues/14567 +72 | x: Literal[None] | None + | ^^^^ PYI061 +73 | y: None | Literal[None] +74 | z: Union[Literal[None], None] + | + = help: Replace with `None` + +PYI061.py:73:19: PYI061 `Literal[None]` can be replaced with `None` + | +71 | # Regression tests for https://github.com/astral-sh/ruff/issues/14567 +72 | x: Literal[None] | None +73 | y: None | Literal[None] + | ^^^^ PYI061 +74 | z: Union[Literal[None], None] + | + = help: Replace with `None` + +PYI061.py:74:18: PYI061 [*] `Literal[None]` can be replaced with `None` + | +72 | x: Literal[None] | None +73 | y: None | Literal[None] +74 | z: Union[Literal[None], None] + | ^^^^ PYI061 +75 | +76 | a: int | Literal[None] | None + | + = help: Replace with `None` + +ℹ Safe fix +71 71 | # Regression tests for https://github.com/astral-sh/ruff/issues/14567 +72 72 | x: Literal[None] | None +73 73 | y: None | Literal[None] +74 |-z: Union[Literal[None], None] + 74 |+z: Union[None, None] +75 75 | +76 76 | a: int | Literal[None] | None +77 77 | b: None | Literal[None] | None + +PYI061.py:76:18: PYI061 `Literal[None]` can be replaced with `None` + | +74 | z: Union[Literal[None], None] +75 | +76 | a: int | Literal[None] | None + | ^^^^ PYI061 +77 | b: None | Literal[None] | None +78 | c: (None | Literal[None]) | None + | + = help: Replace with `None` + +PYI061.py:77:19: PYI061 `Literal[None]` can be replaced with `None` + | +76 | a: int | Literal[None] | None +77 | b: None | Literal[None] | None + | ^^^^ PYI061 +78 | c: (None | Literal[None]) | None +79 | d: None | (Literal[None] | None) + | + = help: Replace with `None` + +PYI061.py:78:20: PYI061 `Literal[None]` can be replaced with `None` + | +76 | a: int | Literal[None] | None +77 | b: None | Literal[None] | None +78 | c: (None | Literal[None]) | None + | ^^^^ PYI061 +79 | d: None | (Literal[None] | None) +80 | e: None | ((None | Literal[None]) | None) | None + | + = help: Replace with `None` + +PYI061.py:79:20: PYI061 `Literal[None]` can be replaced with `None` + | +77 | b: None | Literal[None] | None +78 | c: (None | Literal[None]) | 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` + +PYI061.py:80:28: PYI061 `Literal[None]` can be replaced with `None` + | +78 | c: (None | Literal[None]) | 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 `Literal[None]` can be replaced with `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 `Literal[None]` can be replaced with `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.new b/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI061_PYI061.pyi.snap.new new file mode 100644 index 00000000000000..3cb11234975038 --- /dev/null +++ b/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI061_PYI061.pyi.snap.new @@ -0,0 +1,326 @@ +--- +source: crates/ruff_linter/src/rules/flake8_pyi/mod.rs +assertion_line: 135 +snapshot_kind: text +--- +PYI061.pyi:4:25: PYI061 [*] `Literal[None]` can be replaced with `None` + | +4 | def func1(arg1: Literal[None]): ... + | ^^^^ PYI061 + | + = help: Replace with `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): ... +5 5 | +6 6 | +7 7 | def func2(arg1: Literal[None] | int): ... + +PYI061.pyi:7:25: PYI061 [*] `Literal[None]` can be replaced with `None` + | +7 | def func2(arg1: Literal[None] | int): ... + | ^^^^ PYI061 + | + = help: Replace with `None` + +ℹ Safe fix +4 4 | def func1(arg1: Literal[None]): ... +5 5 | +6 6 | +7 |-def func2(arg1: Literal[None] | int): ... + 7 |+def func2(arg1: None | int): ... +8 8 | +9 9 | +10 10 | def func3() -> Literal[None]: ... + +PYI061.pyi:10:24: PYI061 [*] `Literal[None]` can be replaced with `None` + | +10 | def func3() -> Literal[None]: ... + | ^^^^ PYI061 + | + = help: Replace with `None` + +ℹ Safe fix +7 7 | def func2(arg1: Literal[None] | int): ... +8 8 | +9 9 | +10 |-def func3() -> Literal[None]: ... + 10 |+def func3() -> None: ... +11 11 | +12 12 | +13 13 | def func4(arg1: Literal[int, None, float]): ... + +PYI061.pyi:13:30: PYI061 [*] `Literal[None, ...]` can be replaced with `Literal[...] | None` + | +13 | def func4(arg1: Literal[int, None, float]): ... + | ^^^^ PYI061 + | + = help: Replace with `Literal[...] | None` + +ℹ Safe fix +10 10 | def func3() -> Literal[None]: ... +11 11 | +12 12 | +13 |-def func4(arg1: Literal[int, None, float]): ... + 13 |+def func4(arg1: Literal[int, float] | Literal[None]): ... +14 14 | +15 15 | +16 16 | def func5(arg1: Literal[None, None]): ... + +PYI061.pyi:16:25: PYI061 [*] `Literal[None]` can be replaced with `None` + | +16 | def func5(arg1: Literal[None, None]): ... + | ^^^^ PYI061 + | + = help: Replace with `None` + +ℹ Safe fix +13 13 | def func4(arg1: Literal[int, None, float]): ... +14 14 | +15 15 | +16 |-def func5(arg1: Literal[None, None]): ... + 16 |+def func5(arg1: None): ... +17 17 | +18 18 | +19 19 | def func6(arg1: Literal[ + +PYI061.pyi:16:31: PYI061 [*] `Literal[None]` can be replaced with `None` + | +16 | def func5(arg1: Literal[None, None]): ... + | ^^^^ PYI061 + | + = help: Replace with `None` + +ℹ Safe fix +13 13 | def func4(arg1: Literal[int, None, float]): ... +14 14 | +15 15 | +16 |-def func5(arg1: Literal[None, None]): ... + 16 |+def func5(arg1: None): ... +17 17 | +18 18 | +19 19 | def func6(arg1: Literal[ + +PYI061.pyi:21:5: PYI061 [*] `Literal[None, ...]` can be replaced with `Literal[...] | None` + | +19 | def func6(arg1: Literal[ +20 | "hello", +21 | None # Comment 1 + | ^^^^ PYI061 +22 | , "world" +23 | ]): ... + | + = help: Replace with `Literal[...] | None` + +ℹ Unsafe fix +16 16 | def func5(arg1: Literal[None, None]): ... +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"] | Literal[None]): ... +24 20 | +25 21 | +26 22 | def func7(arg1: Literal[ + +PYI061.pyi:27:5: PYI061 [*] `Literal[None]` can be replaced with `None` + | +26 | def func7(arg1: Literal[ +27 | None # Comment 1 + | ^^^^ PYI061 +28 | ]): ... + | + = help: Replace with `None` + +ℹ Unsafe fix +23 23 | ]): ... +24 24 | +25 25 | +26 |-def func7(arg1: Literal[ +27 |- None # Comment 1 +28 |-]): ... + 26 |+def func7(arg1: None): ... +29 27 | +30 28 | +31 29 | def func8(arg1: Literal[None] | None):... + +PYI061.pyi:31:25: PYI061 `Literal[None]` can be replaced with `None` + | +31 | def func8(arg1: Literal[None] | None):... + | ^^^^ PYI061 + | + = help: Replace with `None` + +PYI061.pyi:34:31: PYI061 [*] `Literal[None]` can be replaced with `None` + | +34 | def func9(arg1: Union[Literal[None], None]): ... + | ^^^^ PYI061 + | + = help: Replace with `None` + +ℹ Safe fix +31 31 | def func8(arg1: Literal[None] | None):... +32 32 | +33 33 | +34 |-def func9(arg1: Union[Literal[None], None]): ... + 34 |+def func9(arg1: Union[None, None]): ... +35 35 | +36 36 | +37 37 | # OK + +PYI061.pyi:42:9: PYI061 [*] `Literal[None]` can be replaced with `None` + | +41 | # From flake8-pyi +42 | 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" + | + = help: Replace with `None` + +ℹ Safe fix +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" +44 44 | +45 45 | + +PYI061.pyi:43:15: PYI061 [*] `Literal[None, ...]` can be replaced with `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" + | ^^^^ 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,] | Literal[None] # PYI061 None inside "Literal[]" expression. Replace with "Literal[True] | None" +44 44 | +45 45 | +46 46 | # Regression tests for https://github.com/astral-sh/ruff/issues/14567 + +PYI061.pyi:47:12: PYI061 `Literal[None]` can be replaced with `None` + | +46 | # Regression tests for https://github.com/astral-sh/ruff/issues/14567 +47 | x: Literal[None] | None + | ^^^^ PYI061 +48 | y: None | Literal[None] +49 | z: Union[Literal[None], None] + | + = help: Replace with `None` + +PYI061.pyi:48:19: PYI061 `Literal[None]` can be replaced with `None` + | +46 | # Regression tests for https://github.com/astral-sh/ruff/issues/14567 +47 | x: Literal[None] | None +48 | y: None | Literal[None] + | ^^^^ PYI061 +49 | z: Union[Literal[None], None] + | + = help: Replace with `None` + +PYI061.pyi:49:18: PYI061 [*] `Literal[None]` can be replaced with `None` + | +47 | x: Literal[None] | None +48 | y: None | Literal[None] +49 | z: Union[Literal[None], None] + | ^^^^ PYI061 +50 | +51 | 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 `Literal[None]` can be replaced with `None` + | +49 | z: Union[Literal[None], None] +50 | +51 | a: int | Literal[None] | None + | ^^^^ PYI061 +52 | b: None | Literal[None] | None +53 | c: (None | Literal[None]) | None + | + = help: Replace with `None` + +PYI061.pyi:52:19: PYI061 `Literal[None]` can be replaced with `None` + | +51 | a: int | Literal[None] | None +52 | b: None | Literal[None] | None + | ^^^^ PYI061 +53 | c: (None | Literal[None]) | None +54 | d: None | (Literal[None] | None) + | + = help: Replace with `None` + +PYI061.pyi:53:20: PYI061 `Literal[None]` can be replaced with `None` + | +51 | a: int | Literal[None] | None +52 | b: None | Literal[None] | None +53 | c: (None | Literal[None]) | None + | ^^^^ PYI061 +54 | d: None | (Literal[None] | None) +55 | e: None | ((None | Literal[None]) | None) | None + | + = help: Replace with `None` + +PYI061.pyi:54:20: PYI061 `Literal[None]` can be replaced with `None` + | +52 | b: None | Literal[None] | None +53 | c: (None | Literal[None]) | 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` + +PYI061.pyi:55:28: PYI061 `Literal[None]` can be replaced with `None` + | +53 | c: (None | Literal[None]) | 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 `Literal[None]` can be replaced with `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 `Literal[None]` can be replaced with `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 86000023acb08e4f5e5d7ea53e0ccc4eb7ce0eec Mon Sep 17 00:00:00 2001 From: Chandra Kiran G Date: Wed, 11 Dec 2024 16:15:06 +0530 Subject: [PATCH 5/8] chore: accept new snapshots --- ...__flake8_pyi__tests__PYI061_PYI061.py.snap | 89 +++- ...ake8_pyi__tests__PYI061_PYI061.py.snap.new | 457 ------------------ ..._flake8_pyi__tests__PYI061_PYI061.pyi.snap | 41 +- ...ke8_pyi__tests__PYI061_PYI061.pyi.snap.new | 326 ------------- 4 files changed, 120 insertions(+), 793 deletions(-) delete mode 100644 crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI061_PYI061.py.snap.new delete mode 100644 crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI061_PYI061.pyi.snap.new 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 380b81d37b0605..8a79e03b845bd9 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 @@ -1,5 +1,6 @@ --- source: crates/ruff_linter/src/rules/flake8_pyi/mod.rs +snapshot_kind: text --- PYI061.py:4:25: PYI061 [*] `Literal[None]` can be replaced with `None` | @@ -55,7 +56,7 @@ PYI061.py:12:24: PYI061 [*] `Literal[None]` can be replaced with `None` 14 14 | 15 15 | -PYI061.py:16:30: PYI061 `Literal[None, ...]` can be replaced with `Literal[...] | None` +PYI061.py:16:30: PYI061 [*] `Literal[None, ...]` can be replaced with `Literal[...] | None` | 16 | def func4(arg1: Literal[int, None, float]): | ^^^^ PYI061 @@ -63,6 +64,16 @@ PYI061.py:16:30: PYI061 `Literal[None, ...]` can be replaced with `Literal[...] | = help: Replace with `Literal[...] | None` +ℹ Safe fix +13 13 | ... +14 14 | +15 15 | +16 |-def func4(arg1: Literal[int, None, float]): + 16 |+def func4(arg1: Literal[int, float] | Literal[None]): +17 17 | ... +18 18 | +19 19 | + PYI061.py:20:25: PYI061 [*] `Literal[None]` can be replaced with `None` | 20 | def func5(arg1: Literal[None, None]): @@ -99,7 +110,7 @@ PYI061.py:20:31: PYI061 [*] `Literal[None]` can be replaced with `None` 22 22 | 23 23 | -PYI061.py:26:5: PYI061 `Literal[None, ...]` can be replaced with `Literal[...] | None` +PYI061.py:26:5: PYI061 [*] `Literal[None, ...]` can be replaced with `Literal[...] | None` | 24 | def func6(arg1: Literal[ 25 | "hello", @@ -110,6 +121,20 @@ PYI061.py:26:5: PYI061 `Literal[None, ...]` can be replaced with `Literal[...] | | = help: Replace with `Literal[...] | None` +ℹ Unsafe fix +21 21 | ... +22 22 | +23 23 | +24 |-def func6(arg1: Literal[ +25 |- "hello", +26 |- None # Comment 1 +27 |- , "world" +28 |- ]): + 24 |+def func6(arg1: Literal["hello", "world"] | Literal[None]): +29 25 | ... +30 26 | +31 27 | + PYI061.py:33:5: PYI061 [*] `Literal[None]` can be replaced with `None` | 32 | def func7(arg1: Literal[ @@ -177,7 +202,7 @@ PYI061.py:52:9: PYI061 [*] `Literal[None]` can be replaced with `None` 54 54 | 55 55 | ### -PYI061.py:53:15: PYI061 `Literal[None, ...]` can be replaced with `Literal[...] | None` +PYI061.py:53:15: PYI061 [*] `Literal[None, ...]` can be replaced with `Literal[...] | None` | 51 | # From flake8-pyi 52 | Literal[None] # Y061 None inside "Literal[]" expression. Replace with "None" @@ -188,6 +213,16 @@ PYI061.py:53:15: PYI061 `Literal[None, ...]` can be replaced with `Literal[...] | = help: Replace with `Literal[...] | None` +ℹ Safe fix +50 50 | +51 51 | # From flake8-pyi +52 52 | Literal[None] # Y061 None inside "Literal[]" expression. Replace with "None" +53 |-Literal[True, None] # Y061 None inside "Literal[]" expression. Replace with "Literal[True] | None" + 53 |+Literal[True,] | Literal[None] # Y061 None inside "Literal[]" expression. Replace with "Literal[True] | None" +54 54 | +55 55 | ### +56 56 | # The following rules here are slightly subtle, + PYI061.py:62:9: PYI061 [*] `Literal[None]` can be replaced with `None` | 60 | # If Y061 and Y062 both apply, but all the duplicate members are None, @@ -228,7 +263,7 @@ PYI061.py:62:15: PYI061 [*] `Literal[None]` can be replaced with `None` 64 64 | 65 65 | # ... but if Y061 and Y062 both apply -PYI061.py:63:12: PYI061 `Literal[None, ...]` can be replaced with `Literal[...] | None` +PYI061.py:63:12: PYI061 [*] `Literal[None, ...]` can be replaced with `Literal[...] | None` | 61 | # only emit Y061... 62 | Literal[None, None] # Y061 None inside "Literal[]" expression. Replace with "None" @@ -239,7 +274,17 @@ PYI061.py:63:12: PYI061 `Literal[None, ...]` can be replaced with `Literal[...] | = help: Replace with `Literal[...] | None` -PYI061.py:63:25: PYI061 `Literal[None, ...]` can be replaced with `Literal[...] | None` +ℹ Safe fix +60 60 | # If Y061 and Y062 both apply, but all the duplicate members are None, +61 61 | # only emit Y061... +62 62 | Literal[None, None] # Y061 None inside "Literal[]" expression. Replace with "None" +63 |-Literal[1, None, "foo", None] # Y061 None inside "Literal[]" expression. Replace with "Literal[1, 'foo'] | None" + 63 |+Literal[1, "foo"] | Literal[None] # Y061 None inside "Literal[]" expression. Replace with "Literal[1, 'foo'] | None" +64 64 | +65 65 | # ... but if Y061 and Y062 both apply +66 66 | # and there are no None members in the Literal[] slice, + +PYI061.py:63:25: PYI061 [*] `Literal[None, ...]` can be replaced with `Literal[...] | None` | 61 | # only emit Y061... 62 | Literal[None, None] # Y061 None inside "Literal[]" expression. Replace with "None" @@ -250,7 +295,17 @@ PYI061.py:63:25: PYI061 `Literal[None, ...]` can be replaced with `Literal[...] | = help: Replace with `Literal[...] | None` -PYI061.py:68:9: PYI061 `Literal[None, ...]` can be replaced with `Literal[...] | None` +ℹ Safe fix +60 60 | # If Y061 and Y062 both apply, but all the duplicate members are None, +61 61 | # only emit Y061... +62 62 | Literal[None, None] # Y061 None inside "Literal[]" expression. Replace with "None" +63 |-Literal[1, None, "foo", None] # Y061 None inside "Literal[]" expression. Replace with "Literal[1, 'foo'] | None" + 63 |+Literal[1, "foo"] | Literal[None] # Y061 None inside "Literal[]" expression. Replace with "Literal[1, 'foo'] | None" +64 64 | +65 65 | # ... but if Y061 and Y062 both apply +66 66 | # and there are no None members in the Literal[] slice, + +PYI061.py:68:9: PYI061 [*] `Literal[None, ...]` can be replaced with `Literal[...] | None` | 66 | # and there are no None members in the Literal[] slice, 67 | # only emit Y062: @@ -259,7 +314,17 @@ PYI061.py:68:9: PYI061 `Literal[None, ...]` can be replaced with `Literal[...] | | = help: Replace with `Literal[...] | None` -PYI061.py:68:21: PYI061 `Literal[None, ...]` can be replaced with `Literal[...] | None` +ℹ Safe fix +65 65 | # ... but if Y061 and Y062 both apply +66 66 | # and there are no None members in the Literal[] slice, +67 67 | # only emit Y062: +68 |-Literal[None, True, None, True] # Y062 Duplicate "Literal[]" member "True" + 68 |+Literal[True, True] | Literal[None] # Y062 Duplicate "Literal[]" member "True" +69 69 | +70 70 | +71 71 | # Regression tests for https://github.com/astral-sh/ruff/issues/14567 + +PYI061.py:68:21: PYI061 [*] `Literal[None, ...]` can be replaced with `Literal[...] | None` | 66 | # and there are no None members in the Literal[] slice, 67 | # only emit Y062: @@ -268,6 +333,16 @@ PYI061.py:68:21: PYI061 `Literal[None, ...]` can be replaced with `Literal[...] | = help: Replace with `Literal[...] | None` +ℹ Safe fix +65 65 | # ... but if Y061 and Y062 both apply +66 66 | # and there are no None members in the Literal[] slice, +67 67 | # only emit Y062: +68 |-Literal[None, True, None, True] # Y062 Duplicate "Literal[]" member "True" + 68 |+Literal[True, True] | Literal[None] # Y062 Duplicate "Literal[]" member "True" +69 69 | +70 70 | +71 71 | # Regression tests for https://github.com/astral-sh/ruff/issues/14567 + PYI061.py:72:12: PYI061 `Literal[None]` can be replaced with `None` | 71 | # Regression tests for https://github.com/astral-sh/ruff/issues/14567 diff --git a/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI061_PYI061.py.snap.new b/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI061_PYI061.py.snap.new deleted file mode 100644 index 606be0f7dc0af0..00000000000000 --- a/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI061_PYI061.py.snap.new +++ /dev/null @@ -1,457 +0,0 @@ ---- -source: crates/ruff_linter/src/rules/flake8_pyi/mod.rs -assertion_line: 135 -snapshot_kind: text ---- -PYI061.py:4:25: PYI061 [*] `Literal[None]` can be replaced with `None` - | -4 | def func1(arg1: Literal[None]): - | ^^^^ PYI061 -5 | ... - | - = help: Replace with `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): -5 5 | ... -6 6 | -7 7 | - -PYI061.py:8:25: PYI061 [*] `Literal[None]` can be replaced with `None` - | -8 | def func2(arg1: Literal[None] | int): - | ^^^^ PYI061 -9 | ... - | - = help: Replace with `None` - -ℹ Safe fix -5 5 | ... -6 6 | -7 7 | -8 |-def func2(arg1: Literal[None] | int): - 8 |+def func2(arg1: None | int): -9 9 | ... -10 10 | -11 11 | - -PYI061.py:12:24: PYI061 [*] `Literal[None]` can be replaced with `None` - | -12 | def func3() -> Literal[None]: - | ^^^^ PYI061 -13 | ... - | - = help: Replace with `None` - -ℹ Safe fix -9 9 | ... -10 10 | -11 11 | -12 |-def func3() -> Literal[None]: - 12 |+def func3() -> None: -13 13 | ... -14 14 | -15 15 | - -PYI061.py:16:30: PYI061 [*] `Literal[None, ...]` can be replaced with `Literal[...] | None` - | -16 | def func4(arg1: Literal[int, None, float]): - | ^^^^ PYI061 -17 | ... - | - = help: Replace with `Literal[...] | None` - -ℹ Safe fix -13 13 | ... -14 14 | -15 15 | -16 |-def func4(arg1: Literal[int, None, float]): - 16 |+def func4(arg1: Literal[int, float] | Literal[None]): -17 17 | ... -18 18 | -19 19 | - -PYI061.py:20:25: PYI061 [*] `Literal[None]` can be replaced with `None` - | -20 | def func5(arg1: Literal[None, None]): - | ^^^^ PYI061 -21 | ... - | - = help: Replace with `None` - -ℹ Safe fix -17 17 | ... -18 18 | -19 19 | -20 |-def func5(arg1: Literal[None, None]): - 20 |+def func5(arg1: None): -21 21 | ... -22 22 | -23 23 | - -PYI061.py:20:31: PYI061 [*] `Literal[None]` can be replaced with `None` - | -20 | def func5(arg1: Literal[None, None]): - | ^^^^ PYI061 -21 | ... - | - = help: Replace with `None` - -ℹ Safe fix -17 17 | ... -18 18 | -19 19 | -20 |-def func5(arg1: Literal[None, None]): - 20 |+def func5(arg1: None): -21 21 | ... -22 22 | -23 23 | - -PYI061.py:26:5: PYI061 [*] `Literal[None, ...]` can be replaced with `Literal[...] | None` - | -24 | def func6(arg1: Literal[ -25 | "hello", -26 | None # Comment 1 - | ^^^^ PYI061 -27 | , "world" -28 | ]): - | - = help: Replace with `Literal[...] | None` - -ℹ Unsafe fix -21 21 | ... -22 22 | -23 23 | -24 |-def func6(arg1: Literal[ -25 |- "hello", -26 |- None # Comment 1 -27 |- , "world" -28 |- ]): - 24 |+def func6(arg1: Literal["hello", "world"] | Literal[None]): -29 25 | ... -30 26 | -31 27 | - -PYI061.py:33:5: PYI061 [*] `Literal[None]` can be replaced with `None` - | -32 | def func7(arg1: Literal[ -33 | None # Comment 1 - | ^^^^ PYI061 -34 | ]): -35 | ... - | - = help: Replace with `None` - -ℹ Unsafe fix -29 29 | ... -30 30 | -31 31 | -32 |-def func7(arg1: Literal[ -33 |- None # Comment 1 -34 |- ]): - 32 |+def func7(arg1: None): -35 33 | ... -36 34 | -37 35 | - -PYI061.py:38:25: PYI061 `Literal[None]` can be replaced with `None` - | -38 | def func8(arg1: Literal[None] | None): - | ^^^^ PYI061 -39 | ... - | - = help: Replace with `None` - -PYI061.py:42:31: PYI061 [*] `Literal[None]` can be replaced with `None` - | -42 | def func9(arg1: Union[Literal[None], None]): - | ^^^^ PYI061 -43 | ... - | - = help: Replace with `None` - -ℹ Safe fix -39 39 | ... -40 40 | -41 41 | -42 |-def func9(arg1: Union[Literal[None], None]): - 42 |+def func9(arg1: Union[None, None]): -43 43 | ... -44 44 | -45 45 | - -PYI061.py:52:9: PYI061 [*] `Literal[None]` can be replaced with `None` - | -51 | # From flake8-pyi -52 | Literal[None] # Y061 None inside "Literal[]" expression. Replace with "None" - | ^^^^ PYI061 -53 | Literal[True, None] # Y061 None inside "Literal[]" expression. Replace with "Literal[True] | None" - | - = help: Replace with `None` - -ℹ Safe fix -49 49 | -50 50 | -51 51 | # From flake8-pyi -52 |-Literal[None] # Y061 None inside "Literal[]" expression. Replace with "None" - 52 |+None # Y061 None inside "Literal[]" expression. Replace with "None" -53 53 | Literal[True, None] # Y061 None inside "Literal[]" expression. Replace with "Literal[True] | None" -54 54 | -55 55 | ### - -PYI061.py:53:15: PYI061 [*] `Literal[None, ...]` can be replaced with `Literal[...] | None` - | -51 | # From flake8-pyi -52 | Literal[None] # Y061 None inside "Literal[]" expression. Replace with "None" -53 | Literal[True, None] # Y061 None inside "Literal[]" expression. Replace with "Literal[True] | None" - | ^^^^ PYI061 -54 | -55 | ### - | - = help: Replace with `Literal[...] | None` - -ℹ Safe fix -50 50 | -51 51 | # From flake8-pyi -52 52 | Literal[None] # Y061 None inside "Literal[]" expression. Replace with "None" -53 |-Literal[True, None] # Y061 None inside "Literal[]" expression. Replace with "Literal[True] | None" - 53 |+Literal[True,] | Literal[None] # Y061 None inside "Literal[]" expression. Replace with "Literal[True] | None" -54 54 | -55 55 | ### -56 56 | # The following rules here are slightly subtle, - -PYI061.py:62:9: PYI061 [*] `Literal[None]` can be replaced with `None` - | -60 | # If Y061 and Y062 both apply, but all the duplicate members are None, -61 | # only emit Y061... -62 | Literal[None, None] # Y061 None inside "Literal[]" expression. Replace with "None" - | ^^^^ PYI061 -63 | Literal[1, None, "foo", None] # Y061 None inside "Literal[]" expression. Replace with "Literal[1, 'foo'] | None" - | - = help: Replace with `None` - -ℹ Safe fix -59 59 | -60 60 | # If Y061 and Y062 both apply, but all the duplicate members are None, -61 61 | # only emit Y061... -62 |-Literal[None, None] # Y061 None inside "Literal[]" expression. Replace with "None" - 62 |+None # Y061 None inside "Literal[]" expression. Replace with "None" -63 63 | Literal[1, None, "foo", None] # Y061 None inside "Literal[]" expression. Replace with "Literal[1, 'foo'] | None" -64 64 | -65 65 | # ... but if Y061 and Y062 both apply - -PYI061.py:62:15: PYI061 [*] `Literal[None]` can be replaced with `None` - | -60 | # If Y061 and Y062 both apply, but all the duplicate members are None, -61 | # only emit Y061... -62 | Literal[None, None] # Y061 None inside "Literal[]" expression. Replace with "None" - | ^^^^ PYI061 -63 | Literal[1, None, "foo", None] # Y061 None inside "Literal[]" expression. Replace with "Literal[1, 'foo'] | None" - | - = help: Replace with `None` - -ℹ Safe fix -59 59 | -60 60 | # If Y061 and Y062 both apply, but all the duplicate members are None, -61 61 | # only emit Y061... -62 |-Literal[None, None] # Y061 None inside "Literal[]" expression. Replace with "None" - 62 |+None # Y061 None inside "Literal[]" expression. Replace with "None" -63 63 | Literal[1, None, "foo", None] # Y061 None inside "Literal[]" expression. Replace with "Literal[1, 'foo'] | None" -64 64 | -65 65 | # ... but if Y061 and Y062 both apply - -PYI061.py:63:12: PYI061 [*] `Literal[None, ...]` can be replaced with `Literal[...] | None` - | -61 | # only emit Y061... -62 | Literal[None, None] # Y061 None inside "Literal[]" expression. Replace with "None" -63 | Literal[1, None, "foo", None] # Y061 None inside "Literal[]" expression. Replace with "Literal[1, 'foo'] | None" - | ^^^^ PYI061 -64 | -65 | # ... but if Y061 and Y062 both apply - | - = help: Replace with `Literal[...] | None` - -ℹ Safe fix -60 60 | # If Y061 and Y062 both apply, but all the duplicate members are None, -61 61 | # only emit Y061... -62 62 | Literal[None, None] # Y061 None inside "Literal[]" expression. Replace with "None" -63 |-Literal[1, None, "foo", None] # Y061 None inside "Literal[]" expression. Replace with "Literal[1, 'foo'] | None" - 63 |+Literal[1, "foo"] | Literal[None] # Y061 None inside "Literal[]" expression. Replace with "Literal[1, 'foo'] | None" -64 64 | -65 65 | # ... but if Y061 and Y062 both apply -66 66 | # and there are no None members in the Literal[] slice, - -PYI061.py:63:25: PYI061 [*] `Literal[None, ...]` can be replaced with `Literal[...] | None` - | -61 | # only emit Y061... -62 | Literal[None, None] # Y061 None inside "Literal[]" expression. Replace with "None" -63 | Literal[1, None, "foo", None] # Y061 None inside "Literal[]" expression. Replace with "Literal[1, 'foo'] | None" - | ^^^^ PYI061 -64 | -65 | # ... but if Y061 and Y062 both apply - | - = help: Replace with `Literal[...] | None` - -ℹ Safe fix -60 60 | # If Y061 and Y062 both apply, but all the duplicate members are None, -61 61 | # only emit Y061... -62 62 | Literal[None, None] # Y061 None inside "Literal[]" expression. Replace with "None" -63 |-Literal[1, None, "foo", None] # Y061 None inside "Literal[]" expression. Replace with "Literal[1, 'foo'] | None" - 63 |+Literal[1, "foo"] | Literal[None] # Y061 None inside "Literal[]" expression. Replace with "Literal[1, 'foo'] | None" -64 64 | -65 65 | # ... but if Y061 and Y062 both apply -66 66 | # and there are no None members in the Literal[] slice, - -PYI061.py:68:9: PYI061 [*] `Literal[None, ...]` can be replaced with `Literal[...] | None` - | -66 | # and there are no None members in the Literal[] slice, -67 | # only emit Y062: -68 | Literal[None, True, None, True] # Y062 Duplicate "Literal[]" member "True" - | ^^^^ PYI061 - | - = help: Replace with `Literal[...] | None` - -ℹ Safe fix -65 65 | # ... but if Y061 and Y062 both apply -66 66 | # and there are no None members in the Literal[] slice, -67 67 | # only emit Y062: -68 |-Literal[None, True, None, True] # Y062 Duplicate "Literal[]" member "True" - 68 |+Literal[True, True] | Literal[None] # Y062 Duplicate "Literal[]" member "True" -69 69 | -70 70 | -71 71 | # Regression tests for https://github.com/astral-sh/ruff/issues/14567 - -PYI061.py:68:21: PYI061 [*] `Literal[None, ...]` can be replaced with `Literal[...] | None` - | -66 | # and there are no None members in the Literal[] slice, -67 | # only emit Y062: -68 | Literal[None, True, None, True] # Y062 Duplicate "Literal[]" member "True" - | ^^^^ PYI061 - | - = help: Replace with `Literal[...] | None` - -ℹ Safe fix -65 65 | # ... but if Y061 and Y062 both apply -66 66 | # and there are no None members in the Literal[] slice, -67 67 | # only emit Y062: -68 |-Literal[None, True, None, True] # Y062 Duplicate "Literal[]" member "True" - 68 |+Literal[True, True] | Literal[None] # Y062 Duplicate "Literal[]" member "True" -69 69 | -70 70 | -71 71 | # Regression tests for https://github.com/astral-sh/ruff/issues/14567 - -PYI061.py:72:12: PYI061 `Literal[None]` can be replaced with `None` - | -71 | # Regression tests for https://github.com/astral-sh/ruff/issues/14567 -72 | x: Literal[None] | None - | ^^^^ PYI061 -73 | y: None | Literal[None] -74 | z: Union[Literal[None], None] - | - = help: Replace with `None` - -PYI061.py:73:19: PYI061 `Literal[None]` can be replaced with `None` - | -71 | # Regression tests for https://github.com/astral-sh/ruff/issues/14567 -72 | x: Literal[None] | None -73 | y: None | Literal[None] - | ^^^^ PYI061 -74 | z: Union[Literal[None], None] - | - = help: Replace with `None` - -PYI061.py:74:18: PYI061 [*] `Literal[None]` can be replaced with `None` - | -72 | x: Literal[None] | None -73 | y: None | Literal[None] -74 | z: Union[Literal[None], None] - | ^^^^ PYI061 -75 | -76 | a: int | Literal[None] | None - | - = help: Replace with `None` - -ℹ Safe fix -71 71 | # Regression tests for https://github.com/astral-sh/ruff/issues/14567 -72 72 | x: Literal[None] | None -73 73 | y: None | Literal[None] -74 |-z: Union[Literal[None], None] - 74 |+z: Union[None, None] -75 75 | -76 76 | a: int | Literal[None] | None -77 77 | b: None | Literal[None] | None - -PYI061.py:76:18: PYI061 `Literal[None]` can be replaced with `None` - | -74 | z: Union[Literal[None], None] -75 | -76 | a: int | Literal[None] | None - | ^^^^ PYI061 -77 | b: None | Literal[None] | None -78 | c: (None | Literal[None]) | None - | - = help: Replace with `None` - -PYI061.py:77:19: PYI061 `Literal[None]` can be replaced with `None` - | -76 | a: int | Literal[None] | None -77 | b: None | Literal[None] | None - | ^^^^ PYI061 -78 | c: (None | Literal[None]) | None -79 | d: None | (Literal[None] | None) - | - = help: Replace with `None` - -PYI061.py:78:20: PYI061 `Literal[None]` can be replaced with `None` - | -76 | a: int | Literal[None] | None -77 | b: None | Literal[None] | None -78 | c: (None | Literal[None]) | None - | ^^^^ PYI061 -79 | d: None | (Literal[None] | None) -80 | e: None | ((None | Literal[None]) | None) | None - | - = help: Replace with `None` - -PYI061.py:79:20: PYI061 `Literal[None]` can be replaced with `None` - | -77 | b: None | Literal[None] | None -78 | c: (None | Literal[None]) | 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` - -PYI061.py:80:28: PYI061 `Literal[None]` can be replaced with `None` - | -78 | c: (None | Literal[None]) | 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 `Literal[None]` can be replaced with `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 `Literal[None]` can be replaced with `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 d1b695f73dbe1d..75dcf75e58a7cd 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,5 +1,6 @@ --- source: crates/ruff_linter/src/rules/flake8_pyi/mod.rs +snapshot_kind: text --- PYI061.pyi:4:25: PYI061 [*] `Literal[None]` can be replaced with `None` | @@ -52,13 +53,23 @@ PYI061.pyi:10:24: PYI061 [*] `Literal[None]` can be replaced with `None` 12 12 | 13 13 | def func4(arg1: Literal[int, None, float]): ... -PYI061.pyi:13:30: PYI061 `Literal[None, ...]` can be replaced with `Literal[...] | None` +PYI061.pyi:13:30: PYI061 [*] `Literal[None, ...]` can be replaced with `Literal[...] | None` | 13 | def func4(arg1: Literal[int, None, float]): ... | ^^^^ PYI061 | = help: Replace with `Literal[...] | None` +ℹ Safe fix +10 10 | def func3() -> Literal[None]: ... +11 11 | +12 12 | +13 |-def func4(arg1: Literal[int, None, float]): ... + 13 |+def func4(arg1: Literal[int, float] | Literal[None]): ... +14 14 | +15 15 | +16 16 | def func5(arg1: Literal[None, None]): ... + PYI061.pyi:16:25: PYI061 [*] `Literal[None]` can be replaced with `None` | 16 | def func5(arg1: Literal[None, None]): ... @@ -93,7 +104,7 @@ PYI061.pyi:16:31: PYI061 [*] `Literal[None]` can be replaced with `None` 18 18 | 19 19 | def func6(arg1: Literal[ -PYI061.pyi:21:5: PYI061 `Literal[None, ...]` can be replaced with `Literal[...] | None` +PYI061.pyi:21:5: PYI061 [*] `Literal[None, ...]` can be replaced with `Literal[...] | None` | 19 | def func6(arg1: Literal[ 20 | "hello", @@ -104,6 +115,20 @@ PYI061.pyi:21:5: PYI061 `Literal[None, ...]` can be replaced with `Literal[...] | = help: Replace with `Literal[...] | None` +ℹ Unsafe fix +16 16 | def func5(arg1: Literal[None, None]): ... +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"] | Literal[None]): ... +24 20 | +25 21 | +26 22 | def func7(arg1: Literal[ + PYI061.pyi:27:5: PYI061 [*] `Literal[None]` can be replaced with `None` | 26 | def func7(arg1: Literal[ @@ -168,7 +193,7 @@ PYI061.pyi:42:9: PYI061 [*] `Literal[None]` can be replaced with `None` 44 44 | 45 45 | -PYI061.pyi:43:15: PYI061 `Literal[None, ...]` can be replaced with `Literal[...] | None` +PYI061.pyi:43:15: PYI061 [*] `Literal[None, ...]` can be replaced with `Literal[...] | None` | 41 | # From flake8-pyi 42 | Literal[None] # PYI061 None inside "Literal[]" expression. Replace with "None" @@ -177,6 +202,16 @@ PYI061.pyi:43:15: PYI061 `Literal[None, ...]` can be replaced with `Literal[...] | = 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,] | Literal[None] # PYI061 None inside "Literal[]" expression. Replace with "Literal[True] | None" +44 44 | +45 45 | +46 46 | # Regression tests for https://github.com/astral-sh/ruff/issues/14567 + PYI061.pyi:47:12: PYI061 `Literal[None]` can be replaced with `None` | 46 | # Regression tests for https://github.com/astral-sh/ruff/issues/14567 diff --git a/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI061_PYI061.pyi.snap.new b/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI061_PYI061.pyi.snap.new deleted file mode 100644 index 3cb11234975038..00000000000000 --- a/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI061_PYI061.pyi.snap.new +++ /dev/null @@ -1,326 +0,0 @@ ---- -source: crates/ruff_linter/src/rules/flake8_pyi/mod.rs -assertion_line: 135 -snapshot_kind: text ---- -PYI061.pyi:4:25: PYI061 [*] `Literal[None]` can be replaced with `None` - | -4 | def func1(arg1: Literal[None]): ... - | ^^^^ PYI061 - | - = help: Replace with `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): ... -5 5 | -6 6 | -7 7 | def func2(arg1: Literal[None] | int): ... - -PYI061.pyi:7:25: PYI061 [*] `Literal[None]` can be replaced with `None` - | -7 | def func2(arg1: Literal[None] | int): ... - | ^^^^ PYI061 - | - = help: Replace with `None` - -ℹ Safe fix -4 4 | def func1(arg1: Literal[None]): ... -5 5 | -6 6 | -7 |-def func2(arg1: Literal[None] | int): ... - 7 |+def func2(arg1: None | int): ... -8 8 | -9 9 | -10 10 | def func3() -> Literal[None]: ... - -PYI061.pyi:10:24: PYI061 [*] `Literal[None]` can be replaced with `None` - | -10 | def func3() -> Literal[None]: ... - | ^^^^ PYI061 - | - = help: Replace with `None` - -ℹ Safe fix -7 7 | def func2(arg1: Literal[None] | int): ... -8 8 | -9 9 | -10 |-def func3() -> Literal[None]: ... - 10 |+def func3() -> None: ... -11 11 | -12 12 | -13 13 | def func4(arg1: Literal[int, None, float]): ... - -PYI061.pyi:13:30: PYI061 [*] `Literal[None, ...]` can be replaced with `Literal[...] | None` - | -13 | def func4(arg1: Literal[int, None, float]): ... - | ^^^^ PYI061 - | - = help: Replace with `Literal[...] | None` - -ℹ Safe fix -10 10 | def func3() -> Literal[None]: ... -11 11 | -12 12 | -13 |-def func4(arg1: Literal[int, None, float]): ... - 13 |+def func4(arg1: Literal[int, float] | Literal[None]): ... -14 14 | -15 15 | -16 16 | def func5(arg1: Literal[None, None]): ... - -PYI061.pyi:16:25: PYI061 [*] `Literal[None]` can be replaced with `None` - | -16 | def func5(arg1: Literal[None, None]): ... - | ^^^^ PYI061 - | - = help: Replace with `None` - -ℹ Safe fix -13 13 | def func4(arg1: Literal[int, None, float]): ... -14 14 | -15 15 | -16 |-def func5(arg1: Literal[None, None]): ... - 16 |+def func5(arg1: None): ... -17 17 | -18 18 | -19 19 | def func6(arg1: Literal[ - -PYI061.pyi:16:31: PYI061 [*] `Literal[None]` can be replaced with `None` - | -16 | def func5(arg1: Literal[None, None]): ... - | ^^^^ PYI061 - | - = help: Replace with `None` - -ℹ Safe fix -13 13 | def func4(arg1: Literal[int, None, float]): ... -14 14 | -15 15 | -16 |-def func5(arg1: Literal[None, None]): ... - 16 |+def func5(arg1: None): ... -17 17 | -18 18 | -19 19 | def func6(arg1: Literal[ - -PYI061.pyi:21:5: PYI061 [*] `Literal[None, ...]` can be replaced with `Literal[...] | None` - | -19 | def func6(arg1: Literal[ -20 | "hello", -21 | None # Comment 1 - | ^^^^ PYI061 -22 | , "world" -23 | ]): ... - | - = help: Replace with `Literal[...] | None` - -ℹ Unsafe fix -16 16 | def func5(arg1: Literal[None, None]): ... -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"] | Literal[None]): ... -24 20 | -25 21 | -26 22 | def func7(arg1: Literal[ - -PYI061.pyi:27:5: PYI061 [*] `Literal[None]` can be replaced with `None` - | -26 | def func7(arg1: Literal[ -27 | None # Comment 1 - | ^^^^ PYI061 -28 | ]): ... - | - = help: Replace with `None` - -ℹ Unsafe fix -23 23 | ]): ... -24 24 | -25 25 | -26 |-def func7(arg1: Literal[ -27 |- None # Comment 1 -28 |-]): ... - 26 |+def func7(arg1: None): ... -29 27 | -30 28 | -31 29 | def func8(arg1: Literal[None] | None):... - -PYI061.pyi:31:25: PYI061 `Literal[None]` can be replaced with `None` - | -31 | def func8(arg1: Literal[None] | None):... - | ^^^^ PYI061 - | - = help: Replace with `None` - -PYI061.pyi:34:31: PYI061 [*] `Literal[None]` can be replaced with `None` - | -34 | def func9(arg1: Union[Literal[None], None]): ... - | ^^^^ PYI061 - | - = help: Replace with `None` - -ℹ Safe fix -31 31 | def func8(arg1: Literal[None] | None):... -32 32 | -33 33 | -34 |-def func9(arg1: Union[Literal[None], None]): ... - 34 |+def func9(arg1: Union[None, None]): ... -35 35 | -36 36 | -37 37 | # OK - -PYI061.pyi:42:9: PYI061 [*] `Literal[None]` can be replaced with `None` - | -41 | # From flake8-pyi -42 | 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" - | - = help: Replace with `None` - -ℹ Safe fix -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" -44 44 | -45 45 | - -PYI061.pyi:43:15: PYI061 [*] `Literal[None, ...]` can be replaced with `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" - | ^^^^ 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,] | Literal[None] # PYI061 None inside "Literal[]" expression. Replace with "Literal[True] | None" -44 44 | -45 45 | -46 46 | # Regression tests for https://github.com/astral-sh/ruff/issues/14567 - -PYI061.pyi:47:12: PYI061 `Literal[None]` can be replaced with `None` - | -46 | # Regression tests for https://github.com/astral-sh/ruff/issues/14567 -47 | x: Literal[None] | None - | ^^^^ PYI061 -48 | y: None | Literal[None] -49 | z: Union[Literal[None], None] - | - = help: Replace with `None` - -PYI061.pyi:48:19: PYI061 `Literal[None]` can be replaced with `None` - | -46 | # Regression tests for https://github.com/astral-sh/ruff/issues/14567 -47 | x: Literal[None] | None -48 | y: None | Literal[None] - | ^^^^ PYI061 -49 | z: Union[Literal[None], None] - | - = help: Replace with `None` - -PYI061.pyi:49:18: PYI061 [*] `Literal[None]` can be replaced with `None` - | -47 | x: Literal[None] | None -48 | y: None | Literal[None] -49 | z: Union[Literal[None], None] - | ^^^^ PYI061 -50 | -51 | 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 `Literal[None]` can be replaced with `None` - | -49 | z: Union[Literal[None], None] -50 | -51 | a: int | Literal[None] | None - | ^^^^ PYI061 -52 | b: None | Literal[None] | None -53 | c: (None | Literal[None]) | None - | - = help: Replace with `None` - -PYI061.pyi:52:19: PYI061 `Literal[None]` can be replaced with `None` - | -51 | a: int | Literal[None] | None -52 | b: None | Literal[None] | None - | ^^^^ PYI061 -53 | c: (None | Literal[None]) | None -54 | d: None | (Literal[None] | None) - | - = help: Replace with `None` - -PYI061.pyi:53:20: PYI061 `Literal[None]` can be replaced with `None` - | -51 | a: int | Literal[None] | None -52 | b: None | Literal[None] | None -53 | c: (None | Literal[None]) | None - | ^^^^ PYI061 -54 | d: None | (Literal[None] | None) -55 | e: None | ((None | Literal[None]) | None) | None - | - = help: Replace with `None` - -PYI061.pyi:54:20: PYI061 `Literal[None]` can be replaced with `None` - | -52 | b: None | Literal[None] | None -53 | c: (None | Literal[None]) | 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` - -PYI061.pyi:55:28: PYI061 `Literal[None]` can be replaced with `None` - | -53 | c: (None | Literal[None]) | 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 `Literal[None]` can be replaced with `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 `Literal[None]` can be replaced with `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 e2fd2c21edac56b18d43b2f4dc5b8fb42e9990f9 Mon Sep 17 00:00:00 2001 From: Chandra Kiran G Date: Wed, 11 Dec 2024 16:45:32 +0530 Subject: [PATCH 6/8] chore: update snapshots --- ...rules__flake8_pyi__tests__PYI061_PYI061.py.snap | 14 +++++++------- ...ules__flake8_pyi__tests__PYI061_PYI061.pyi.snap | 6 +++--- 2 files changed, 10 insertions(+), 10 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 8a79e03b845bd9..06bec134febe7d 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 @@ -69,7 +69,7 @@ PYI061.py:16:30: PYI061 [*] `Literal[None, ...]` can be replaced with `Literal[. 14 14 | 15 15 | 16 |-def func4(arg1: Literal[int, None, float]): - 16 |+def func4(arg1: Literal[int, float] | Literal[None]): + 16 |+def func4(arg1: Literal[int, float] | None): 17 17 | ... 18 18 | 19 19 | @@ -130,7 +130,7 @@ PYI061.py:26:5: PYI061 [*] `Literal[None, ...]` can be replaced with `Literal[.. 26 |- None # Comment 1 27 |- , "world" 28 |- ]): - 24 |+def func6(arg1: Literal["hello", "world"] | Literal[None]): + 24 |+def func6(arg1: Literal["hello", "world"] | None): 29 25 | ... 30 26 | 31 27 | @@ -218,7 +218,7 @@ PYI061.py:53:15: PYI061 [*] `Literal[None, ...]` can be replaced with `Literal[. 51 51 | # From flake8-pyi 52 52 | Literal[None] # Y061 None inside "Literal[]" expression. Replace with "None" 53 |-Literal[True, None] # Y061 None inside "Literal[]" expression. Replace with "Literal[True] | None" - 53 |+Literal[True,] | Literal[None] # Y061 None inside "Literal[]" expression. Replace with "Literal[True] | None" + 53 |+Literal[True] | None # Y061 None inside "Literal[]" expression. Replace with "Literal[True] | None" 54 54 | 55 55 | ### 56 56 | # The following rules here are slightly subtle, @@ -279,7 +279,7 @@ PYI061.py:63:12: PYI061 [*] `Literal[None, ...]` can be replaced with `Literal[. 61 61 | # only emit Y061... 62 62 | Literal[None, None] # Y061 None inside "Literal[]" expression. Replace with "None" 63 |-Literal[1, None, "foo", None] # Y061 None inside "Literal[]" expression. Replace with "Literal[1, 'foo'] | None" - 63 |+Literal[1, "foo"] | Literal[None] # Y061 None inside "Literal[]" expression. Replace with "Literal[1, 'foo'] | None" + 63 |+Literal[1, "foo"] | None # Y061 None inside "Literal[]" expression. Replace with "Literal[1, 'foo'] | None" 64 64 | 65 65 | # ... but if Y061 and Y062 both apply 66 66 | # and there are no None members in the Literal[] slice, @@ -300,7 +300,7 @@ PYI061.py:63:25: PYI061 [*] `Literal[None, ...]` can be replaced with `Literal[. 61 61 | # only emit Y061... 62 62 | Literal[None, None] # Y061 None inside "Literal[]" expression. Replace with "None" 63 |-Literal[1, None, "foo", None] # Y061 None inside "Literal[]" expression. Replace with "Literal[1, 'foo'] | None" - 63 |+Literal[1, "foo"] | Literal[None] # Y061 None inside "Literal[]" expression. Replace with "Literal[1, 'foo'] | None" + 63 |+Literal[1, "foo"] | None # Y061 None inside "Literal[]" expression. Replace with "Literal[1, 'foo'] | None" 64 64 | 65 65 | # ... but if Y061 and Y062 both apply 66 66 | # and there are no None members in the Literal[] slice, @@ -319,7 +319,7 @@ PYI061.py:68:9: PYI061 [*] `Literal[None, ...]` can be replaced with `Literal[.. 66 66 | # and there are no None members in the Literal[] slice, 67 67 | # only emit Y062: 68 |-Literal[None, True, None, True] # Y062 Duplicate "Literal[]" member "True" - 68 |+Literal[True, True] | Literal[None] # Y062 Duplicate "Literal[]" member "True" + 68 |+Literal[True, True] | None # Y062 Duplicate "Literal[]" member "True" 69 69 | 70 70 | 71 71 | # Regression tests for https://github.com/astral-sh/ruff/issues/14567 @@ -338,7 +338,7 @@ PYI061.py:68:21: PYI061 [*] `Literal[None, ...]` can be replaced with `Literal[. 66 66 | # and there are no None members in the Literal[] slice, 67 67 | # only emit Y062: 68 |-Literal[None, True, None, True] # Y062 Duplicate "Literal[]" member "True" - 68 |+Literal[True, True] | Literal[None] # Y062 Duplicate "Literal[]" member "True" + 68 |+Literal[True, True] | None # Y062 Duplicate "Literal[]" member "True" 69 69 | 70 70 | 71 71 | # Regression tests for https://github.com/astral-sh/ruff/issues/14567 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 75dcf75e58a7cd..1352d24f3935b4 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 @@ -65,7 +65,7 @@ PYI061.pyi:13:30: PYI061 [*] `Literal[None, ...]` can be replaced with `Literal[ 11 11 | 12 12 | 13 |-def func4(arg1: Literal[int, None, float]): ... - 13 |+def func4(arg1: Literal[int, float] | Literal[None]): ... + 13 |+def func4(arg1: Literal[int, float] | None): ... 14 14 | 15 15 | 16 16 | def func5(arg1: Literal[None, None]): ... @@ -124,7 +124,7 @@ PYI061.pyi:21:5: PYI061 [*] `Literal[None, ...]` can be replaced with `Literal[. 21 |- None # Comment 1 22 |- , "world" 23 |-]): ... - 19 |+def func6(arg1: Literal["hello", "world"] | Literal[None]): ... + 19 |+def func6(arg1: Literal["hello", "world"] | None): ... 24 20 | 25 21 | 26 22 | def func7(arg1: Literal[ @@ -207,7 +207,7 @@ PYI061.pyi:43:15: PYI061 [*] `Literal[None, ...]` can be replaced with `Literal[ 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,] | Literal[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 | 46 46 | # Regression tests for https://github.com/astral-sh/ruff/issues/14567 From 866335257838785306226671f998d674f8243274 Mon Sep 17 00:00:00 2001 From: Chandra Kiran G Date: Thu, 12 Dec 2024 18:00:37 +0530 Subject: [PATCH 7/8] lint: make clippy happy --- .../src/rules/flake8_pyi/rules/redundant_none_literal.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 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 9fa5ae75d078b2..1e690b7ac20abd 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 @@ -104,7 +104,7 @@ pub(crate) fn redundant_none_literal<'a>(checker: &mut Checker, literal_expr: &' // Provide a [`Fix`] when the complete `Literal` can be replaced. Applying the fix // can leave an unused import to be fixed by the `unused-import` rule. let fix = if other_literal_elements_seen { - create_fix_edit_2(checker, literal_expr, literal_elements, literal_subscript).map(|edit| { + create_fix_edit_2(checker, literal_expr, &literal_elements, literal_subscript).map(|edit| { Fix::applicable_edit( edit, if checker.comment_ranges().intersects(literal_expr.range()) { @@ -193,7 +193,7 @@ fn create_fix_edit(semantic: &SemanticModel, literal_expr: &Expr) -> Option, + literal_elements: &[&Expr], literal_subscript: &Expr, ) -> Option { let enclosing_pep604_union = checker @@ -241,7 +241,7 @@ fn create_fix_edit_2( ctx: ExprContext::Load, slice: Box::new(if literal_elements.len() > 1 { Expr::Tuple(ast::ExprTuple { - elts: literal_elements.clone().into_iter().cloned().collect(), + elts: literal_elements.iter().copied().cloned().collect(), range: TextRange::default(), ctx: ExprContext::Load, parenthesized: true, From c9150d400b56c921137c3e9c6ed47ebc7631b62f Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Thu, 12 Dec 2024 19:39:37 +0000 Subject: [PATCH 8/8] Consolidate the two fix functions and offer no fix on Python <=3.9 --- .../rules/redundant_none_literal.rs | 140 +++++++----------- 1 file changed, 53 insertions(+), 87 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 1e690b7ac20abd..db0f6f5abf0cdc 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 @@ -3,15 +3,12 @@ use ruff_macros::{derive_message_formats, ViolationMetadata}; use ruff_python_ast::{ self as ast, Expr, ExprBinOp, ExprContext, ExprNoneLiteral, ExprSubscript, Operator, }; -use ruff_python_semantic::{ - analyze::typing::{traverse_literal, traverse_union}, - SemanticModel, -}; +use ruff_python_semantic::analyze::typing::{traverse_literal, traverse_union}; use ruff_text_size::{Ranged, TextRange}; use smallvec::SmallVec; -use crate::checkers::ast::Checker; +use crate::{checkers::ast::Checker, settings::types::PythonVersion}; /// ## What it does /// Checks for redundant `Literal[None]` annotations. @@ -36,9 +33,14 @@ use crate::checkers::ast::Checker; /// Literal[1, 2, 3, "foo", 5] | None /// ``` /// -/// ## Fix safety +/// ## Fix safety and availability /// This rule's fix is marked as safe unless the literal contains comments. /// +/// There is currently no fix available if there are other elements in the `Literal` slice aside +/// from `None` and [`target-version`] is set to Python 3.9 or lower, as the fix always uses the +/// `|` syntax to create unions rather than `typing.Union`, and the `|` syntax for unions was added +/// in Python 3.10. +/// /// ## References /// - [Typing documentation: Legal parameters for `Literal` at type check time](https://typing.readthedocs.io/en/latest/spec/literal.html#legal-parameters-for-literal-at-type-check-time) #[derive(ViolationMetadata)] @@ -67,55 +69,44 @@ impl Violation for RedundantNoneLiteral { } } -/// RUF037 +/// PYI061 pub(crate) fn redundant_none_literal<'a>(checker: &mut Checker, literal_expr: &'a Expr) { - if !checker.semantic().seen_typing() { + let semantic = checker.semantic(); + + if !semantic.seen_typing() { return; } - let mut none_exprs: SmallVec<[&ExprNoneLiteral; 1]> = SmallVec::new(); - let mut other_literal_elements_seen = false; - let mut literal_elements: Vec<&Expr> = Vec::new(); - let mut literal_subscript = None; - if let Expr::Subscript(ast::ExprSubscript { value, .. }) = literal_expr { - if checker.semantic().match_typing_expr(value, "Literal") { - literal_subscript = Some(value.as_ref()); - } + let Expr::Subscript(ast::ExprSubscript { + value: literal_subscript, + .. + }) = literal_expr + else { + return; }; - let mut find_literal_elements = |expr: &'a Expr, _parent: &'a Expr| { + let mut none_exprs: SmallVec<[&ExprNoneLiteral; 1]> = SmallVec::new(); + let mut literal_elements = vec![]; + + let mut partition_literal_elements = |expr: &'a Expr, _parent: &'a Expr| { if let Expr::NoneLiteral(none_expr) = expr { none_exprs.push(none_expr); } else { - other_literal_elements_seen = true; literal_elements.push(expr); } }; - let Some(literal_subscript) = literal_subscript else { - return; - }; + traverse_literal(&mut partition_literal_elements, semantic, literal_expr); - traverse_literal(&mut find_literal_elements, checker.semantic(), literal_expr); if none_exprs.is_empty() { return; } - // Provide a [`Fix`] when the complete `Literal` can be replaced. Applying the fix - // can leave an unused import to be fixed by the `unused-import` rule. - let fix = if other_literal_elements_seen { - create_fix_edit_2(checker, literal_expr, &literal_elements, literal_subscript).map(|edit| { - Fix::applicable_edit( - edit, - if checker.comment_ranges().intersects(literal_expr.range()) { - Applicability::Unsafe - } else { - Applicability::Safe - }, - ) - }) - } else { - create_fix_edit(checker.semantic(), literal_expr).map(|edit| { + let other_literal_elements_seen = !literal_elements.is_empty(); + + // N.B. Applying the fix can leave an unused import to be fixed by the `unused-import` rule. + let fix = + create_fix_edit(checker, literal_expr, literal_subscript, literal_elements).map(|edit| { Fix::applicable_edit( edit, if checker.comment_ranges().intersects(literal_expr.range()) { @@ -124,8 +115,7 @@ pub(crate) fn redundant_none_literal<'a>(checker: &mut Checker, literal_expr: &' Applicability::Safe }, ) - }) - }; + }); for none_expr in none_exprs { let mut diagnostic = Diagnostic::new( @@ -150,7 +140,14 @@ pub(crate) fn redundant_none_literal<'a>(checker: &mut Checker, literal_expr: &' /// See . /// /// [`typing.Union`]: https://docs.python.org/3/library/typing.html#typing.Union -fn create_fix_edit(semantic: &SemanticModel, literal_expr: &Expr) -> Option { +fn create_fix_edit( + checker: &Checker, + literal_expr: &Expr, + literal_subscript: &Expr, + literal_elements: Vec<&Expr>, +) -> Option { + let semantic = checker.semantic(); + let enclosing_pep604_union = semantic .current_expressions() .skip(1) @@ -165,8 +162,9 @@ fn create_fix_edit(semantic: &SemanticModel, literal_expr: &Expr) -> Option Option Option { - let enclosing_pep604_union = checker - .semantic() - .current_expressions() - .skip(1) - .take_while(|expr| { - matches!( - expr, - Expr::BinOp(ExprBinOp { - op: Operator::BitOr, - .. - }) - ) - }) - .last(); + if literal_elements.is_empty() { + return Some(Edit::range_replacement( + "None".to_string(), + literal_expr.range(), + )); + } - let mut is_fixable = true; - if let Some(enclosing_pep604_union) = enclosing_pep604_union { - traverse_union( - &mut |expr, _| { - if matches!(expr, Expr::NoneLiteral(_)) { - is_fixable = false; - } - if expr != literal_expr { - if let Expr::Subscript(ExprSubscript { value, slice, .. }) = expr { - if checker.semantic().match_typing_expr(value, "Literal") - && matches!(**slice, Expr::NoneLiteral(_)) - { - is_fixable = false; - } - } - } - }, - checker.semantic(), - enclosing_pep604_union, - ); + if checker.settings.target_version < PythonVersion::Py310 { + return None; } let bin_or = Expr::BinOp(ExprBinOp { @@ -241,7 +208,7 @@ fn create_fix_edit_2( ctx: ExprContext::Load, slice: Box::new(if literal_elements.len() > 1 { Expr::Tuple(ast::ExprTuple { - elts: literal_elements.iter().copied().cloned().collect(), + elts: literal_elements.into_iter().cloned().collect(), range: TextRange::default(), ctx: ExprContext::Load, parenthesized: true, @@ -257,6 +224,5 @@ fn create_fix_edit_2( }); let content = checker.generator().expr(&bin_or); - - is_fixable.then(|| Edit::range_replacement(content, literal_expr.range())) + Some(Edit::range_replacement(content, literal_expr.range())) }