diff --git a/crates/ruff/resources/test/fixtures/ruff/RUF013_0.py b/crates/ruff/resources/test/fixtures/ruff/RUF013_0.py index 90252f46a17e72..acb0e82cd3cc48 100644 --- a/crates/ruff/resources/test/fixtures/ruff/RUF013_0.py +++ b/crates/ruff/resources/test/fixtures/ruff/RUF013_0.py @@ -18,19 +18,19 @@ def f(arg: object = None): pass -def f(arg: int = None): # RUF011 +def f(arg: int = None): # RUF013 pass -def f(arg: str = None): # RUF011 +def f(arg: str = None): # RUF013 pass -def f(arg: typing.List[str] = None): # RUF011 +def f(arg: typing.List[str] = None): # RUF013 pass -def f(arg: Tuple[str] = None): # RUF011 +def f(arg: Tuple[str] = None): # RUF013 pass @@ -64,15 +64,15 @@ def f(arg: Union[int, str, Any] = None): pass -def f(arg: Union = None): # RUF011 +def f(arg: Union = None): # RUF013 pass -def f(arg: Union[int, str] = None): # RUF011 +def f(arg: Union[int, str] = None): # RUF013 pass -def f(arg: typing.Union[int, str] = None): # RUF011 +def f(arg: typing.Union[int, str] = None): # RUF013 pass @@ -91,11 +91,11 @@ def f(arg: int | float | str | None = None): pass -def f(arg: int | float = None): # RUF011 +def f(arg: int | float = None): # RUF013 pass -def f(arg: int | float | str | bytes = None): # RUF011 +def f(arg: int | float | str | bytes = None): # RUF013 pass @@ -110,11 +110,11 @@ def f(arg: Literal[1, 2, None, 3] = None): pass -def f(arg: Literal[1, "foo"] = None): # RUF011 +def f(arg: Literal[1, "foo"] = None): # RUF013 pass -def f(arg: typing.Literal[1, "foo", True] = None): # RUF011 +def f(arg: typing.Literal[1, "foo", True] = None): # RUF013 pass @@ -133,11 +133,11 @@ def f(arg: Annotated[Any, ...] = None): pass -def f(arg: Annotated[int, ...] = None): # RUF011 +def f(arg: Annotated[int, ...] = None): # RUF013 pass -def f(arg: Annotated[Annotated[int | str, ...], ...] = None): # RUF011 +def f(arg: Annotated[Annotated[int | str, ...], ...] = None): # RUF013 pass @@ -153,9 +153,9 @@ def f( def f( - arg1: int = None, # RUF011 - arg2: Union[int, float] = None, # RUF011 - arg3: Literal[1, 2, 3] = None, # RUF011 + arg1: int = None, # RUF013 + arg2: Union[int, float] = None, # RUF013 + arg3: Literal[1, 2, 3] = None, # RUF013 ): pass @@ -183,5 +183,32 @@ def f(arg: Union[Annotated[int, ...], Annotated[Optional[float], ...]] = None): pass -def f(arg: Union[Annotated[int, ...], Union[str, bytes]] = None): # RUF011 +def f(arg: Union[Annotated[int, ...], Union[str, bytes]] = None): # RUF013 + pass + + +# Quoted + + +def f(arg: "int" = None): # RUF013 + pass + + +def f(arg: "str" = None): # RUF013 + pass + + +def f(arg: "st" "r" = None): # RUF013 + pass + + +def f(arg: "Optional[int]" = None): + pass + + +def f(arg: Union["int", "str"] = None): # False negative + pass + + +def f(arg: Union["int", "None"] = None): pass diff --git a/crates/ruff/src/rules/ruff/rules/implicit_optional.rs b/crates/ruff/src/rules/ruff/rules/implicit_optional.rs index 2cc6f1dbc0f6ab..675a75fd0ce36a 100644 --- a/crates/ruff/src/rules/ruff/rules/implicit_optional.rs +++ b/crates/ruff/src/rules/ruff/rules/implicit_optional.rs @@ -4,9 +4,10 @@ use anyhow::Result; use ruff_text_size::TextRange; use rustpython_parser::ast::{self, ArgWithDefault, Arguments, Constant, Expr, Operator, Ranged}; -use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic, Edit, Fix}; +use ruff_diagnostics::{AutofixKind, Diagnostic, Edit, Fix, Violation}; use ruff_macros::{derive_message_formats, violation}; use ruff_python_ast::helpers::is_const_none; +use ruff_python_ast::typing::parse_type_annotation; use ruff_python_semantic::SemanticModel; use crate::checkers::ast::Checker; @@ -65,14 +66,16 @@ pub struct ImplicitOptional { conversion_type: ConversionType, } -impl AlwaysAutofixableViolation for ImplicitOptional { +impl Violation for ImplicitOptional { + const AUTOFIX: AutofixKind = AutofixKind::Sometimes; + #[derive_message_formats] fn message(&self) -> String { format!("PEP 484 prohibits implicit `Optional`") } - fn autofix_title(&self) -> String { - format!("Convert to `{}`", self.conversion_type) + fn autofix_title(&self) -> Option { + Some(format!("Convert to `{}`", self.conversion_type)) } } @@ -142,6 +145,7 @@ enum TypingTarget<'a> { None, Any, Object, + ForwardReference, Optional, Union(Vec<&'a Expr>), Literal(Vec<&'a Expr>), @@ -175,6 +179,10 @@ impl<'a> TypingTarget<'a> { value: Constant::None, .. }) => Some(TypingTarget::None), + Expr::Constant(ast::ExprConstant { + value: Constant::Str(_), + .. + }) => Some(TypingTarget::ForwardReference), _ => semantic.resolve_call_path(expr).and_then(|call_path| { if semantic.match_typing_call_path(&call_path, "Any") { Some(TypingTarget::Any) @@ -196,8 +204,8 @@ impl<'a> TypingTarget<'a> { | TypingTarget::Object => true, TypingTarget::Literal(elements) => elements.iter().any(|element| { let Some(new_target) = TypingTarget::try_from_expr(element, semantic) else { - return false; - }; + return false; + }; // Literal can only contain `None`, a literal value, other `Literal` // or an enum value. match new_target { @@ -208,8 +216,8 @@ impl<'a> TypingTarget<'a> { }), TypingTarget::Union(elements) => elements.iter().any(|element| { let Some(new_target) = TypingTarget::try_from_expr(element, semantic) else { - return false; - }; + return false; + }; match new_target { TypingTarget::None => true, _ => new_target.contains_none(semantic), @@ -217,13 +225,15 @@ impl<'a> TypingTarget<'a> { }), TypingTarget::Annotated(element) => { let Some(new_target) = TypingTarget::try_from_expr(element, semantic) else { - return false; - }; + return false; + }; match new_target { TypingTarget::None => true, _ => new_target.contains_none(semantic), } } + // TODO(charlie): Add support for nested forward references (e.g., `Union["A", "B"]`). + TypingTarget::ForwardReference => true, } } } @@ -305,7 +315,7 @@ fn generate_fix(checker: &Checker, conversion_type: ConversionType, expr: &Expr) } } -/// RUF011 +/// RUF013 pub(crate) fn implicit_optional(checker: &mut Checker, arguments: &Arguments) { for ArgWithDefault { def, @@ -326,15 +336,42 @@ pub(crate) fn implicit_optional(checker: &mut Checker, arguments: &Arguments) { let Some(annotation) = &def.annotation else { continue }; - let Some(expr) = type_hint_explicitly_allows_none(annotation, checker.semantic()) else { - continue; - }; - let conversion_type = checker.settings.target_version.into(); - let mut diagnostic = Diagnostic::new(ImplicitOptional { conversion_type }, expr.range()); - if checker.patch(diagnostic.kind.rule()) { - diagnostic.try_set_fix(|| generate_fix(checker, conversion_type, expr)); + if let Expr::Constant(ast::ExprConstant { + range, + value: Constant::Str(value), + kind: _, + }) = annotation.as_ref() + { + // Quoted annotation. + if let Ok((annotation, kind)) = parse_type_annotation(value, *range, checker.locator) { + let Some(expr) = type_hint_explicitly_allows_none(&annotation, checker.semantic()) else { + continue; + }; + let conversion_type = checker.settings.target_version.into(); + + let mut diagnostic = + Diagnostic::new(ImplicitOptional { conversion_type }, expr.range()); + if checker.patch(diagnostic.kind.rule()) { + if kind.is_simple() { + diagnostic.try_set_fix(|| generate_fix(checker, conversion_type, expr)); + } + } + checker.diagnostics.push(diagnostic); + } + } else { + // Unquoted annotation. + let Some(expr) = type_hint_explicitly_allows_none(annotation, checker.semantic()) else { + continue; + }; + let conversion_type = checker.settings.target_version.into(); + + let mut diagnostic = + Diagnostic::new(ImplicitOptional { conversion_type }, expr.range()); + if checker.patch(diagnostic.kind.rule()) { + diagnostic.try_set_fix(|| generate_fix(checker, conversion_type, expr)); + } + checker.diagnostics.push(diagnostic); } - checker.diagnostics.push(diagnostic); } } diff --git a/crates/ruff/src/rules/ruff/snapshots/ruff__rules__ruff__tests__PY39_RUF013_RUF013_0.py.snap b/crates/ruff/src/rules/ruff/snapshots/ruff__rules__ruff__tests__PY39_RUF013_RUF013_0.py.snap index 956b055d44102d..323513456ce174 100644 --- a/crates/ruff/src/rules/ruff/snapshots/ruff__rules__ruff__tests__PY39_RUF013_RUF013_0.py.snap +++ b/crates/ruff/src/rules/ruff/snapshots/ruff__rules__ruff__tests__PY39_RUF013_RUF013_0.py.snap @@ -3,7 +3,7 @@ source: crates/ruff/src/rules/ruff/mod.rs --- RUF013_0.py:21:12: RUF013 [*] PEP 484 prohibits implicit `Optional` | -21 | def f(arg: int = None): # RUF011 +21 | def f(arg: int = None): # RUF013 | ^^^ RUF013 22 | pass | @@ -13,15 +13,15 @@ RUF013_0.py:21:12: RUF013 [*] PEP 484 prohibits implicit `Optional` 18 18 | pass 19 19 | 20 20 | -21 |-def f(arg: int = None): # RUF011 - 21 |+def f(arg: Optional[int] = None): # RUF011 +21 |-def f(arg: int = None): # RUF013 + 21 |+def f(arg: Optional[int] = None): # RUF013 22 22 | pass 23 23 | 24 24 | RUF013_0.py:25:12: RUF013 [*] PEP 484 prohibits implicit `Optional` | -25 | def f(arg: str = None): # RUF011 +25 | def f(arg: str = None): # RUF013 | ^^^ RUF013 26 | pass | @@ -31,15 +31,15 @@ RUF013_0.py:25:12: RUF013 [*] PEP 484 prohibits implicit `Optional` 22 22 | pass 23 23 | 24 24 | -25 |-def f(arg: str = None): # RUF011 - 25 |+def f(arg: Optional[str] = None): # RUF011 +25 |-def f(arg: str = None): # RUF013 + 25 |+def f(arg: Optional[str] = None): # RUF013 26 26 | pass 27 27 | 28 28 | RUF013_0.py:29:12: RUF013 [*] PEP 484 prohibits implicit `Optional` | -29 | def f(arg: typing.List[str] = None): # RUF011 +29 | def f(arg: typing.List[str] = None): # RUF013 | ^^^^^^^^^^^^^^^^ RUF013 30 | pass | @@ -49,15 +49,15 @@ RUF013_0.py:29:12: RUF013 [*] PEP 484 prohibits implicit `Optional` 26 26 | pass 27 27 | 28 28 | -29 |-def f(arg: typing.List[str] = None): # RUF011 - 29 |+def f(arg: Optional[typing.List[str]] = None): # RUF011 +29 |-def f(arg: typing.List[str] = None): # RUF013 + 29 |+def f(arg: Optional[typing.List[str]] = None): # RUF013 30 30 | pass 31 31 | 32 32 | RUF013_0.py:33:12: RUF013 [*] PEP 484 prohibits implicit `Optional` | -33 | def f(arg: Tuple[str] = None): # RUF011 +33 | def f(arg: Tuple[str] = None): # RUF013 | ^^^^^^^^^^ RUF013 34 | pass | @@ -67,15 +67,15 @@ RUF013_0.py:33:12: RUF013 [*] PEP 484 prohibits implicit `Optional` 30 30 | pass 31 31 | 32 32 | -33 |-def f(arg: Tuple[str] = None): # RUF011 - 33 |+def f(arg: Optional[Tuple[str]] = None): # RUF011 +33 |-def f(arg: Tuple[str] = None): # RUF013 + 33 |+def f(arg: Optional[Tuple[str]] = None): # RUF013 34 34 | pass 35 35 | 36 36 | RUF013_0.py:67:12: RUF013 [*] PEP 484 prohibits implicit `Optional` | -67 | def f(arg: Union = None): # RUF011 +67 | def f(arg: Union = None): # RUF013 | ^^^^^ RUF013 68 | pass | @@ -85,15 +85,15 @@ RUF013_0.py:67:12: RUF013 [*] PEP 484 prohibits implicit `Optional` 64 64 | pass 65 65 | 66 66 | -67 |-def f(arg: Union = None): # RUF011 - 67 |+def f(arg: Optional[Union] = None): # RUF011 +67 |-def f(arg: Union = None): # RUF013 + 67 |+def f(arg: Optional[Union] = None): # RUF013 68 68 | pass 69 69 | 70 70 | RUF013_0.py:71:12: RUF013 [*] PEP 484 prohibits implicit `Optional` | -71 | def f(arg: Union[int, str] = None): # RUF011 +71 | def f(arg: Union[int, str] = None): # RUF013 | ^^^^^^^^^^^^^^^ RUF013 72 | pass | @@ -103,15 +103,15 @@ RUF013_0.py:71:12: RUF013 [*] PEP 484 prohibits implicit `Optional` 68 68 | pass 69 69 | 70 70 | -71 |-def f(arg: Union[int, str] = None): # RUF011 - 71 |+def f(arg: Optional[Union[int, str]] = None): # RUF011 +71 |-def f(arg: Union[int, str] = None): # RUF013 + 71 |+def f(arg: Optional[Union[int, str]] = None): # RUF013 72 72 | pass 73 73 | 74 74 | RUF013_0.py:75:12: RUF013 [*] PEP 484 prohibits implicit `Optional` | -75 | def f(arg: typing.Union[int, str] = None): # RUF011 +75 | def f(arg: typing.Union[int, str] = None): # RUF013 | ^^^^^^^^^^^^^^^^^^^^^^ RUF013 76 | pass | @@ -121,15 +121,15 @@ RUF013_0.py:75:12: RUF013 [*] PEP 484 prohibits implicit `Optional` 72 72 | pass 73 73 | 74 74 | -75 |-def f(arg: typing.Union[int, str] = None): # RUF011 - 75 |+def f(arg: Optional[typing.Union[int, str]] = None): # RUF011 +75 |-def f(arg: typing.Union[int, str] = None): # RUF013 + 75 |+def f(arg: Optional[typing.Union[int, str]] = None): # RUF013 76 76 | pass 77 77 | 78 78 | RUF013_0.py:94:12: RUF013 [*] PEP 484 prohibits implicit `Optional` | -94 | def f(arg: int | float = None): # RUF011 +94 | def f(arg: int | float = None): # RUF013 | ^^^^^^^^^^^ RUF013 95 | pass | @@ -139,15 +139,15 @@ RUF013_0.py:94:12: RUF013 [*] PEP 484 prohibits implicit `Optional` 91 91 | pass 92 92 | 93 93 | -94 |-def f(arg: int | float = None): # RUF011 - 94 |+def f(arg: Optional[int | float] = None): # RUF011 +94 |-def f(arg: int | float = None): # RUF013 + 94 |+def f(arg: Optional[int | float] = None): # RUF013 95 95 | pass 96 96 | 97 97 | RUF013_0.py:98:12: RUF013 [*] PEP 484 prohibits implicit `Optional` | -98 | def f(arg: int | float | str | bytes = None): # RUF011 +98 | def f(arg: int | float | str | bytes = None): # RUF013 | ^^^^^^^^^^^^^^^^^^^^^^^^^ RUF013 99 | pass | @@ -157,15 +157,15 @@ RUF013_0.py:98:12: RUF013 [*] PEP 484 prohibits implicit `Optional` 95 95 | pass 96 96 | 97 97 | -98 |-def f(arg: int | float | str | bytes = None): # RUF011 - 98 |+def f(arg: Optional[int | float | str | bytes] = None): # RUF011 +98 |-def f(arg: int | float | str | bytes = None): # RUF013 + 98 |+def f(arg: Optional[int | float | str | bytes] = None): # RUF013 99 99 | pass 100 100 | 101 101 | RUF013_0.py:113:12: RUF013 [*] PEP 484 prohibits implicit `Optional` | -113 | def f(arg: Literal[1, "foo"] = None): # RUF011 +113 | def f(arg: Literal[1, "foo"] = None): # RUF013 | ^^^^^^^^^^^^^^^^^ RUF013 114 | pass | @@ -175,15 +175,15 @@ RUF013_0.py:113:12: RUF013 [*] PEP 484 prohibits implicit `Optional` 110 110 | pass 111 111 | 112 112 | -113 |-def f(arg: Literal[1, "foo"] = None): # RUF011 - 113 |+def f(arg: Optional[Literal[1, "foo"]] = None): # RUF011 +113 |-def f(arg: Literal[1, "foo"] = None): # RUF013 + 113 |+def f(arg: Optional[Literal[1, "foo"]] = None): # RUF013 114 114 | pass 115 115 | 116 116 | RUF013_0.py:117:12: RUF013 [*] PEP 484 prohibits implicit `Optional` | -117 | def f(arg: typing.Literal[1, "foo", True] = None): # RUF011 +117 | def f(arg: typing.Literal[1, "foo", True] = None): # RUF013 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ RUF013 118 | pass | @@ -193,15 +193,15 @@ RUF013_0.py:117:12: RUF013 [*] PEP 484 prohibits implicit `Optional` 114 114 | pass 115 115 | 116 116 | -117 |-def f(arg: typing.Literal[1, "foo", True] = None): # RUF011 - 117 |+def f(arg: Optional[typing.Literal[1, "foo", True]] = None): # RUF011 +117 |-def f(arg: typing.Literal[1, "foo", True] = None): # RUF013 + 117 |+def f(arg: Optional[typing.Literal[1, "foo", True]] = None): # RUF013 118 118 | pass 119 119 | 120 120 | RUF013_0.py:136:22: RUF013 [*] PEP 484 prohibits implicit `Optional` | -136 | def f(arg: Annotated[int, ...] = None): # RUF011 +136 | def f(arg: Annotated[int, ...] = None): # RUF013 | ^^^ RUF013 137 | pass | @@ -211,15 +211,15 @@ RUF013_0.py:136:22: RUF013 [*] PEP 484 prohibits implicit `Optional` 133 133 | pass 134 134 | 135 135 | -136 |-def f(arg: Annotated[int, ...] = None): # RUF011 - 136 |+def f(arg: Annotated[Optional[int], ...] = None): # RUF011 +136 |-def f(arg: Annotated[int, ...] = None): # RUF013 + 136 |+def f(arg: Annotated[Optional[int], ...] = None): # RUF013 137 137 | pass 138 138 | 139 139 | RUF013_0.py:140:32: RUF013 [*] PEP 484 prohibits implicit `Optional` | -140 | def f(arg: Annotated[Annotated[int | str, ...], ...] = None): # RUF011 +140 | def f(arg: Annotated[Annotated[int | str, ...], ...] = None): # RUF013 | ^^^^^^^^^ RUF013 141 | pass | @@ -229,8 +229,8 @@ RUF013_0.py:140:32: RUF013 [*] PEP 484 prohibits implicit `Optional` 137 137 | pass 138 138 | 139 139 | -140 |-def f(arg: Annotated[Annotated[int | str, ...], ...] = None): # RUF011 - 140 |+def f(arg: Annotated[Annotated[Optional[int | str], ...], ...] = None): # RUF011 +140 |-def f(arg: Annotated[Annotated[int | str, ...], ...] = None): # RUF013 + 140 |+def f(arg: Annotated[Annotated[Optional[int | str], ...], ...] = None): # RUF013 141 141 | pass 142 142 | 143 143 | @@ -238,10 +238,10 @@ RUF013_0.py:140:32: RUF013 [*] PEP 484 prohibits implicit `Optional` RUF013_0.py:156:11: RUF013 [*] PEP 484 prohibits implicit `Optional` | 155 | def f( -156 | arg1: int = None, # RUF011 +156 | arg1: int = None, # RUF013 | ^^^ RUF013 -157 | arg2: Union[int, float] = None, # RUF011 -158 | arg3: Literal[1, 2, 3] = None, # RUF011 +157 | arg2: Union[int, float] = None, # RUF013 +158 | arg3: Literal[1, 2, 3] = None, # RUF013 | = help: Convert to `Optional[T]` @@ -249,19 +249,19 @@ RUF013_0.py:156:11: RUF013 [*] PEP 484 prohibits implicit `Optional` 153 153 | 154 154 | 155 155 | def f( -156 |- arg1: int = None, # RUF011 - 156 |+ arg1: Optional[int] = None, # RUF011 -157 157 | arg2: Union[int, float] = None, # RUF011 -158 158 | arg3: Literal[1, 2, 3] = None, # RUF011 +156 |- arg1: int = None, # RUF013 + 156 |+ arg1: Optional[int] = None, # RUF013 +157 157 | arg2: Union[int, float] = None, # RUF013 +158 158 | arg3: Literal[1, 2, 3] = None, # RUF013 159 159 | ): RUF013_0.py:157:11: RUF013 [*] PEP 484 prohibits implicit `Optional` | 155 | def f( -156 | arg1: int = None, # RUF011 -157 | arg2: Union[int, float] = None, # RUF011 +156 | arg1: int = None, # RUF013 +157 | arg2: Union[int, float] = None, # RUF013 | ^^^^^^^^^^^^^^^^^ RUF013 -158 | arg3: Literal[1, 2, 3] = None, # RUF011 +158 | arg3: Literal[1, 2, 3] = None, # RUF013 159 | ): | = help: Convert to `Optional[T]` @@ -269,18 +269,18 @@ RUF013_0.py:157:11: RUF013 [*] PEP 484 prohibits implicit `Optional` ℹ Suggested fix 154 154 | 155 155 | def f( -156 156 | arg1: int = None, # RUF011 -157 |- arg2: Union[int, float] = None, # RUF011 - 157 |+ arg2: Optional[Union[int, float]] = None, # RUF011 -158 158 | arg3: Literal[1, 2, 3] = None, # RUF011 +156 156 | arg1: int = None, # RUF013 +157 |- arg2: Union[int, float] = None, # RUF013 + 157 |+ arg2: Optional[Union[int, float]] = None, # RUF013 +158 158 | arg3: Literal[1, 2, 3] = None, # RUF013 159 159 | ): 160 160 | pass RUF013_0.py:158:11: RUF013 [*] PEP 484 prohibits implicit `Optional` | -156 | arg1: int = None, # RUF011 -157 | arg2: Union[int, float] = None, # RUF011 -158 | arg3: Literal[1, 2, 3] = None, # RUF011 +156 | arg1: int = None, # RUF013 +157 | arg2: Union[int, float] = None, # RUF013 +158 | arg3: Literal[1, 2, 3] = None, # RUF013 | ^^^^^^^^^^^^^^^^ RUF013 159 | ): 160 | pass @@ -289,17 +289,17 @@ RUF013_0.py:158:11: RUF013 [*] PEP 484 prohibits implicit `Optional` ℹ Suggested fix 155 155 | def f( -156 156 | arg1: int = None, # RUF011 -157 157 | arg2: Union[int, float] = None, # RUF011 -158 |- arg3: Literal[1, 2, 3] = None, # RUF011 - 158 |+ arg3: Optional[Literal[1, 2, 3]] = None, # RUF011 +156 156 | arg1: int = None, # RUF013 +157 157 | arg2: Union[int, float] = None, # RUF013 +158 |- arg3: Literal[1, 2, 3] = None, # RUF013 + 158 |+ arg3: Optional[Literal[1, 2, 3]] = None, # RUF013 159 159 | ): 160 160 | pass 161 161 | RUF013_0.py:186:12: RUF013 [*] PEP 484 prohibits implicit `Optional` | -186 | def f(arg: Union[Annotated[int, ...], Union[str, bytes]] = None): # RUF011 +186 | def f(arg: Union[Annotated[int, ...], Union[str, bytes]] = None): # RUF013 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ RUF013 187 | pass | @@ -309,8 +309,54 @@ RUF013_0.py:186:12: RUF013 [*] PEP 484 prohibits implicit `Optional` 183 183 | pass 184 184 | 185 185 | -186 |-def f(arg: Union[Annotated[int, ...], Union[str, bytes]] = None): # RUF011 - 186 |+def f(arg: Optional[Union[Annotated[int, ...], Union[str, bytes]]] = None): # RUF011 +186 |-def f(arg: Union[Annotated[int, ...], Union[str, bytes]] = None): # RUF013 + 186 |+def f(arg: Optional[Union[Annotated[int, ...], Union[str, bytes]]] = None): # RUF013 187 187 | pass +188 188 | +189 189 | + +RUF013_0.py:193:13: RUF013 [*] PEP 484 prohibits implicit `Optional` + | +193 | def f(arg: "int" = None): # RUF013 + | ^^^ RUF013 +194 | pass + | + = help: Convert to `Optional[T]` + +ℹ Suggested fix +190 190 | # Quoted +191 191 | +192 192 | +193 |-def f(arg: "int" = None): # RUF013 + 193 |+def f(arg: "Optional[int]" = None): # RUF013 +194 194 | pass +195 195 | +196 196 | + +RUF013_0.py:197:13: RUF013 [*] PEP 484 prohibits implicit `Optional` + | +197 | def f(arg: "str" = None): # RUF013 + | ^^^ RUF013 +198 | pass + | + = help: Convert to `Optional[T]` + +ℹ Suggested fix +194 194 | pass +195 195 | +196 196 | +197 |-def f(arg: "str" = None): # RUF013 + 197 |+def f(arg: "Optional[str]" = None): # RUF013 +198 198 | pass +199 199 | +200 200 | + +RUF013_0.py:201:12: RUF013 PEP 484 prohibits implicit `Optional` + | +201 | def f(arg: "st" "r" = None): # RUF013 + | ^^^^^^^^ RUF013 +202 | pass + | + = help: Convert to `Optional[T]` diff --git a/crates/ruff/src/rules/ruff/snapshots/ruff__rules__ruff__tests__RUF013_RUF013_0.py.snap b/crates/ruff/src/rules/ruff/snapshots/ruff__rules__ruff__tests__RUF013_RUF013_0.py.snap index 23ff31a6b69480..25ffc55a4d7839 100644 --- a/crates/ruff/src/rules/ruff/snapshots/ruff__rules__ruff__tests__RUF013_RUF013_0.py.snap +++ b/crates/ruff/src/rules/ruff/snapshots/ruff__rules__ruff__tests__RUF013_RUF013_0.py.snap @@ -3,7 +3,7 @@ source: crates/ruff/src/rules/ruff/mod.rs --- RUF013_0.py:21:12: RUF013 [*] PEP 484 prohibits implicit `Optional` | -21 | def f(arg: int = None): # RUF011 +21 | def f(arg: int = None): # RUF013 | ^^^ RUF013 22 | pass | @@ -13,15 +13,15 @@ RUF013_0.py:21:12: RUF013 [*] PEP 484 prohibits implicit `Optional` 18 18 | pass 19 19 | 20 20 | -21 |-def f(arg: int = None): # RUF011 - 21 |+def f(arg: int | None = None): # RUF011 +21 |-def f(arg: int = None): # RUF013 + 21 |+def f(arg: int | None = None): # RUF013 22 22 | pass 23 23 | 24 24 | RUF013_0.py:25:12: RUF013 [*] PEP 484 prohibits implicit `Optional` | -25 | def f(arg: str = None): # RUF011 +25 | def f(arg: str = None): # RUF013 | ^^^ RUF013 26 | pass | @@ -31,15 +31,15 @@ RUF013_0.py:25:12: RUF013 [*] PEP 484 prohibits implicit `Optional` 22 22 | pass 23 23 | 24 24 | -25 |-def f(arg: str = None): # RUF011 - 25 |+def f(arg: str | None = None): # RUF011 +25 |-def f(arg: str = None): # RUF013 + 25 |+def f(arg: str | None = None): # RUF013 26 26 | pass 27 27 | 28 28 | RUF013_0.py:29:12: RUF013 [*] PEP 484 prohibits implicit `Optional` | -29 | def f(arg: typing.List[str] = None): # RUF011 +29 | def f(arg: typing.List[str] = None): # RUF013 | ^^^^^^^^^^^^^^^^ RUF013 30 | pass | @@ -49,15 +49,15 @@ RUF013_0.py:29:12: RUF013 [*] PEP 484 prohibits implicit `Optional` 26 26 | pass 27 27 | 28 28 | -29 |-def f(arg: typing.List[str] = None): # RUF011 - 29 |+def f(arg: typing.List[str] | None = None): # RUF011 +29 |-def f(arg: typing.List[str] = None): # RUF013 + 29 |+def f(arg: typing.List[str] | None = None): # RUF013 30 30 | pass 31 31 | 32 32 | RUF013_0.py:33:12: RUF013 [*] PEP 484 prohibits implicit `Optional` | -33 | def f(arg: Tuple[str] = None): # RUF011 +33 | def f(arg: Tuple[str] = None): # RUF013 | ^^^^^^^^^^ RUF013 34 | pass | @@ -67,15 +67,15 @@ RUF013_0.py:33:12: RUF013 [*] PEP 484 prohibits implicit `Optional` 30 30 | pass 31 31 | 32 32 | -33 |-def f(arg: Tuple[str] = None): # RUF011 - 33 |+def f(arg: Tuple[str] | None = None): # RUF011 +33 |-def f(arg: Tuple[str] = None): # RUF013 + 33 |+def f(arg: Tuple[str] | None = None): # RUF013 34 34 | pass 35 35 | 36 36 | RUF013_0.py:67:12: RUF013 [*] PEP 484 prohibits implicit `Optional` | -67 | def f(arg: Union = None): # RUF011 +67 | def f(arg: Union = None): # RUF013 | ^^^^^ RUF013 68 | pass | @@ -85,15 +85,15 @@ RUF013_0.py:67:12: RUF013 [*] PEP 484 prohibits implicit `Optional` 64 64 | pass 65 65 | 66 66 | -67 |-def f(arg: Union = None): # RUF011 - 67 |+def f(arg: Union | None = None): # RUF011 +67 |-def f(arg: Union = None): # RUF013 + 67 |+def f(arg: Union | None = None): # RUF013 68 68 | pass 69 69 | 70 70 | RUF013_0.py:71:12: RUF013 [*] PEP 484 prohibits implicit `Optional` | -71 | def f(arg: Union[int, str] = None): # RUF011 +71 | def f(arg: Union[int, str] = None): # RUF013 | ^^^^^^^^^^^^^^^ RUF013 72 | pass | @@ -103,15 +103,15 @@ RUF013_0.py:71:12: RUF013 [*] PEP 484 prohibits implicit `Optional` 68 68 | pass 69 69 | 70 70 | -71 |-def f(arg: Union[int, str] = None): # RUF011 - 71 |+def f(arg: Union[int, str] | None = None): # RUF011 +71 |-def f(arg: Union[int, str] = None): # RUF013 + 71 |+def f(arg: Union[int, str] | None = None): # RUF013 72 72 | pass 73 73 | 74 74 | RUF013_0.py:75:12: RUF013 [*] PEP 484 prohibits implicit `Optional` | -75 | def f(arg: typing.Union[int, str] = None): # RUF011 +75 | def f(arg: typing.Union[int, str] = None): # RUF013 | ^^^^^^^^^^^^^^^^^^^^^^ RUF013 76 | pass | @@ -121,15 +121,15 @@ RUF013_0.py:75:12: RUF013 [*] PEP 484 prohibits implicit `Optional` 72 72 | pass 73 73 | 74 74 | -75 |-def f(arg: typing.Union[int, str] = None): # RUF011 - 75 |+def f(arg: typing.Union[int, str] | None = None): # RUF011 +75 |-def f(arg: typing.Union[int, str] = None): # RUF013 + 75 |+def f(arg: typing.Union[int, str] | None = None): # RUF013 76 76 | pass 77 77 | 78 78 | RUF013_0.py:94:12: RUF013 [*] PEP 484 prohibits implicit `Optional` | -94 | def f(arg: int | float = None): # RUF011 +94 | def f(arg: int | float = None): # RUF013 | ^^^^^^^^^^^ RUF013 95 | pass | @@ -139,15 +139,15 @@ RUF013_0.py:94:12: RUF013 [*] PEP 484 prohibits implicit `Optional` 91 91 | pass 92 92 | 93 93 | -94 |-def f(arg: int | float = None): # RUF011 - 94 |+def f(arg: int | float | None = None): # RUF011 +94 |-def f(arg: int | float = None): # RUF013 + 94 |+def f(arg: int | float | None = None): # RUF013 95 95 | pass 96 96 | 97 97 | RUF013_0.py:98:12: RUF013 [*] PEP 484 prohibits implicit `Optional` | -98 | def f(arg: int | float | str | bytes = None): # RUF011 +98 | def f(arg: int | float | str | bytes = None): # RUF013 | ^^^^^^^^^^^^^^^^^^^^^^^^^ RUF013 99 | pass | @@ -157,15 +157,15 @@ RUF013_0.py:98:12: RUF013 [*] PEP 484 prohibits implicit `Optional` 95 95 | pass 96 96 | 97 97 | -98 |-def f(arg: int | float | str | bytes = None): # RUF011 - 98 |+def f(arg: int | float | str | bytes | None = None): # RUF011 +98 |-def f(arg: int | float | str | bytes = None): # RUF013 + 98 |+def f(arg: int | float | str | bytes | None = None): # RUF013 99 99 | pass 100 100 | 101 101 | RUF013_0.py:113:12: RUF013 [*] PEP 484 prohibits implicit `Optional` | -113 | def f(arg: Literal[1, "foo"] = None): # RUF011 +113 | def f(arg: Literal[1, "foo"] = None): # RUF013 | ^^^^^^^^^^^^^^^^^ RUF013 114 | pass | @@ -175,15 +175,15 @@ RUF013_0.py:113:12: RUF013 [*] PEP 484 prohibits implicit `Optional` 110 110 | pass 111 111 | 112 112 | -113 |-def f(arg: Literal[1, "foo"] = None): # RUF011 - 113 |+def f(arg: Literal[1, "foo"] | None = None): # RUF011 +113 |-def f(arg: Literal[1, "foo"] = None): # RUF013 + 113 |+def f(arg: Literal[1, "foo"] | None = None): # RUF013 114 114 | pass 115 115 | 116 116 | RUF013_0.py:117:12: RUF013 [*] PEP 484 prohibits implicit `Optional` | -117 | def f(arg: typing.Literal[1, "foo", True] = None): # RUF011 +117 | def f(arg: typing.Literal[1, "foo", True] = None): # RUF013 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ RUF013 118 | pass | @@ -193,15 +193,15 @@ RUF013_0.py:117:12: RUF013 [*] PEP 484 prohibits implicit `Optional` 114 114 | pass 115 115 | 116 116 | -117 |-def f(arg: typing.Literal[1, "foo", True] = None): # RUF011 - 117 |+def f(arg: typing.Literal[1, "foo", True] | None = None): # RUF011 +117 |-def f(arg: typing.Literal[1, "foo", True] = None): # RUF013 + 117 |+def f(arg: typing.Literal[1, "foo", True] | None = None): # RUF013 118 118 | pass 119 119 | 120 120 | RUF013_0.py:136:22: RUF013 [*] PEP 484 prohibits implicit `Optional` | -136 | def f(arg: Annotated[int, ...] = None): # RUF011 +136 | def f(arg: Annotated[int, ...] = None): # RUF013 | ^^^ RUF013 137 | pass | @@ -211,15 +211,15 @@ RUF013_0.py:136:22: RUF013 [*] PEP 484 prohibits implicit `Optional` 133 133 | pass 134 134 | 135 135 | -136 |-def f(arg: Annotated[int, ...] = None): # RUF011 - 136 |+def f(arg: Annotated[int | None, ...] = None): # RUF011 +136 |-def f(arg: Annotated[int, ...] = None): # RUF013 + 136 |+def f(arg: Annotated[int | None, ...] = None): # RUF013 137 137 | pass 138 138 | 139 139 | RUF013_0.py:140:32: RUF013 [*] PEP 484 prohibits implicit `Optional` | -140 | def f(arg: Annotated[Annotated[int | str, ...], ...] = None): # RUF011 +140 | def f(arg: Annotated[Annotated[int | str, ...], ...] = None): # RUF013 | ^^^^^^^^^ RUF013 141 | pass | @@ -229,8 +229,8 @@ RUF013_0.py:140:32: RUF013 [*] PEP 484 prohibits implicit `Optional` 137 137 | pass 138 138 | 139 139 | -140 |-def f(arg: Annotated[Annotated[int | str, ...], ...] = None): # RUF011 - 140 |+def f(arg: Annotated[Annotated[int | str | None, ...], ...] = None): # RUF011 +140 |-def f(arg: Annotated[Annotated[int | str, ...], ...] = None): # RUF013 + 140 |+def f(arg: Annotated[Annotated[int | str | None, ...], ...] = None): # RUF013 141 141 | pass 142 142 | 143 143 | @@ -238,10 +238,10 @@ RUF013_0.py:140:32: RUF013 [*] PEP 484 prohibits implicit `Optional` RUF013_0.py:156:11: RUF013 [*] PEP 484 prohibits implicit `Optional` | 155 | def f( -156 | arg1: int = None, # RUF011 +156 | arg1: int = None, # RUF013 | ^^^ RUF013 -157 | arg2: Union[int, float] = None, # RUF011 -158 | arg3: Literal[1, 2, 3] = None, # RUF011 +157 | arg2: Union[int, float] = None, # RUF013 +158 | arg3: Literal[1, 2, 3] = None, # RUF013 | = help: Convert to `T | None` @@ -249,19 +249,19 @@ RUF013_0.py:156:11: RUF013 [*] PEP 484 prohibits implicit `Optional` 153 153 | 154 154 | 155 155 | def f( -156 |- arg1: int = None, # RUF011 - 156 |+ arg1: int | None = None, # RUF011 -157 157 | arg2: Union[int, float] = None, # RUF011 -158 158 | arg3: Literal[1, 2, 3] = None, # RUF011 +156 |- arg1: int = None, # RUF013 + 156 |+ arg1: int | None = None, # RUF013 +157 157 | arg2: Union[int, float] = None, # RUF013 +158 158 | arg3: Literal[1, 2, 3] = None, # RUF013 159 159 | ): RUF013_0.py:157:11: RUF013 [*] PEP 484 prohibits implicit `Optional` | 155 | def f( -156 | arg1: int = None, # RUF011 -157 | arg2: Union[int, float] = None, # RUF011 +156 | arg1: int = None, # RUF013 +157 | arg2: Union[int, float] = None, # RUF013 | ^^^^^^^^^^^^^^^^^ RUF013 -158 | arg3: Literal[1, 2, 3] = None, # RUF011 +158 | arg3: Literal[1, 2, 3] = None, # RUF013 159 | ): | = help: Convert to `T | None` @@ -269,18 +269,18 @@ RUF013_0.py:157:11: RUF013 [*] PEP 484 prohibits implicit `Optional` ℹ Suggested fix 154 154 | 155 155 | def f( -156 156 | arg1: int = None, # RUF011 -157 |- arg2: Union[int, float] = None, # RUF011 - 157 |+ arg2: Union[int, float] | None = None, # RUF011 -158 158 | arg3: Literal[1, 2, 3] = None, # RUF011 +156 156 | arg1: int = None, # RUF013 +157 |- arg2: Union[int, float] = None, # RUF013 + 157 |+ arg2: Union[int, float] | None = None, # RUF013 +158 158 | arg3: Literal[1, 2, 3] = None, # RUF013 159 159 | ): 160 160 | pass RUF013_0.py:158:11: RUF013 [*] PEP 484 prohibits implicit `Optional` | -156 | arg1: int = None, # RUF011 -157 | arg2: Union[int, float] = None, # RUF011 -158 | arg3: Literal[1, 2, 3] = None, # RUF011 +156 | arg1: int = None, # RUF013 +157 | arg2: Union[int, float] = None, # RUF013 +158 | arg3: Literal[1, 2, 3] = None, # RUF013 | ^^^^^^^^^^^^^^^^ RUF013 159 | ): 160 | pass @@ -289,17 +289,17 @@ RUF013_0.py:158:11: RUF013 [*] PEP 484 prohibits implicit `Optional` ℹ Suggested fix 155 155 | def f( -156 156 | arg1: int = None, # RUF011 -157 157 | arg2: Union[int, float] = None, # RUF011 -158 |- arg3: Literal[1, 2, 3] = None, # RUF011 - 158 |+ arg3: Literal[1, 2, 3] | None = None, # RUF011 +156 156 | arg1: int = None, # RUF013 +157 157 | arg2: Union[int, float] = None, # RUF013 +158 |- arg3: Literal[1, 2, 3] = None, # RUF013 + 158 |+ arg3: Literal[1, 2, 3] | None = None, # RUF013 159 159 | ): 160 160 | pass 161 161 | RUF013_0.py:186:12: RUF013 [*] PEP 484 prohibits implicit `Optional` | -186 | def f(arg: Union[Annotated[int, ...], Union[str, bytes]] = None): # RUF011 +186 | def f(arg: Union[Annotated[int, ...], Union[str, bytes]] = None): # RUF013 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ RUF013 187 | pass | @@ -309,8 +309,54 @@ RUF013_0.py:186:12: RUF013 [*] PEP 484 prohibits implicit `Optional` 183 183 | pass 184 184 | 185 185 | -186 |-def f(arg: Union[Annotated[int, ...], Union[str, bytes]] = None): # RUF011 - 186 |+def f(arg: Union[Annotated[int, ...], Union[str, bytes]] | None = None): # RUF011 +186 |-def f(arg: Union[Annotated[int, ...], Union[str, bytes]] = None): # RUF013 + 186 |+def f(arg: Union[Annotated[int, ...], Union[str, bytes]] | None = None): # RUF013 187 187 | pass +188 188 | +189 189 | + +RUF013_0.py:193:13: RUF013 [*] PEP 484 prohibits implicit `Optional` + | +193 | def f(arg: "int" = None): # RUF013 + | ^^^ RUF013 +194 | pass + | + = help: Convert to `T | None` + +ℹ Suggested fix +190 190 | # Quoted +191 191 | +192 192 | +193 |-def f(arg: "int" = None): # RUF013 + 193 |+def f(arg: "int | None" = None): # RUF013 +194 194 | pass +195 195 | +196 196 | + +RUF013_0.py:197:13: RUF013 [*] PEP 484 prohibits implicit `Optional` + | +197 | def f(arg: "str" = None): # RUF013 + | ^^^ RUF013 +198 | pass + | + = help: Convert to `T | None` + +ℹ Suggested fix +194 194 | pass +195 195 | +196 196 | +197 |-def f(arg: "str" = None): # RUF013 + 197 |+def f(arg: "str | None" = None): # RUF013 +198 198 | pass +199 199 | +200 200 | + +RUF013_0.py:201:12: RUF013 PEP 484 prohibits implicit `Optional` + | +201 | def f(arg: "st" "r" = None): # RUF013 + | ^^^^^^^^ RUF013 +202 | pass + | + = help: Convert to `T | None`