From cb827d447484edb9896362bce03fa09182644a45 Mon Sep 17 00:00:00 2001 From: cake-monotone Date: Thu, 18 Jul 2024 02:01:40 +0900 Subject: [PATCH 1/4] feat: Implement new rule UP043 for unnecessary default type arguments --- .../test/fixtures/pyupgrade/UP043.py | 41 +++++ .../src/checkers/ast/analyze/expression.rs | 6 + crates/ruff_linter/src/codes.rs | 1 + crates/ruff_linter/src/rules/pyupgrade/mod.rs | 1 + .../src/rules/pyupgrade/rules/mod.rs | 2 + .../rules/unnecessary_default_type_args.rs | 161 ++++++++++++++++++ ...er__rules__pyupgrade__tests__UP043.py.snap | 75 ++++++++ ruff.schema.json | 1 + 8 files changed, 288 insertions(+) create mode 100644 crates/ruff_linter/resources/test/fixtures/pyupgrade/UP043.py create mode 100644 crates/ruff_linter/src/rules/pyupgrade/rules/unnecessary_default_type_args.rs create mode 100644 crates/ruff_linter/src/rules/pyupgrade/snapshots/ruff_linter__rules__pyupgrade__tests__UP043.py.snap diff --git a/crates/ruff_linter/resources/test/fixtures/pyupgrade/UP043.py b/crates/ruff_linter/resources/test/fixtures/pyupgrade/UP043.py new file mode 100644 index 0000000000000..c4ebf662a67cd --- /dev/null +++ b/crates/ruff_linter/resources/test/fixtures/pyupgrade/UP043.py @@ -0,0 +1,41 @@ +from typing import Generator, AsyncGenerator + + +def func() -> Generator[int, None, None]: + yield 42 + + +def func() -> Generator[int, None]: + yield 42 + + +def func() -> Generator[int]: + yield 42 + + +def func() -> Generator[int, int, int]: + foo = yield 42 + return foo + + +def func() -> Generator[int, int, None]: + _ = yield 42 + return None + + +def func() -> Generator[int, None, int]: + yield 42 + return 42 + + +async def func() -> AsyncGenerator[int, None]: + yield 42 + + +async def func() -> AsyncGenerator[int]: + yield 42 + + +async def func() -> AsyncGenerator[int, int]: + foo = yield 42 + return foo diff --git a/crates/ruff_linter/src/checkers/ast/analyze/expression.rs b/crates/ruff_linter/src/checkers/ast/analyze/expression.rs index fae75a22d131d..5536a58a54cc0 100644 --- a/crates/ruff_linter/src/checkers/ast/analyze/expression.rs +++ b/crates/ruff_linter/src/checkers/ast/analyze/expression.rs @@ -110,6 +110,12 @@ pub(crate) fn expression(expr: &Expr, checker: &mut Checker) { ruff::rules::never_union(checker, expr); } + if checker.enabled(Rule::UnnecessaryDefaultTypeArgs) { + if checker.settings.target_version >= PythonVersion::Py313 { + pyupgrade::rules::unnecessary_default_type_args(checker, expr); + } + } + if checker.any_enabled(&[ Rule::SysVersionSlice3, Rule::SysVersion2, diff --git a/crates/ruff_linter/src/codes.rs b/crates/ruff_linter/src/codes.rs index ce31b13908671..08cbbc174f029 100644 --- a/crates/ruff_linter/src/codes.rs +++ b/crates/ruff_linter/src/codes.rs @@ -521,6 +521,7 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> { (Pyupgrade, "040") => (RuleGroup::Stable, rules::pyupgrade::rules::NonPEP695TypeAlias), (Pyupgrade, "041") => (RuleGroup::Stable, rules::pyupgrade::rules::TimeoutErrorAlias), (Pyupgrade, "042") => (RuleGroup::Preview, rules::pyupgrade::rules::ReplaceStrEnum), + (Pyupgrade, "043") => (RuleGroup::Preview, rules::pyupgrade::rules::UnnecessaryDefaultTypeArgs), // pydocstyle (Pydocstyle, "100") => (RuleGroup::Stable, rules::pydocstyle::rules::UndocumentedPublicModule), diff --git a/crates/ruff_linter/src/rules/pyupgrade/mod.rs b/crates/ruff_linter/src/rules/pyupgrade/mod.rs index 8b09cd22cb60a..12577c87de1e7 100644 --- a/crates/ruff_linter/src/rules/pyupgrade/mod.rs +++ b/crates/ruff_linter/src/rules/pyupgrade/mod.rs @@ -80,6 +80,7 @@ mod tests { #[test_case(Rule::UnicodeKindPrefix, Path::new("UP025.py"))] #[test_case(Rule::UnnecessaryBuiltinImport, Path::new("UP029.py"))] #[test_case(Rule::UnnecessaryClassParentheses, Path::new("UP039.py"))] + #[test_case(Rule::UnnecessaryDefaultTypeArgs, Path::new("UP043.py"))] #[test_case(Rule::UnnecessaryEncodeUTF8, Path::new("UP012.py"))] #[test_case(Rule::UnnecessaryFutureImport, Path::new("UP010.py"))] #[test_case(Rule::UnpackedListComprehension, Path::new("UP027.py"))] diff --git a/crates/ruff_linter/src/rules/pyupgrade/rules/mod.rs b/crates/ruff_linter/src/rules/pyupgrade/rules/mod.rs index 3b7928f6e9020..a3dbd706bf516 100644 --- a/crates/ruff_linter/src/rules/pyupgrade/rules/mod.rs +++ b/crates/ruff_linter/src/rules/pyupgrade/rules/mod.rs @@ -28,6 +28,7 @@ pub(crate) use unicode_kind_prefix::*; pub(crate) use unnecessary_builtin_import::*; pub(crate) use unnecessary_class_parentheses::*; pub(crate) use unnecessary_coding_comment::*; +pub(crate) use unnecessary_default_type_args::*; pub(crate) use unnecessary_encode_utf8::*; pub(crate) use unnecessary_future_import::*; pub(crate) use unpacked_list_comprehension::*; @@ -69,6 +70,7 @@ mod unicode_kind_prefix; mod unnecessary_builtin_import; mod unnecessary_class_parentheses; mod unnecessary_coding_comment; +mod unnecessary_default_type_args; mod unnecessary_encode_utf8; mod unnecessary_future_import; mod unpacked_list_comprehension; diff --git a/crates/ruff_linter/src/rules/pyupgrade/rules/unnecessary_default_type_args.rs b/crates/ruff_linter/src/rules/pyupgrade/rules/unnecessary_default_type_args.rs new file mode 100644 index 0000000000000..a3a5be0a38bcd --- /dev/null +++ b/crates/ruff_linter/src/rules/pyupgrade/rules/unnecessary_default_type_args.rs @@ -0,0 +1,161 @@ +use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Edit, Fix}; +use ruff_macros::{derive_message_formats, violation}; +use ruff_python_ast::{self as ast, Expr}; +use ruff_text_size::{Ranged, TextRange}; + +use crate::checkers::ast::Checker; + +/// ## What it does +/// Checks for unnecessary default type arguments. +/// +/// ## Why is this bad? +/// Python 3.13 introduced a new feature: type defaults for type parameters. +/// Now, you can omit default type arguments for some types in the standard library (e.g., Generator, AsyncGenerator). +/// Including unnecessary default type arguments can make the code more verbose and less readable. +/// +/// ## Examples +/// ```python +/// from typing import Generator, AsyncGenerator +/// +/// def sync_gen() -> Generator[int, None, None]: +/// yield 42 +/// +/// async def async_gen() -> AsyncGenerator[int, None]: +/// yield 42 +/// ``` +/// +/// Use instead: +/// ```python +/// from typing import Generator, AsyncGenerator +/// +/// def sync_gen() -> Generator[int]: +/// yield 42 +/// +/// async def async_gen() -> AsyncGenerator[int]: +/// yield 42 +/// ``` +/// +/// ## References +/// - [PEP 696 – Type Defaults for Type Parameters](https://peps.python.org/pep-0696/) +/// - [typing.Generator](https://docs.python.org/3.13/library/typing.html#typing.Generator) +/// - [typing.AsyncGenerator](https://docs.python.org/3.13/library/typing.html#typing.AsyncGenerator) +#[violation] +pub struct UnnecessaryDefaultTypeArgs; + +impl AlwaysFixableViolation for UnnecessaryDefaultTypeArgs { + #[derive_message_formats] + fn message(&self) -> String { + format!("Unnecessary default type arguments") + } + + fn fix_title(&self) -> String { + format!("Remove unnecessary default type arguments") + } +} + +// UP043 +pub(crate) fn unnecessary_default_type_args(checker: &mut Checker, expr: &Expr) { + if let Expr::Subscript(ast::ExprSubscript { value, slice, .. }) = expr { + // Check if the type annotation is Generator or AsyncGenerator. + let Some(type_annotation) = DefaultedTypeAnnotation::from_expr(value, checker.semantic()) + else { + return; + }; + + let Expr::Tuple(ast::ExprTuple { + elts, + ctx: _, + range: _, + parenthesized: _, + }) = slice.as_ref() + else { + return; + }; + + let valid_elts = type_annotation.trim_unnecessary_defaults(elts[..].as_ref()); + + if *elts == valid_elts { + return; + } + + let mut diagnostic = Diagnostic::new(UnnecessaryDefaultTypeArgs, expr.range()); + + diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement( + checker + .generator() + .expr(&Expr::Subscript(ast::ExprSubscript { + value: value.clone(), + slice: Box::new(if valid_elts.len() == 1 { + valid_elts[0].clone() + } else { + Expr::Tuple(ast::ExprTuple { + elts: valid_elts, + ctx: ast::ExprContext::Load, + range: TextRange::default(), + parenthesized: true, + }) + }), + ctx: ast::ExprContext::Load, + range: TextRange::default(), + })), + expr.range(), + ))); + checker.diagnostics.push(diagnostic); + } +} + +fn trim_trailing_none(elts: &[Expr]) -> &[Expr] { + // Trim trailing None literals. + // e.g. [int, None, None] -> [int] + match elts.iter().rposition(|elt| !elt.is_none_literal_expr()) { + Some(trimmed_last_index) => elts[..=trimmed_last_index].as_ref(), + None => &[], + } +} + +// Type annotations affected by the new feature(type defaults). +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +enum DefaultedTypeAnnotation { + Generator, // typing.Generator[YieldType, SendType = None, ReturnType = None] + AsyncGenerator, // typing.AsyncGenerator[YieldType, SendType = None] +} + +impl DefaultedTypeAnnotation { + fn from_expr(expr: &Expr, semantic: &ruff_python_semantic::SemanticModel) -> Option { + let qualified_name = semantic.resolve_qualified_name(expr)?; + if semantic.match_typing_qualified_name(&qualified_name, "Generator") { + Some(DefaultedTypeAnnotation::Generator) + } else if semantic.match_typing_qualified_name(&qualified_name, "AsyncGenerator") { + Some(DefaultedTypeAnnotation::AsyncGenerator) + } else { + None + } + } + + fn trim_unnecessary_defaults(self, elts: &[Expr]) -> Vec { + match self { + DefaultedTypeAnnotation::Generator => { + // Check only if the number of elements is 2 or 3 (e.g. Generator[int, None] or Generator[int, None, None]). + // Ignore otherwise (e.g. Generator[], Generator[int], Generator[int, None, None, None]) + if !(2 <= elts.len() && elts.len() <= 3) { + return elts.to_vec(); + } + + std::iter::once(elts[0].clone()) + .chain(trim_trailing_none(&elts[1..]).iter().cloned()) + .collect::>() + } + DefaultedTypeAnnotation::AsyncGenerator => { + // Check only if the number of elements is 2 (e.g. AsyncGenerator[int, None]). + // Ignore otherwise (e.g. AsyncGenerator[], AsyncGenerator[int], AsyncGenerator[int, None, None]) + if elts.len() != 2 { + return elts.to_vec(); + } + + std::iter::once(elts[0].clone()) + .chain(trim_trailing_none(&elts[1..]).iter().cloned()) + .collect::>() + } + } + } +} diff --git a/crates/ruff_linter/src/rules/pyupgrade/snapshots/ruff_linter__rules__pyupgrade__tests__UP043.py.snap b/crates/ruff_linter/src/rules/pyupgrade/snapshots/ruff_linter__rules__pyupgrade__tests__UP043.py.snap new file mode 100644 index 0000000000000..8dc555c66de75 --- /dev/null +++ b/crates/ruff_linter/src/rules/pyupgrade/snapshots/ruff_linter__rules__pyupgrade__tests__UP043.py.snap @@ -0,0 +1,75 @@ +--- +source: crates/ruff_linter/src/rules/pyupgrade/mod.rs +--- +UP043.py:4:15: UP043 [*] Unnecessary default type arguments + | +4 | def func() -> Generator[int, None, None]: + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ UP043 +5 | yield 42 + | + = help: Remove unnecessary default type arguments + +ℹ Safe fix +1 1 | from typing import Generator, AsyncGenerator +2 2 | +3 3 | +4 |-def func() -> Generator[int, None, None]: + 4 |+def func() -> Generator[int]: +5 5 | yield 42 +6 6 | +7 7 | + +UP043.py:8:15: UP043 [*] Unnecessary default type arguments + | +8 | def func() -> Generator[int, None]: + | ^^^^^^^^^^^^^^^^^^^^ UP043 +9 | yield 42 + | + = help: Remove unnecessary default type arguments + +ℹ Safe fix +5 5 | yield 42 +6 6 | +7 7 | +8 |-def func() -> Generator[int, None]: + 8 |+def func() -> Generator[int]: +9 9 | yield 42 +10 10 | +11 11 | + +UP043.py:21:15: UP043 [*] Unnecessary default type arguments + | +21 | def func() -> Generator[int, int, None]: + | ^^^^^^^^^^^^^^^^^^^^^^^^^ UP043 +22 | _ = yield 42 +23 | return None + | + = help: Remove unnecessary default type arguments + +ℹ Safe fix +18 18 | return foo +19 19 | +20 20 | +21 |-def func() -> Generator[int, int, None]: + 21 |+def func() -> Generator[int, int]: +22 22 | _ = yield 42 +23 23 | return None +24 24 | + +UP043.py:31:21: UP043 [*] Unnecessary default type arguments + | +31 | async def func() -> AsyncGenerator[int, None]: + | ^^^^^^^^^^^^^^^^^^^^^^^^^ UP043 +32 | yield 42 + | + = help: Remove unnecessary default type arguments + +ℹ Safe fix +28 28 | return 42 +29 29 | +30 30 | +31 |-async def func() -> AsyncGenerator[int, None]: + 31 |+async def func() -> AsyncGenerator[int]: +32 32 | yield 42 +33 33 | +34 34 | diff --git a/ruff.schema.json b/ruff.schema.json index ab7bad65446f5..2114f20e64318 100644 --- a/ruff.schema.json +++ b/ruff.schema.json @@ -3927,6 +3927,7 @@ "UP040", "UP041", "UP042", + "UP043", "W", "W1", "W19", From ac5e3c1d02ec46aff43422a4dc80be79584bc90b Mon Sep 17 00:00:00 2001 From: cake-monotone Date: Thu, 18 Jul 2024 02:40:09 +0900 Subject: [PATCH 2/4] fix: wrong formatted mkdoc for UP043 --- .../rules/pyupgrade/rules/unnecessary_default_type_args.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/crates/ruff_linter/src/rules/pyupgrade/rules/unnecessary_default_type_args.rs b/crates/ruff_linter/src/rules/pyupgrade/rules/unnecessary_default_type_args.rs index a3a5be0a38bcd..0f518ed735dec 100644 --- a/crates/ruff_linter/src/rules/pyupgrade/rules/unnecessary_default_type_args.rs +++ b/crates/ruff_linter/src/rules/pyupgrade/rules/unnecessary_default_type_args.rs @@ -14,28 +14,35 @@ use crate::checkers::ast::Checker; /// Including unnecessary default type arguments can make the code more verbose and less readable. /// /// ## Examples +/// /// ```python /// from typing import Generator, AsyncGenerator /// +/// /// def sync_gen() -> Generator[int, None, None]: /// yield 42 /// +/// /// async def async_gen() -> AsyncGenerator[int, None]: /// yield 42 /// ``` /// /// Use instead: +/// /// ```python /// from typing import Generator, AsyncGenerator /// +/// /// def sync_gen() -> Generator[int]: /// yield 42 /// +/// /// async def async_gen() -> AsyncGenerator[int]: /// yield 42 /// ``` /// /// ## References +/// /// - [PEP 696 – Type Defaults for Type Parameters](https://peps.python.org/pep-0696/) /// - [typing.Generator](https://docs.python.org/3.13/library/typing.html#typing.Generator) /// - [typing.AsyncGenerator](https://docs.python.org/3.13/library/typing.html#typing.AsyncGenerator) From 5c2b4146823db656c22593d90f3568306f8488e5 Mon Sep 17 00:00:00 2001 From: Charlie Marsh Date: Wed, 17 Jul 2024 15:39:33 -0400 Subject: [PATCH 3/4] Minor tweaks --- .../rules/unnecessary_default_type_args.rs | 141 ++++++++++-------- ...er__rules__pyupgrade__tests__UP043.py.snap | 8 +- 2 files changed, 80 insertions(+), 69 deletions(-) diff --git a/crates/ruff_linter/src/rules/pyupgrade/rules/unnecessary_default_type_args.rs b/crates/ruff_linter/src/rules/pyupgrade/rules/unnecessary_default_type_args.rs index 0f518ed735dec..363df668a7140 100644 --- a/crates/ruff_linter/src/rules/pyupgrade/rules/unnecessary_default_type_args.rs +++ b/crates/ruff_linter/src/rules/pyupgrade/rules/unnecessary_default_type_args.rs @@ -9,9 +9,12 @@ use crate::checkers::ast::Checker; /// Checks for unnecessary default type arguments. /// /// ## Why is this bad? -/// Python 3.13 introduced a new feature: type defaults for type parameters. -/// Now, you can omit default type arguments for some types in the standard library (e.g., Generator, AsyncGenerator). -/// Including unnecessary default type arguments can make the code more verbose and less readable. +/// Python 3.13 introduced the ability for type parameters to specify default +/// values. As such, the default type arguments for some types in the standard +/// library (e.g., Generator, AsyncGenerator) are now optional. +/// +/// Omitting type parameters that match the default values can make the code +/// more concise and easier to read. /// /// ## Examples /// @@ -56,95 +59,103 @@ impl AlwaysFixableViolation for UnnecessaryDefaultTypeArgs { } fn fix_title(&self) -> String { - format!("Remove unnecessary default type arguments") + format!("Remove default type arguments") } } -// UP043 +/// UP043 pub(crate) fn unnecessary_default_type_args(checker: &mut Checker, expr: &Expr) { - if let Expr::Subscript(ast::ExprSubscript { value, slice, .. }) = expr { - // Check if the type annotation is Generator or AsyncGenerator. - let Some(type_annotation) = DefaultedTypeAnnotation::from_expr(value, checker.semantic()) - else { - return; - }; - - let Expr::Tuple(ast::ExprTuple { - elts, - ctx: _, - range: _, - parenthesized: _, - }) = slice.as_ref() - else { - return; - }; - - let valid_elts = type_annotation.trim_unnecessary_defaults(elts[..].as_ref()); - - if *elts == valid_elts { - return; - } - - let mut diagnostic = Diagnostic::new(UnnecessaryDefaultTypeArgs, expr.range()); - - diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement( - checker - .generator() - .expr(&Expr::Subscript(ast::ExprSubscript { - value: value.clone(), - slice: Box::new(if valid_elts.len() == 1 { - valid_elts[0].clone() - } else { - Expr::Tuple(ast::ExprTuple { - elts: valid_elts, - ctx: ast::ExprContext::Load, - range: TextRange::default(), - parenthesized: true, - }) - }), - ctx: ast::ExprContext::Load, - range: TextRange::default(), - })), - expr.range(), - ))); - checker.diagnostics.push(diagnostic); + let Expr::Subscript(ast::ExprSubscript { value, slice, .. }) = expr else { + return; + }; + + let Expr::Tuple(ast::ExprTuple { + elts, + ctx: _, + range: _, + parenthesized: _, + }) = slice.as_ref() + else { + return; + }; + + // The type annotation must be `Generator` or `AsyncGenerator`. + let Some(type_annotation) = DefaultedTypeAnnotation::from_expr(value, checker.semantic()) + else { + return; + }; + + let valid_elts = type_annotation.trim_unnecessary_defaults(&elts); + + // If we didn't trim any elements, then the default type arguments are necessary. + if *elts == valid_elts { + return; } + + let mut diagnostic = Diagnostic::new(UnnecessaryDefaultTypeArgs, expr.range()); + diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement( + checker + .generator() + .expr(&Expr::Subscript(ast::ExprSubscript { + value: value.clone(), + slice: Box::new(if let [elt] = valid_elts.as_slice() { + elt.clone() + } else { + Expr::Tuple(ast::ExprTuple { + elts: valid_elts, + ctx: ast::ExprContext::Load, + range: TextRange::default(), + parenthesized: true, + }) + }), + ctx: ast::ExprContext::Load, + range: TextRange::default(), + })), + expr.range(), + ))); + checker.diagnostics.push(diagnostic); } +/// Trim trailing `None` literals from the given elements. +/// +/// For example, given `[int, None, None]`, return `[int]`. fn trim_trailing_none(elts: &[Expr]) -> &[Expr] { - // Trim trailing None literals. - // e.g. [int, None, None] -> [int] match elts.iter().rposition(|elt| !elt.is_none_literal_expr()) { Some(trimmed_last_index) => elts[..=trimmed_last_index].as_ref(), None => &[], } } -// Type annotations affected by the new feature(type defaults). +/// Type annotations that include default type arguments as of Python 3.13. #[derive(Debug, Clone, Copy, PartialEq, Eq)] enum DefaultedTypeAnnotation { - Generator, // typing.Generator[YieldType, SendType = None, ReturnType = None] - AsyncGenerator, // typing.AsyncGenerator[YieldType, SendType = None] + /// `typing.Generator[YieldType, SendType = None, ReturnType = None]` + Generator, + /// `typing.AsyncGenerator[YieldType, SendType = None]` + AsyncGenerator, } impl DefaultedTypeAnnotation { + /// Returns the [`DefaultedTypeAnnotation`], if the given expression is a type annotation that + /// includes default type arguments. fn from_expr(expr: &Expr, semantic: &ruff_python_semantic::SemanticModel) -> Option { let qualified_name = semantic.resolve_qualified_name(expr)?; if semantic.match_typing_qualified_name(&qualified_name, "Generator") { - Some(DefaultedTypeAnnotation::Generator) + Some(Self::Generator) } else if semantic.match_typing_qualified_name(&qualified_name, "AsyncGenerator") { - Some(DefaultedTypeAnnotation::AsyncGenerator) + Some(Self::AsyncGenerator) } else { None } } + /// Trim any unnecessary default type arguments from the given elements. fn trim_unnecessary_defaults(self, elts: &[Expr]) -> Vec { match self { - DefaultedTypeAnnotation::Generator => { - // Check only if the number of elements is 2 or 3 (e.g. Generator[int, None] or Generator[int, None, None]). - // Ignore otherwise (e.g. Generator[], Generator[int], Generator[int, None, None, None]) - if !(2 <= elts.len() && elts.len() <= 3) { + Self::Generator => { + // Check only if the number of elements is 2 or 3 (e.g., `Generator[int, None]` or `Generator[int, None, None]`). + // Otherwise, ignore (e.g., `Generator[]`, `Generator[int]`, `Generator[int, None, None, None]`) + if elts.len() != 2 && elts.len() != 3 { return elts.to_vec(); } @@ -152,9 +163,9 @@ impl DefaultedTypeAnnotation { .chain(trim_trailing_none(&elts[1..]).iter().cloned()) .collect::>() } - DefaultedTypeAnnotation::AsyncGenerator => { - // Check only if the number of elements is 2 (e.g. AsyncGenerator[int, None]). - // Ignore otherwise (e.g. AsyncGenerator[], AsyncGenerator[int], AsyncGenerator[int, None, None]) + Self::AsyncGenerator => { + // Check only if the number of elements is 2 (e.g., `AsyncGenerator[int, None]`). + // Otherwise, ignore (e.g., `AsyncGenerator[]`, `AsyncGenerator[int]`, `AsyncGenerator[int, None, None]`) if elts.len() != 2 { return elts.to_vec(); } diff --git a/crates/ruff_linter/src/rules/pyupgrade/snapshots/ruff_linter__rules__pyupgrade__tests__UP043.py.snap b/crates/ruff_linter/src/rules/pyupgrade/snapshots/ruff_linter__rules__pyupgrade__tests__UP043.py.snap index 8dc555c66de75..4198822ad3363 100644 --- a/crates/ruff_linter/src/rules/pyupgrade/snapshots/ruff_linter__rules__pyupgrade__tests__UP043.py.snap +++ b/crates/ruff_linter/src/rules/pyupgrade/snapshots/ruff_linter__rules__pyupgrade__tests__UP043.py.snap @@ -7,7 +7,7 @@ UP043.py:4:15: UP043 [*] Unnecessary default type arguments | ^^^^^^^^^^^^^^^^^^^^^^^^^^ UP043 5 | yield 42 | - = help: Remove unnecessary default type arguments + = help: Remove default type arguments ℹ Safe fix 1 1 | from typing import Generator, AsyncGenerator @@ -25,7 +25,7 @@ UP043.py:8:15: UP043 [*] Unnecessary default type arguments | ^^^^^^^^^^^^^^^^^^^^ UP043 9 | yield 42 | - = help: Remove unnecessary default type arguments + = help: Remove default type arguments ℹ Safe fix 5 5 | yield 42 @@ -44,7 +44,7 @@ UP043.py:21:15: UP043 [*] Unnecessary default type arguments 22 | _ = yield 42 23 | return None | - = help: Remove unnecessary default type arguments + = help: Remove default type arguments ℹ Safe fix 18 18 | return foo @@ -62,7 +62,7 @@ UP043.py:31:21: UP043 [*] Unnecessary default type arguments | ^^^^^^^^^^^^^^^^^^^^^^^^^ UP043 32 | yield 42 | - = help: Remove unnecessary default type arguments + = help: Remove default type arguments ℹ Safe fix 28 28 | return 42 From d53bbd450402a271f8d9a1cee441a341c2db39d0 Mon Sep 17 00:00:00 2001 From: Charlie Marsh Date: Wed, 17 Jul 2024 15:41:18 -0400 Subject: [PATCH 4/4] Clippy --- .../src/rules/pyupgrade/rules/unnecessary_default_type_args.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/ruff_linter/src/rules/pyupgrade/rules/unnecessary_default_type_args.rs b/crates/ruff_linter/src/rules/pyupgrade/rules/unnecessary_default_type_args.rs index 363df668a7140..8349eae78fce0 100644 --- a/crates/ruff_linter/src/rules/pyupgrade/rules/unnecessary_default_type_args.rs +++ b/crates/ruff_linter/src/rules/pyupgrade/rules/unnecessary_default_type_args.rs @@ -85,7 +85,7 @@ pub(crate) fn unnecessary_default_type_args(checker: &mut Checker, expr: &Expr) return; }; - let valid_elts = type_annotation.trim_unnecessary_defaults(&elts); + let valid_elts = type_annotation.trim_unnecessary_defaults(elts); // If we didn't trim any elements, then the default type arguments are necessary. if *elts == valid_elts {