From f178ecc2d7a5bf2580a223ba5392f15395431c78 Mon Sep 17 00:00:00 2001 From: Dylan <53534755+dylwil3@users.noreply.github.com> Date: Sun, 9 Feb 2025 09:58:53 -0600 Subject: [PATCH] [`flake8-pyi`] Extend fix to Python <= 3.9 for `redundant-none-literal` (`PYI061`) (#16044) This PR extends the fix offered for [redundant-none-literal (PYI061)](https://docs.astral.sh/ruff/rules/redundant-none-literal/#redundant-none-literal-pyi061) to include Python versions <= 3.9 by using `typing.Optional` instead of the operator `|`. We also offer the fix with `|` for any target version on stub files. Closes #15795 --- .../ruff_linter/src/rules/flake8_pyi/mod.rs | 2 + .../rules/redundant_none_literal.rs | 174 +++--- ...__flake8_pyi__tests__PYI061_PYI061.py.snap | 56 +- ..._flake8_pyi__tests__PYI061_PYI061.pyi.snap | 44 +- ...ke8_pyi__tests__py38_PYI061_PYI061.py.snap | 497 ++++++++++++++++++ ...e8_pyi__tests__py38_PYI061_PYI061.pyi.snap | 324 ++++++++++++ 6 files changed, 977 insertions(+), 120 deletions(-) create mode 100644 crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__py38_PYI061_PYI061.py.snap create mode 100644 crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__py38_PYI061_PYI061.pyi.snap diff --git a/crates/ruff_linter/src/rules/flake8_pyi/mod.rs b/crates/ruff_linter/src/rules/flake8_pyi/mod.rs index ddc47c1e31..6d11956018 100644 --- a/crates/ruff_linter/src/rules/flake8_pyi/mod.rs +++ b/crates/ruff_linter/src/rules/flake8_pyi/mod.rs @@ -181,6 +181,8 @@ mod tests { #[test_case(Rule::TypeAliasWithoutAnnotation, Path::new("PYI026.py"))] #[test_case(Rule::TypeAliasWithoutAnnotation, Path::new("PYI026.pyi"))] + #[test_case(Rule::RedundantNoneLiteral, Path::new("PYI061.py"))] + #[test_case(Rule::RedundantNoneLiteral, Path::new("PYI061.pyi"))] fn py38(rule_code: Rule, path: &Path) -> Result<()> { let snapshot = format!("py38_{}_{}", rule_code.noqa_code(), path.to_string_lossy()); let diagnostics = test_path( 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 a5304251fb..36bb60e9ca 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,14 +1,18 @@ +use anyhow::Result; use ruff_diagnostics::{Applicability, Diagnostic, Edit, Fix, FixAvailability, Violation}; use ruff_macros::{derive_message_formats, ViolationMetadata}; use ruff_python_ast::{ - self as ast, Expr, ExprBinOp, ExprContext, ExprNoneLiteral, ExprSubscript, Operator, + self as ast, + helpers::{pep_604_union, typing_optional}, + name::Name, + Expr, ExprBinOp, ExprContext, ExprNoneLiteral, ExprSubscript, Operator, }; use ruff_python_semantic::analyze::typing::{traverse_literal, traverse_union}; use ruff_text_size::{Ranged, TextRange}; use smallvec::SmallVec; -use crate::{checkers::ast::Checker, settings::types::PythonVersion}; +use crate::{checkers::ast::Checker, importer::ImportRequest, settings::types::PythonVersion}; /// ## What it does /// Checks for redundant `Literal[None]` annotations. @@ -36,16 +40,16 @@ use crate::{checkers::ast::Checker, settings::types::PythonVersion}; /// ## 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. +/// There is currently no fix available when applying the fix would lead to +/// a `TypeError` from an expression of the form `None | None` or when we +/// are unable to import the symbol `typing.Optional` and the Python version +/// is 3.9 or below. /// /// ## 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)] pub(crate) struct RedundantNoneLiteral { - other_literal_elements_seen: bool, + union_kind: UnionKind, } impl Violation for RedundantNoneLiteral { @@ -53,18 +57,22 @@ impl Violation for RedundantNoneLiteral { #[derive_message_formats] fn message(&self) -> String { - if self.other_literal_elements_seen { - "`Literal[None, ...]` can be replaced with `Literal[...] | None`".to_string() - } else { - "`Literal[None]` can be replaced with `None`".to_string() + match self.union_kind { + UnionKind::NoUnion => "Use `None` rather than `Literal[None]`".to_string(), + UnionKind::TypingOptional => { + "Use `Optional[Literal[...]]` rather than `Literal[None, ...]` ".to_string() + } + UnionKind::BitOr => { + "Use `Literal[...] | None` rather than `Literal[None, ...]` ".to_string() + } } } fn fix_title(&self) -> Option { - Some(if self.other_literal_elements_seen { - "Replace with `Literal[...] | None`".to_string() - } else { - "Replace with `None`".to_string() + Some(match self.union_kind { + UnionKind::NoUnion => "Replace with `None`".to_string(), + UnionKind::TypingOptional => "Replace with `Optional[Literal[...]]`".to_string(), + UnionKind::BitOr => "Replace with `Literal[...] | None`".to_string(), }) } } @@ -102,31 +110,29 @@ pub(crate) fn redundant_none_literal<'a>(checker: &Checker, literal_expr: &'a Ex return; } - let other_literal_elements_seen = !literal_elements.is_empty(); + let union_kind = if literal_elements.is_empty() { + UnionKind::NoUnion + } else if (checker.settings.target_version >= PythonVersion::Py310) + || checker.source_type.is_stub() + { + UnionKind::BitOr + } else { + UnionKind::TypingOptional + }; // 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()) { - Applicability::Unsafe - } else { - Applicability::Safe - }, + for none_expr in none_exprs { + let mut diagnostic = + Diagnostic::new(RedundantNoneLiteral { union_kind }, none_expr.range()); + diagnostic.try_set_optional_fix(|| { + create_fix( + checker, + literal_expr, + literal_subscript, + literal_elements.clone(), + union_kind, ) }); - - for none_expr in none_exprs { - let mut diagnostic = Diagnostic::new( - RedundantNoneLiteral { - other_literal_elements_seen, - }, - none_expr.range(), - ); - if let Some(ref fix) = fix { - diagnostic.set_fix(fix.clone()); - } checker.report_diagnostic(diagnostic); } } @@ -140,12 +146,13 @@ pub(crate) fn redundant_none_literal<'a>(checker: &Checker, literal_expr: &'a Ex /// See . /// /// [`typing.Union`]: https://docs.python.org/3/library/typing.html#typing.Union -fn create_fix_edit( +fn create_fix( checker: &Checker, literal_expr: &Expr, literal_subscript: &Expr, literal_elements: Vec<&Expr>, -) -> Option { + union_kind: UnionKind, +) -> Result> { let semantic = checker.semantic(); let enclosing_pep604_union = semantic @@ -185,44 +192,71 @@ fn create_fix_edit( ); if !is_fixable { - return None; + return Ok(None); } } - if literal_elements.is_empty() { - return Some(Edit::range_replacement( - "None".to_string(), - literal_expr.range(), - )); + let applicability = if checker.comment_ranges().intersects(literal_expr.range()) { + Applicability::Unsafe + } else { + Applicability::Safe + }; + + if matches!(union_kind, UnionKind::NoUnion) { + return Ok(Some(Fix::applicable_edit( + Edit::range_replacement("None".to_string(), literal_expr.range()), + applicability, + ))); } - if checker.settings.target_version < PythonVersion::Py310 { - return None; - } - - let bin_or = Expr::BinOp(ExprBinOp { + let new_literal_expr = Expr::Subscript(ast::ExprSubscript { + value: Box::new(literal_subscript.clone()), range: TextRange::default(), - left: Box::new(Expr::Subscript(ast::ExprSubscript { - value: Box::new(literal_subscript.clone()), - range: TextRange::default(), - ctx: ExprContext::Load, - slice: Box::new(if literal_elements.len() > 1 { - Expr::Tuple(ast::ExprTuple { - elts: literal_elements.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 { - range: TextRange::default(), - })), + ctx: ExprContext::Load, + slice: Box::new(if literal_elements.len() > 1 { + Expr::Tuple(ast::ExprTuple { + elts: literal_elements.into_iter().cloned().collect(), + range: TextRange::default(), + ctx: ExprContext::Load, + parenthesized: true, + }) + } else { + literal_elements[0].clone() + }), }); - let content = checker.generator().expr(&bin_or); - Some(Edit::range_replacement(content, literal_expr.range())) + let fix = match union_kind { + UnionKind::TypingOptional => { + let (import_edit, bound_name) = checker.importer().get_or_import_symbol( + &ImportRequest::import_from("typing", "Optional"), + literal_expr.start(), + checker.semantic(), + )?; + let optional_expr = typing_optional(new_literal_expr, Name::from(bound_name)); + let content = checker.generator().expr(&optional_expr); + let optional_edit = Edit::range_replacement(content, literal_expr.range()); + Fix::applicable_edits(import_edit, [optional_edit], applicability) + } + UnionKind::BitOr => { + let none_expr = Expr::NoneLiteral(ExprNoneLiteral { + range: TextRange::default(), + }); + let union_expr = pep_604_union(&[new_literal_expr, none_expr]); + let content = checker.generator().expr(&union_expr); + let union_edit = Edit::range_replacement(content, literal_expr.range()); + Fix::applicable_edit(union_edit, applicability) + } + // We dealt with this case earlier to avoid allocating `lhs` and `rhs` + UnionKind::NoUnion => { + unreachable!() + } + }; + Ok(Some(fix)) +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +enum UnionKind { + NoUnion, + TypingOptional, + BitOr, } 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 558d4ee739..33bf166e8b 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,7 +1,7 @@ --- source: crates/ruff_linter/src/rules/flake8_pyi/mod.rs --- -PYI061.py:4:25: PYI061 [*] `Literal[None]` can be replaced with `None` +PYI061.py:4:25: PYI061 [*] Use `None` rather than `Literal[None]` | 4 | def func1(arg1: Literal[None]): | ^^^^ PYI061 @@ -19,7 +19,7 @@ PYI061.py:4:25: PYI061 [*] `Literal[None]` can be replaced with `None` 6 6 | 7 7 | -PYI061.py:8:25: PYI061 [*] `Literal[None]` can be replaced with `None` +PYI061.py:8:25: PYI061 [*] Use `None` rather than `Literal[None]` | 8 | def func2(arg1: Literal[None] | int): | ^^^^ PYI061 @@ -37,7 +37,7 @@ PYI061.py:8:25: PYI061 [*] `Literal[None]` can be replaced with `None` 10 10 | 11 11 | -PYI061.py:12:24: PYI061 [*] `Literal[None]` can be replaced with `None` +PYI061.py:12:24: PYI061 [*] Use `None` rather than `Literal[None]` | 12 | def func3() -> Literal[None]: | ^^^^ PYI061 @@ -55,7 +55,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 [*] Use `Literal[...] | None` rather than `Literal[None, ...]` | 16 | def func4(arg1: Literal[int, None, float]): | ^^^^ PYI061 @@ -73,7 +73,7 @@ PYI061.py:16:30: PYI061 [*] `Literal[None, ...]` can be replaced with `Literal[. 18 18 | 19 19 | -PYI061.py:20:25: PYI061 [*] `Literal[None]` can be replaced with `None` +PYI061.py:20:25: PYI061 [*] Use `None` rather than `Literal[None]` | 20 | def func5(arg1: Literal[None, None]): | ^^^^ PYI061 @@ -91,7 +91,7 @@ PYI061.py:20:25: PYI061 [*] `Literal[None]` can be replaced with `None` 22 22 | 23 23 | -PYI061.py:20:31: PYI061 [*] `Literal[None]` can be replaced with `None` +PYI061.py:20:31: PYI061 [*] Use `None` rather than `Literal[None]` | 20 | def func5(arg1: Literal[None, None]): | ^^^^ PYI061 @@ -109,7 +109,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 [*] Use `Literal[...] | None` rather than `Literal[None, ...]` | 24 | def func6(arg1: Literal[ 25 | "hello", @@ -134,7 +134,7 @@ PYI061.py:26:5: PYI061 [*] `Literal[None, ...]` can be replaced with `Literal[.. 30 26 | 31 27 | -PYI061.py:33:5: PYI061 [*] `Literal[None]` can be replaced with `None` +PYI061.py:33:5: PYI061 [*] Use `None` rather than `Literal[None]` | 32 | def func7(arg1: Literal[ 33 | None # Comment 1 @@ -156,7 +156,7 @@ PYI061.py:33:5: PYI061 [*] `Literal[None]` can be replaced with `None` 36 34 | 37 35 | -PYI061.py:38:25: PYI061 `Literal[None]` can be replaced with `None` +PYI061.py:38:25: PYI061 Use `None` rather than `Literal[None]` | 38 | def func8(arg1: Literal[None] | None): | ^^^^ PYI061 @@ -164,7 +164,7 @@ PYI061.py:38:25: PYI061 `Literal[None]` can be replaced with `None` | = help: Replace with `None` -PYI061.py:42:31: PYI061 [*] `Literal[None]` can be replaced with `None` +PYI061.py:42:31: PYI061 [*] Use `None` rather than `Literal[None]` | 42 | def func9(arg1: Union[Literal[None], None]): | ^^^^ PYI061 @@ -182,7 +182,7 @@ PYI061.py:42:31: PYI061 [*] `Literal[None]` can be replaced with `None` 44 44 | 45 45 | -PYI061.py:52:9: PYI061 [*] `Literal[None]` can be replaced with `None` +PYI061.py:52:9: PYI061 [*] Use `None` rather than `Literal[None]` | 51 | # From flake8-pyi 52 | Literal[None] # Y061 None inside "Literal[]" expression. Replace with "None" @@ -201,7 +201,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 [*] Use `Literal[...] | None` rather than `Literal[None, ...]` | 51 | # From flake8-pyi 52 | Literal[None] # Y061 None inside "Literal[]" expression. Replace with "None" @@ -222,7 +222,7 @@ PYI061.py:53:15: PYI061 [*] `Literal[None, ...]` can be replaced with `Literal[. 55 55 | ### 56 56 | # The following rules here are slightly subtle, -PYI061.py:62:9: PYI061 [*] `Literal[None]` can be replaced with `None` +PYI061.py:62:9: PYI061 [*] Use `None` rather than `Literal[None]` | 60 | # If Y061 and Y062 both apply, but all the duplicate members are None, 61 | # only emit Y061... @@ -242,7 +242,7 @@ PYI061.py:62:9: PYI061 [*] `Literal[None]` can be replaced with `None` 64 64 | 65 65 | # ... but if Y061 and Y062 both apply -PYI061.py:62:15: PYI061 [*] `Literal[None]` can be replaced with `None` +PYI061.py:62:15: PYI061 [*] Use `None` rather than `Literal[None]` | 60 | # If Y061 and Y062 both apply, but all the duplicate members are None, 61 | # only emit Y061... @@ -262,7 +262,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 [*] Use `Literal[...] | None` rather than `Literal[None, ...]` | 61 | # only emit Y061... 62 | Literal[None, None] # Y061 None inside "Literal[]" expression. Replace with "None" @@ -283,7 +283,7 @@ PYI061.py:63:12: PYI061 [*] `Literal[None, ...]` can be replaced with `Literal[. 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` +PYI061.py:63:25: PYI061 [*] Use `Literal[...] | None` rather than `Literal[None, ...]` | 61 | # only emit Y061... 62 | Literal[None, None] # Y061 None inside "Literal[]" expression. Replace with "None" @@ -304,7 +304,7 @@ PYI061.py:63:25: PYI061 [*] `Literal[None, ...]` can be replaced with `Literal[. 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` +PYI061.py:68:9: PYI061 [*] Use `Literal[...] | None` rather than `Literal[None, ...]` | 66 | # and there are no None members in the Literal[] slice, 67 | # only emit Y062: @@ -323,7 +323,7 @@ PYI061.py:68:9: PYI061 [*] `Literal[None, ...]` can be replaced with `Literal[.. 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` +PYI061.py:68:21: PYI061 [*] Use `Literal[...] | None` rather than `Literal[None, ...]` | 66 | # and there are no None members in the Literal[] slice, 67 | # only emit Y062: @@ -342,7 +342,7 @@ PYI061.py:68:21: PYI061 [*] `Literal[None, ...]` can be replaced with `Literal[. 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` +PYI061.py:72:12: PYI061 Use `None` rather than `Literal[None]` | 71 | # Regression tests for https://github.com/astral-sh/ruff/issues/14567 72 | x: Literal[None] | None @@ -352,7 +352,7 @@ PYI061.py:72:12: PYI061 `Literal[None]` can be replaced with `None` | = help: Replace with `None` -PYI061.py:73:19: PYI061 `Literal[None]` can be replaced with `None` +PYI061.py:73:19: PYI061 Use `None` rather than `Literal[None]` | 71 | # Regression tests for https://github.com/astral-sh/ruff/issues/14567 72 | x: Literal[None] | None @@ -362,7 +362,7 @@ PYI061.py:73:19: PYI061 `Literal[None]` can be replaced with `None` | = help: Replace with `None` -PYI061.py:74:18: PYI061 [*] `Literal[None]` can be replaced with `None` +PYI061.py:74:18: PYI061 [*] Use `None` rather than `Literal[None]` | 72 | x: Literal[None] | None 73 | y: None | Literal[None] @@ -383,7 +383,7 @@ PYI061.py:74:18: PYI061 [*] `Literal[None]` can be replaced with `None` 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` +PYI061.py:76:18: PYI061 Use `None` rather than `Literal[None]` | 74 | z: Union[Literal[None], None] 75 | @@ -394,7 +394,7 @@ PYI061.py:76:18: PYI061 `Literal[None]` can be replaced with `None` | = help: Replace with `None` -PYI061.py:77:19: PYI061 `Literal[None]` can be replaced with `None` +PYI061.py:77:19: PYI061 Use `None` rather than `Literal[None]` | 76 | a: int | Literal[None] | None 77 | b: None | Literal[None] | None @@ -404,7 +404,7 @@ PYI061.py:77:19: PYI061 `Literal[None]` can be replaced with `None` | = help: Replace with `None` -PYI061.py:78:20: PYI061 `Literal[None]` can be replaced with `None` +PYI061.py:78:20: PYI061 Use `None` rather than `Literal[None]` | 76 | a: int | Literal[None] | None 77 | b: None | Literal[None] | None @@ -415,7 +415,7 @@ PYI061.py:78:20: PYI061 `Literal[None]` can be replaced with `None` | = help: Replace with `None` -PYI061.py:79:20: PYI061 `Literal[None]` can be replaced with `None` +PYI061.py:79:20: PYI061 Use `None` rather than `Literal[None]` | 77 | b: None | Literal[None] | None 78 | c: (None | Literal[None]) | None @@ -426,7 +426,7 @@ PYI061.py:79:20: PYI061 `Literal[None]` can be replaced with `None` | = help: Replace with `None` -PYI061.py:80:28: PYI061 `Literal[None]` can be replaced with `None` +PYI061.py:80:28: PYI061 Use `None` rather than `Literal[None]` | 78 | c: (None | Literal[None]) | None 79 | d: None | (Literal[None] | None) @@ -436,7 +436,7 @@ PYI061.py:80:28: PYI061 `Literal[None]` can be replaced with `None` | = help: Replace with `None` -PYI061.py:81:12: PYI061 `Literal[None]` can be replaced 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 @@ -445,7 +445,7 @@ PYI061.py:81:12: PYI061 `Literal[None]` can be replaced with `None` | = help: Replace with `None` -PYI061.py:81:28: PYI061 `Literal[None]` can be replaced 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 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 e1bc23eca4..a2c845c064 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,7 +1,7 @@ --- source: crates/ruff_linter/src/rules/flake8_pyi/mod.rs --- -PYI061.pyi:4:25: PYI061 [*] `Literal[None]` can be replaced with `None` +PYI061.pyi:4:25: PYI061 [*] Use `None` rather than `Literal[None]` | 4 | def func1(arg1: Literal[None]): ... | ^^^^ PYI061 @@ -18,7 +18,7 @@ PYI061.pyi:4:25: PYI061 [*] `Literal[None]` can be replaced with `None` 6 6 | 7 7 | def func2(arg1: Literal[None] | int): ... -PYI061.pyi:7:25: PYI061 [*] `Literal[None]` can be replaced with `None` +PYI061.pyi:7:25: PYI061 [*] Use `None` rather than `Literal[None]` | 7 | def func2(arg1: Literal[None] | int): ... | ^^^^ PYI061 @@ -35,7 +35,7 @@ PYI061.pyi:7:25: PYI061 [*] `Literal[None]` can be replaced with `None` 9 9 | 10 10 | def func3() -> Literal[None]: ... -PYI061.pyi:10:24: PYI061 [*] `Literal[None]` can be replaced with `None` +PYI061.pyi:10:24: PYI061 [*] Use `None` rather than `Literal[None]` | 10 | def func3() -> Literal[None]: ... | ^^^^ PYI061 @@ -52,7 +52,7 @@ 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 [*] Use `Literal[...] | None` rather than `Literal[None, ...]` | 13 | def func4(arg1: Literal[int, None, float]): ... | ^^^^ PYI061 @@ -69,7 +69,7 @@ PYI061.pyi:13:30: PYI061 [*] `Literal[None, ...]` can be replaced with `Literal[ 15 15 | 16 16 | def func5(arg1: Literal[None, None]): ... -PYI061.pyi:16:25: PYI061 [*] `Literal[None]` can be replaced with `None` +PYI061.pyi:16:25: PYI061 [*] Use `None` rather than `Literal[None]` | 16 | def func5(arg1: Literal[None, None]): ... | ^^^^ PYI061 @@ -86,7 +86,7 @@ PYI061.pyi:16:25: PYI061 [*] `Literal[None]` can be replaced with `None` 18 18 | 19 19 | def func6(arg1: Literal[ -PYI061.pyi:16:31: PYI061 [*] `Literal[None]` can be replaced with `None` +PYI061.pyi:16:31: PYI061 [*] Use `None` rather than `Literal[None]` | 16 | def func5(arg1: Literal[None, None]): ... | ^^^^ PYI061 @@ -103,7 +103,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 [*] Use `Literal[...] | None` rather than `Literal[None, ...]` | 19 | def func6(arg1: Literal[ 20 | "hello", @@ -128,7 +128,7 @@ PYI061.pyi:21:5: PYI061 [*] `Literal[None, ...]` can be replaced with `Literal[. 25 21 | 26 22 | def func7(arg1: Literal[ -PYI061.pyi:27:5: PYI061 [*] `Literal[None]` can be replaced with `None` +PYI061.pyi:27:5: PYI061 [*] Use `None` rather than `Literal[None]` | 26 | def func7(arg1: Literal[ 27 | None # Comment 1 @@ -149,14 +149,14 @@ PYI061.pyi:27:5: PYI061 [*] `Literal[None]` can be replaced with `None` 30 28 | 31 29 | def func8(arg1: Literal[None] | None):... -PYI061.pyi:31:25: PYI061 `Literal[None]` can be replaced with `None` +PYI061.pyi:31:25: PYI061 Use `None` rather than `Literal[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` +PYI061.pyi:34:31: PYI061 [*] Use `None` rather than `Literal[None]` | 34 | def func9(arg1: Union[Literal[None], None]): ... | ^^^^ PYI061 @@ -173,7 +173,7 @@ PYI061.pyi:34:31: PYI061 [*] `Literal[None]` can be replaced with `None` 36 36 | 37 37 | # OK -PYI061.pyi:42:9: PYI061 [*] `Literal[None]` can be replaced with `None` +PYI061.pyi:42:9: PYI061 [*] Use `None` rather than `Literal[None]` | 41 | # From flake8-pyi 42 | Literal[None] # PYI061 None inside "Literal[]" expression. Replace with "None" @@ -192,7 +192,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 [*] Use `Literal[...] | None` rather than `Literal[None, ...]` | 41 | # From flake8-pyi 42 | Literal[None] # PYI061 None inside "Literal[]" expression. Replace with "None" @@ -211,7 +211,7 @@ PYI061.pyi:43:15: PYI061 [*] `Literal[None, ...]` can be replaced with `Literal[ 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` +PYI061.pyi:47: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 @@ -221,7 +221,7 @@ PYI061.pyi:47:12: PYI061 `Literal[None]` can be replaced with `None` | = help: Replace with `None` -PYI061.pyi:48:19: PYI061 `Literal[None]` can be replaced with `None` +PYI061.pyi:48: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 @@ -231,7 +231,7 @@ PYI061.pyi:48:19: PYI061 `Literal[None]` can be replaced with `None` | = help: Replace with `None` -PYI061.pyi:49:18: PYI061 [*] `Literal[None]` can be replaced with `None` +PYI061.pyi:49:18: PYI061 [*] Use `None` rather than `Literal[None]` | 47 | x: Literal[None] | None 48 | y: None | Literal[None] @@ -252,7 +252,7 @@ PYI061.pyi:49:18: PYI061 [*] `Literal[None]` can be replaced with `None` 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` +PYI061.pyi:51:18: PYI061 Use `None` rather than `Literal[None]` | 49 | z: Union[Literal[None], None] 50 | @@ -263,7 +263,7 @@ PYI061.pyi:51:18: PYI061 `Literal[None]` can be replaced with `None` | = help: Replace with `None` -PYI061.pyi:52:19: PYI061 `Literal[None]` can be replaced with `None` +PYI061.pyi:52:19: PYI061 Use `None` rather than `Literal[None]` | 51 | a: int | Literal[None] | None 52 | b: None | Literal[None] | None @@ -273,7 +273,7 @@ PYI061.pyi:52:19: PYI061 `Literal[None]` can be replaced with `None` | = help: Replace with `None` -PYI061.pyi:53:20: PYI061 `Literal[None]` can be replaced with `None` +PYI061.pyi:53:20: PYI061 Use `None` rather than `Literal[None]` | 51 | a: int | Literal[None] | None 52 | b: None | Literal[None] | None @@ -284,7 +284,7 @@ PYI061.pyi:53:20: PYI061 `Literal[None]` can be replaced with `None` | = help: Replace with `None` -PYI061.pyi:54:20: PYI061 `Literal[None]` can be replaced with `None` +PYI061.pyi:54:20: PYI061 Use `None` rather than `Literal[None]` | 52 | b: None | Literal[None] | None 53 | c: (None | Literal[None]) | None @@ -295,7 +295,7 @@ PYI061.pyi:54:20: PYI061 `Literal[None]` can be replaced with `None` | = help: Replace with `None` -PYI061.pyi:55:28: PYI061 `Literal[None]` can be replaced with `None` +PYI061.pyi:55:28: PYI061 Use `None` rather than `Literal[None]` | 53 | c: (None | Literal[None]) | None 54 | d: None | (Literal[None] | None) @@ -305,7 +305,7 @@ PYI061.pyi:55:28: PYI061 `Literal[None]` can be replaced with `None` | = help: Replace with `None` -PYI061.pyi:56:12: PYI061 `Literal[None]` can be replaced 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 @@ -314,7 +314,7 @@ PYI061.pyi:56:12: PYI061 `Literal[None]` can be replaced with `None` | = help: Replace with `None` -PYI061.pyi:56:28: PYI061 `Literal[None]` can be replaced 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 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 new file mode 100644 index 0000000000..99f4969598 --- /dev/null +++ b/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__py38_PYI061_PYI061.py.snap @@ -0,0 +1,497 @@ +--- +source: crates/ruff_linter/src/rules/flake8_pyi/mod.rs +--- +PYI061.py:4:25: PYI061 [*] Use `None` rather than `Literal[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 [*] Use `None` rather than `Literal[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 [*] Use `None` rather than `Literal[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 [*] Use `Optional[Literal[...]]` rather than `Literal[None, ...]` + | +16 | def func4(arg1: Literal[int, None, float]): + | ^^^^ PYI061 +17 | ... + | + = help: Replace with `Optional[Literal[...]]` + +ℹ Safe fix +1 |-from typing import Literal, Union + 1 |+from typing import Literal, Union, Optional +2 2 | +3 3 | +4 4 | def func1(arg1: Literal[None]): +-------------------------------------------------------------------------------- +13 13 | ... +14 14 | +15 15 | +16 |-def func4(arg1: Literal[int, None, float]): + 16 |+def func4(arg1: Optional[Literal[int, float]]): +17 17 | ... +18 18 | +19 19 | + +PYI061.py:20:25: PYI061 [*] Use `None` rather than `Literal[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 [*] Use `None` rather than `Literal[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 [*] Use `Optional[Literal[...]]` rather than `Literal[None, ...]` + | +24 | def func6(arg1: Literal[ +25 | "hello", +26 | None # Comment 1 + | ^^^^ PYI061 +27 | , "world" +28 | ]): + | + = help: Replace with `Optional[Literal[...]]` + +ℹ Unsafe fix +1 |-from typing import Literal, Union + 1 |+from typing import Literal, Union, Optional +2 2 | +3 3 | +4 4 | def func1(arg1: Literal[None]): +-------------------------------------------------------------------------------- +21 21 | ... +22 22 | +23 23 | +24 |-def func6(arg1: Literal[ +25 |- "hello", +26 |- None # Comment 1 +27 |- , "world" +28 |- ]): + 24 |+def func6(arg1: Optional[Literal["hello", "world"]]): +29 25 | ... +30 26 | +31 27 | + +PYI061.py:33:5: PYI061 [*] Use `None` rather than `Literal[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 Use `None` rather than `Literal[None]` + | +38 | def func8(arg1: Literal[None] | None): + | ^^^^ PYI061 +39 | ... + | + = help: Replace with `None` + +PYI061.py:42:31: PYI061 [*] Use `None` rather than `Literal[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 [*] Use `None` rather than `Literal[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 [*] Use `Optional[Literal[...]]` rather than `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 `Optional[Literal[...]]` + +ℹ Safe fix +1 |-from typing import Literal, Union + 1 |+from typing import Literal, Union, Optional +2 2 | +3 3 | +4 4 | def func1(arg1: Literal[None]): +-------------------------------------------------------------------------------- +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 |+Optional[Literal[True]] # 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 [*] Use `None` rather than `Literal[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 [*] Use `None` rather than `Literal[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 [*] Use `Optional[Literal[...]]` rather than `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 `Optional[Literal[...]]` + +ℹ Safe fix +1 |-from typing import Literal, Union + 1 |+from typing import Literal, Union, Optional +2 2 | +3 3 | +4 4 | def func1(arg1: Literal[None]): +-------------------------------------------------------------------------------- +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 |+Optional[Literal[1, "foo"]] # 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 [*] Use `Optional[Literal[...]]` rather than `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 `Optional[Literal[...]]` + +ℹ Safe fix +1 |-from typing import Literal, Union + 1 |+from typing import Literal, Union, Optional +2 2 | +3 3 | +4 4 | def func1(arg1: Literal[None]): +-------------------------------------------------------------------------------- +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 |+Optional[Literal[1, "foo"]] # 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 [*] Use `Optional[Literal[...]]` rather than `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 `Optional[Literal[...]]` + +ℹ Safe fix +1 |-from typing import Literal, Union + 1 |+from typing import Literal, Union, Optional +2 2 | +3 3 | +4 4 | def func1(arg1: Literal[None]): +-------------------------------------------------------------------------------- +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 |+Optional[Literal[True, True]] # 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 [*] Use `Optional[Literal[...]]` rather than `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 `Optional[Literal[...]]` + +ℹ Safe fix +1 |-from typing import Literal, Union + 1 |+from typing import Literal, Union, Optional +2 2 | +3 3 | +4 4 | def func1(arg1: Literal[None]): +-------------------------------------------------------------------------------- +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 |+Optional[Literal[True, True]] # 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 Use `None` rather than `Literal[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 Use `None` rather than `Literal[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 [*] Use `None` rather than `Literal[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 Use `None` rather than `Literal[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 Use `None` rather than `Literal[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 Use `None` rather than `Literal[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 Use `None` rather than `Literal[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 Use `None` rather than `Literal[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 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 new file mode 100644 index 0000000000..a2c845c064 --- /dev/null +++ b/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__py38_PYI061_PYI061.pyi.snap @@ -0,0 +1,324 @@ +--- +source: crates/ruff_linter/src/rules/flake8_pyi/mod.rs +--- +PYI061.pyi:4:25: PYI061 [*] Use `None` rather than `Literal[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 [*] Use `None` rather than `Literal[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 [*] Use `None` rather than `Literal[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 [*] Use `Literal[...] | None` rather than `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] | None): ... +14 14 | +15 15 | +16 16 | def func5(arg1: Literal[None, None]): ... + +PYI061.pyi:16:25: PYI061 [*] Use `None` rather than `Literal[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 [*] Use `None` rather than `Literal[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 [*] Use `Literal[...] | None` rather than `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"] | None): ... +24 20 | +25 21 | +26 22 | def func7(arg1: Literal[ + +PYI061.pyi:27:5: PYI061 [*] Use `None` rather than `Literal[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 Use `None` rather than `Literal[None]` + | +31 | def func8(arg1: Literal[None] | None):... + | ^^^^ PYI061 + | + = help: Replace with `None` + +PYI061.pyi:34:31: PYI061 [*] Use `None` rather than `Literal[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 [*] Use `None` rather than `Literal[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 [*] 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" + | ^^^^ 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" +44 44 | +45 45 | +46 46 | # Regression tests for https://github.com/astral-sh/ruff/issues/14567 + +PYI061.pyi:47: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 + | ^^^^ PYI061 +48 | y: None | Literal[None] +49 | z: Union[Literal[None], None] + | + = help: Replace with `None` + +PYI061.pyi:48: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] + | ^^^^ PYI061 +49 | z: Union[Literal[None], None] + | + = help: Replace with `None` + +PYI061.pyi:49:18: PYI061 [*] Use `None` rather than `Literal[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 Use `None` rather than `Literal[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 Use `None` rather than `Literal[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 Use `None` rather than `Literal[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 Use `None` rather than `Literal[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 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 + | ^^^^ 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`