From a256fdb9f4cc6ed4ac1a8b1656c65665e6d540a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ho=C3=ABl=20Bagard?= <34478245+hoel-bagard@users.noreply.github.com> Date: Wed, 24 May 2023 10:26:34 +0900 Subject: [PATCH] Extend `RUF005` to recursive and literal-literal concatenations (#4557) --- .../resources/test/fixtures/ruff/RUF005.py | 68 ++- .../rules/collection_literal_concatenation.rs | 165 +++--- ..._rules__ruff__tests__RUF005_RUF005.py.snap | 508 ++++++++++-------- 3 files changed, 431 insertions(+), 310 deletions(-) diff --git a/crates/ruff/resources/test/fixtures/ruff/RUF005.py b/crates/ruff/resources/test/fixtures/ruff/RUF005.py index e8ecc06b16e61..2007e47a2e5e8 100644 --- a/crates/ruff/resources/test/fixtures/ruff/RUF005.py +++ b/crates/ruff/resources/test/fixtures/ruff/RUF005.py @@ -1,9 +1,38 @@ +### +# Non-fixable Errors. +### +foo + [ # This will be preserved. +] +[*foo] + [ # This will be preserved. +] +first = [ + # The order + 1, # here + 2, # is + # extremely + 3, # critical + # to preserve +] +second = first + [ + # please + 4, + # don't + 5, + # touch + 6, +] + + +### +# Fixable errors. +### class Fun: words = ("how", "fun!") def yay(self): return self.words + yay = Fun().yay foo = [4, 5, 6] @@ -13,36 +42,27 @@ def yay(self): spam = quux + (10, 11, 12) spom = list(spam) eggs = spom + [13, 14, 15] -elatement = ("we all say", ) + yay() -excitement = ("we all think", ) + Fun().yay() -astonishment = ("we all feel", ) + Fun.words +elatement = ("we all say",) + yay() +excitement = ("we all think",) + Fun().yay() +astonishment = ("we all feel",) + Fun.words -chain = ['a', 'b', 'c'] + eggs + list(('yes', 'no', 'pants') + zoob) +chain = ["a", "b", "c"] + eggs + list(("yes", "no", "pants") + zoob) baz = () + zoob -first = [ - # The order - 1, # here - 2, # is - # extremely - 3, # critical - # to preserve -] -second = first + [ - # please - 4, - # don't - 5, - # touch - 6, -] - [] + foo + [ ] -[] + foo + [ # This will be preserved, but doesn't prevent the fix -] +pylint_call = [sys.executable, "-m", "pylint"] + args + [path] +pylint_call_tuple = (sys.executable, "-m", "pylint") + args + (path, path2) +b = a + [2, 3] + [4] # Uses the non-preferred quote style, which should be retained. -f"{[*a(), 'b']}" +f"{a() + ['b']}" + +### +# Non-errors. +### +a = (1,) + [2] +a = [1, 2] + (3, 4) +a = ([1, 2, 3] + b) + (4, 5, 6) diff --git a/crates/ruff/src/rules/ruff/rules/collection_literal_concatenation.rs b/crates/ruff/src/rules/ruff/rules/collection_literal_concatenation.rs index 4f868b30838e6..c5b2196d53ef9 100644 --- a/crates/ruff/src/rules/ruff/rules/collection_literal_concatenation.rs +++ b/crates/ruff/src/rules/ruff/rules/collection_literal_concatenation.rs @@ -49,92 +49,106 @@ fn make_splat_elts( } #[derive(Debug, Copy, Clone)] -enum Kind { +enum Type { List, Tuple, } -/// RUF005 -/// This suggestion could be unsafe if the non-literal expression in the -/// expression has overridden the `__add__` (or `__radd__`) magic methods. -pub(crate) fn collection_literal_concatenation(checker: &mut Checker, expr: &Expr) { +/// Recursively merge all the tuples and lists in the expression. +fn concatenate_expressions(expr: &Expr) -> Option<(Expr, Type)> { let Expr::BinOp(ast::ExprBinOp { left, op: Operator::Add, right, range: _ }) = expr else { - return; + return None; }; - // Figure out which way the splat is, and what the kind of the collection is. - let (kind, splat_element, other_elements, splat_at_left, ctx) = - match (left.as_ref(), right.as_ref()) { - ( - Expr::List(ast::ExprList { - elts: l_elts, - ctx, - range: _, - }), - _, - ) => (Kind::List, right, l_elts, false, ctx), - ( - Expr::Tuple(ast::ExprTuple { - elts: l_elts, - ctx, - range: _, - }), - _, - ) => (Kind::Tuple, right, l_elts, false, ctx), - ( - _, - Expr::List(ast::ExprList { - elts: r_elts, - ctx, - range: _, - }), - ) => (Kind::List, left, r_elts, true, ctx), - ( - _, - Expr::Tuple(ast::ExprTuple { - elts: r_elts, - ctx, - range: _, - }), - ) => (Kind::Tuple, left, r_elts, true, ctx), - _ => return, - }; - - // We'll be a bit conservative here; only calls, names and attribute accesses - // will be considered as splat elements. - if !(splat_element.is_call_expr() - || splat_element.is_name_expr() - || splat_element.is_attribute_expr()) - { - return; - } + let new_left = match left.as_ref() { + Expr::BinOp(ast::ExprBinOp { .. }) => match concatenate_expressions(left) { + Some((new_left, _)) => new_left, + None => *left.clone(), + }, + _ => *left.clone(), + }; - let new_expr = match kind { - Kind::List => { - let node = ast::ExprList { - elts: make_splat_elts(splat_element, other_elements, splat_at_left), - ctx: *ctx, - range: TextRange::default(), - }; - node.into() + let new_right = match right.as_ref() { + Expr::BinOp(ast::ExprBinOp { .. }) => match concatenate_expressions(right) { + Some((new_right, _)) => new_right, + None => *right.clone(), + }, + _ => *right.clone(), + }; + + // Figure out which way the splat is, and the type of the collection. + let (type_, splat_element, other_elements, splat_at_left) = match (&new_left, &new_right) { + (Expr::List(ast::ExprList { elts: l_elts, .. }), _) => { + (Type::List, &new_right, l_elts, false) + } + (Expr::Tuple(ast::ExprTuple { elts: l_elts, .. }), _) => { + (Type::Tuple, &new_right, l_elts, false) + } + (_, Expr::List(ast::ExprList { elts: r_elts, .. })) => { + (Type::List, &new_left, r_elts, true) } - Kind::Tuple => { - let node = ast::ExprTuple { - elts: make_splat_elts(splat_element, other_elements, splat_at_left), - ctx: *ctx, - range: TextRange::default(), - }; - node.into() + (_, Expr::Tuple(ast::ExprTuple { elts: r_elts, .. })) => { + (Type::Tuple, &new_left, r_elts, true) } + _ => return None, }; - let contents = match kind { - // Wrap the new expression in parentheses if it was a tuple - Kind::Tuple => format!("({})", checker.generator().expr(&new_expr)), - Kind::List => checker.generator().expr(&new_expr), + let new_elts = match splat_element { + // We'll be a bit conservative here; only calls, names and attribute accesses + // will be considered as splat elements. + Expr::Call(_) | Expr::Attribute(_) | Expr::Name(_) => { + make_splat_elts(splat_element, other_elements, splat_at_left) + } + // If the splat element is itself a list/tuple, insert them in the other list/tuple. + Expr::List(ast::ExprList { elts, .. }) if matches!(type_, Type::List) => { + other_elements.iter().chain(elts.iter()).cloned().collect() + } + Expr::Tuple(ast::ExprTuple { elts, .. }) if matches!(type_, Type::Tuple) => { + other_elements.iter().chain(elts.iter()).cloned().collect() + } + _ => return None, }; - let fixable = !has_comments(expr, checker.locator); + let new_expr = match type_ { + Type::List => ast::ExprList { + elts: new_elts, + ctx: ExprContext::Load, + range: TextRange::default(), + } + .into(), + Type::Tuple => ast::ExprTuple { + elts: new_elts, + ctx: ExprContext::Load, + range: TextRange::default(), + } + .into(), + }; + + Some((new_expr, type_)) +} + +/// RUF005 +pub(crate) fn collection_literal_concatenation(checker: &mut Checker, expr: &Expr) { + // If the expression is already a child of an addition, we'll have analyzed it already. + if matches!( + checker.semantic_model().expr_parent(), + Some(Expr::BinOp(ast::ExprBinOp { + op: Operator::Add, + .. + })) + ) { + return; + } + + let Some((new_expr, type_)) = concatenate_expressions(expr) else { + return + }; + + let contents = match type_ { + // Wrap the new expression in parentheses if it was a tuple. + Type::Tuple => format!("({})", checker.generator().expr(&new_expr)), + Type::List => checker.generator().expr(&new_expr), + }; let mut diagnostic = Diagnostic::new( CollectionLiteralConcatenation { expr: contents.clone(), @@ -142,9 +156,10 @@ pub(crate) fn collection_literal_concatenation(checker: &mut Checker, expr: &Exp expr.range(), ); if checker.patch(diagnostic.kind.rule()) { - if fixable { - #[allow(deprecated)] - diagnostic.set_fix(Fix::unspecified(Edit::range_replacement( + if !has_comments(expr, checker.locator) { + // This suggestion could be unsafe if the non-literal expression in the + // expression has overridden the `__add__` (or `__radd__`) magic methods. + diagnostic.set_fix(Fix::suggested(Edit::range_replacement( contents, expr.range(), ))); diff --git a/crates/ruff/src/rules/ruff/snapshots/ruff__rules__ruff__tests__RUF005_RUF005.py.snap b/crates/ruff/src/rules/ruff/snapshots/ruff__rules__ruff__tests__RUF005_RUF005.py.snap index bb508f24fa47e..a2885f683f89c 100644 --- a/crates/ruff/src/rules/ruff/snapshots/ruff__rules__ruff__tests__RUF005_RUF005.py.snap +++ b/crates/ruff/src/rules/ruff/snapshots/ruff__rules__ruff__tests__RUF005_RUF005.py.snap @@ -1,271 +1,357 @@ --- source: crates/ruff/src/rules/ruff/mod.rs --- -RUF005.py:10:7: RUF005 [*] Consider `[1, 2, 3, *foo]` instead of concatenation +RUF005.py:4:1: RUF005 Consider `[*foo]` instead of concatenation + | +4 | # Non-fixable Errors. +5 | ### +6 | / foo + [ # This will be preserved. +7 | | ] + | |_^ RUF005 +8 | [*foo] + [ # This will be preserved. +9 | ] + | + = help: Replace with `[*foo]` + +RUF005.py:6:1: RUF005 Consider `[*foo]` instead of concatenation + | + 6 | foo + [ # This will be preserved. + 7 | ] + 8 | / [*foo] + [ # This will be preserved. + 9 | | ] + | |_^ RUF005 +10 | first = [ +11 | # The order | -10 | foo = [4, 5, 6] -11 | bar = [1, 2, 3] + foo + = help: Replace with `[*foo]` + +RUF005.py:16:10: RUF005 Consider `[*first, 4, 5, 6]` instead of concatenation + | +16 | # to preserve +17 | ] +18 | second = first + [ + | __________^ +19 | | # please +20 | | 4, +21 | | # don't +22 | | 5, +23 | | # touch +24 | | 6, +25 | | ] + | |_^ RUF005 + | + = help: Replace with `[*first, 4, 5, 6]` + +RUF005.py:39:7: RUF005 [*] Consider `[1, 2, 3, *foo]` instead of concatenation + | +39 | foo = [4, 5, 6] +40 | bar = [1, 2, 3] + foo | ^^^^^^^^^^^^^^^ RUF005 -12 | zoob = tuple(bar) -13 | quux = (7, 8, 9) + zoob +41 | zoob = tuple(bar) +42 | quux = (7, 8, 9) + zoob | = help: Replace with `[1, 2, 3, *foo]` ℹ Suggested fix -7 7 | yay = Fun().yay -8 8 | -9 9 | foo = [4, 5, 6] -10 |-bar = [1, 2, 3] + foo - 10 |+bar = [1, 2, 3, *foo] -11 11 | zoob = tuple(bar) -12 12 | quux = (7, 8, 9) + zoob -13 13 | spam = quux + (10, 11, 12) - -RUF005.py:12:8: RUF005 [*] Consider `(7, 8, 9, *zoob)` instead of concatenation - | -12 | bar = [1, 2, 3] + foo -13 | zoob = tuple(bar) -14 | quux = (7, 8, 9) + zoob +36 36 | yay = Fun().yay +37 37 | +38 38 | foo = [4, 5, 6] +39 |-bar = [1, 2, 3] + foo + 39 |+bar = [1, 2, 3, *foo] +40 40 | zoob = tuple(bar) +41 41 | quux = (7, 8, 9) + zoob +42 42 | spam = quux + (10, 11, 12) + +RUF005.py:41:8: RUF005 [*] Consider `(7, 8, 9, *zoob)` instead of concatenation + | +41 | bar = [1, 2, 3] + foo +42 | zoob = tuple(bar) +43 | quux = (7, 8, 9) + zoob | ^^^^^^^^^^^^^^^^ RUF005 -15 | spam = quux + (10, 11, 12) -16 | spom = list(spam) +44 | spam = quux + (10, 11, 12) +45 | spom = list(spam) | = help: Replace with `(7, 8, 9, *zoob)` ℹ Suggested fix -9 9 | foo = [4, 5, 6] -10 10 | bar = [1, 2, 3] + foo -11 11 | zoob = tuple(bar) -12 |-quux = (7, 8, 9) + zoob - 12 |+quux = (7, 8, 9, *zoob) -13 13 | spam = quux + (10, 11, 12) -14 14 | spom = list(spam) -15 15 | eggs = spom + [13, 14, 15] - -RUF005.py:13:8: RUF005 [*] Consider `(*quux, 10, 11, 12)` instead of concatenation - | -13 | zoob = tuple(bar) -14 | quux = (7, 8, 9) + zoob -15 | spam = quux + (10, 11, 12) +38 38 | foo = [4, 5, 6] +39 39 | bar = [1, 2, 3] + foo +40 40 | zoob = tuple(bar) +41 |-quux = (7, 8, 9) + zoob + 41 |+quux = (7, 8, 9, *zoob) +42 42 | spam = quux + (10, 11, 12) +43 43 | spom = list(spam) +44 44 | eggs = spom + [13, 14, 15] + +RUF005.py:42:8: RUF005 [*] Consider `(*quux, 10, 11, 12)` instead of concatenation + | +42 | zoob = tuple(bar) +43 | quux = (7, 8, 9) + zoob +44 | spam = quux + (10, 11, 12) | ^^^^^^^^^^^^^^^^^^^ RUF005 -16 | spom = list(spam) -17 | eggs = spom + [13, 14, 15] +45 | spom = list(spam) +46 | eggs = spom + [13, 14, 15] | = help: Replace with `(*quux, 10, 11, 12)` ℹ Suggested fix -10 10 | bar = [1, 2, 3] + foo -11 11 | zoob = tuple(bar) -12 12 | quux = (7, 8, 9) + zoob -13 |-spam = quux + (10, 11, 12) - 13 |+spam = (*quux, 10, 11, 12) -14 14 | spom = list(spam) -15 15 | eggs = spom + [13, 14, 15] -16 16 | elatement = ("we all say", ) + yay() - -RUF005.py:15:8: RUF005 [*] Consider `[*spom, 13, 14, 15]` instead of concatenation - | -15 | spam = quux + (10, 11, 12) -16 | spom = list(spam) -17 | eggs = spom + [13, 14, 15] +39 39 | bar = [1, 2, 3] + foo +40 40 | zoob = tuple(bar) +41 41 | quux = (7, 8, 9) + zoob +42 |-spam = quux + (10, 11, 12) + 42 |+spam = (*quux, 10, 11, 12) +43 43 | spom = list(spam) +44 44 | eggs = spom + [13, 14, 15] +45 45 | elatement = ("we all say",) + yay() + +RUF005.py:44:8: RUF005 [*] Consider `[*spom, 13, 14, 15]` instead of concatenation + | +44 | spam = quux + (10, 11, 12) +45 | spom = list(spam) +46 | eggs = spom + [13, 14, 15] | ^^^^^^^^^^^^^^^^^^^ RUF005 -18 | elatement = ("we all say", ) + yay() -19 | excitement = ("we all think", ) + Fun().yay() +47 | elatement = ("we all say",) + yay() +48 | excitement = ("we all think",) + Fun().yay() | = help: Replace with `[*spom, 13, 14, 15]` ℹ Suggested fix -12 12 | quux = (7, 8, 9) + zoob -13 13 | spam = quux + (10, 11, 12) -14 14 | spom = list(spam) -15 |-eggs = spom + [13, 14, 15] - 15 |+eggs = [*spom, 13, 14, 15] -16 16 | elatement = ("we all say", ) + yay() -17 17 | excitement = ("we all think", ) + Fun().yay() -18 18 | astonishment = ("we all feel", ) + Fun.words - -RUF005.py:16:13: RUF005 [*] Consider `("we all say", *yay())` instead of concatenation - | -16 | spom = list(spam) -17 | eggs = spom + [13, 14, 15] -18 | elatement = ("we all say", ) + yay() - | ^^^^^^^^^^^^^^^^^^^^^^^^ RUF005 -19 | excitement = ("we all think", ) + Fun().yay() -20 | astonishment = ("we all feel", ) + Fun.words +41 41 | quux = (7, 8, 9) + zoob +42 42 | spam = quux + (10, 11, 12) +43 43 | spom = list(spam) +44 |-eggs = spom + [13, 14, 15] + 44 |+eggs = [*spom, 13, 14, 15] +45 45 | elatement = ("we all say",) + yay() +46 46 | excitement = ("we all think",) + Fun().yay() +47 47 | astonishment = ("we all feel",) + Fun.words + +RUF005.py:45:13: RUF005 [*] Consider `("we all say", *yay())` instead of concatenation + | +45 | spom = list(spam) +46 | eggs = spom + [13, 14, 15] +47 | elatement = ("we all say",) + yay() + | ^^^^^^^^^^^^^^^^^^^^^^^ RUF005 +48 | excitement = ("we all think",) + Fun().yay() +49 | astonishment = ("we all feel",) + Fun.words | = help: Replace with `("we all say", *yay())` ℹ Suggested fix -13 13 | spam = quux + (10, 11, 12) -14 14 | spom = list(spam) -15 15 | eggs = spom + [13, 14, 15] -16 |-elatement = ("we all say", ) + yay() - 16 |+elatement = ("we all say", *yay()) -17 17 | excitement = ("we all think", ) + Fun().yay() -18 18 | astonishment = ("we all feel", ) + Fun.words -19 19 | - -RUF005.py:17:14: RUF005 [*] Consider `("we all think", *Fun().yay())` instead of concatenation - | -17 | eggs = spom + [13, 14, 15] -18 | elatement = ("we all say", ) + yay() -19 | excitement = ("we all think", ) + Fun().yay() - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ RUF005 -20 | astonishment = ("we all feel", ) + Fun.words +42 42 | spam = quux + (10, 11, 12) +43 43 | spom = list(spam) +44 44 | eggs = spom + [13, 14, 15] +45 |-elatement = ("we all say",) + yay() + 45 |+elatement = ("we all say", *yay()) +46 46 | excitement = ("we all think",) + Fun().yay() +47 47 | astonishment = ("we all feel",) + Fun.words +48 48 | + +RUF005.py:46:14: RUF005 [*] Consider `("we all think", *Fun().yay())` instead of concatenation + | +46 | eggs = spom + [13, 14, 15] +47 | elatement = ("we all say",) + yay() +48 | excitement = ("we all think",) + Fun().yay() + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ RUF005 +49 | astonishment = ("we all feel",) + Fun.words | = help: Replace with `("we all think", *Fun().yay())` ℹ Suggested fix -14 14 | spom = list(spam) -15 15 | eggs = spom + [13, 14, 15] -16 16 | elatement = ("we all say", ) + yay() -17 |-excitement = ("we all think", ) + Fun().yay() - 17 |+excitement = ("we all think", *Fun().yay()) -18 18 | astonishment = ("we all feel", ) + Fun.words -19 19 | -20 20 | chain = ['a', 'b', 'c'] + eggs + list(('yes', 'no', 'pants') + zoob) - -RUF005.py:18:16: RUF005 [*] Consider `("we all feel", *Fun.words)` instead of concatenation - | -18 | elatement = ("we all say", ) + yay() -19 | excitement = ("we all think", ) + Fun().yay() -20 | astonishment = ("we all feel", ) + Fun.words - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ RUF005 -21 | -22 | chain = ['a', 'b', 'c'] + eggs + list(('yes', 'no', 'pants') + zoob) +43 43 | spom = list(spam) +44 44 | eggs = spom + [13, 14, 15] +45 45 | elatement = ("we all say",) + yay() +46 |-excitement = ("we all think",) + Fun().yay() + 46 |+excitement = ("we all think", *Fun().yay()) +47 47 | astonishment = ("we all feel",) + Fun.words +48 48 | +49 49 | chain = ["a", "b", "c"] + eggs + list(("yes", "no", "pants") + zoob) + +RUF005.py:47:16: RUF005 [*] Consider `("we all feel", *Fun.words)` instead of concatenation + | +47 | elatement = ("we all say",) + yay() +48 | excitement = ("we all think",) + Fun().yay() +49 | astonishment = ("we all feel",) + Fun.words + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ RUF005 +50 | +51 | chain = ["a", "b", "c"] + eggs + list(("yes", "no", "pants") + zoob) | = help: Replace with `("we all feel", *Fun.words)` ℹ Suggested fix -15 15 | eggs = spom + [13, 14, 15] -16 16 | elatement = ("we all say", ) + yay() -17 17 | excitement = ("we all think", ) + Fun().yay() -18 |-astonishment = ("we all feel", ) + Fun.words - 18 |+astonishment = ("we all feel", *Fun.words) -19 19 | -20 20 | chain = ['a', 'b', 'c'] + eggs + list(('yes', 'no', 'pants') + zoob) -21 21 | - -RUF005.py:20:9: RUF005 [*] Consider `["a", "b", "c", *eggs]` instead of concatenation - | -20 | astonishment = ("we all feel", ) + Fun.words -21 | -22 | chain = ['a', 'b', 'c'] + eggs + list(('yes', 'no', 'pants') + zoob) - | ^^^^^^^^^^^^^^^^^^^^^^ RUF005 -23 | -24 | baz = () + zoob - | - = help: Replace with `["a", "b", "c", *eggs]` +44 44 | eggs = spom + [13, 14, 15] +45 45 | elatement = ("we all say",) + yay() +46 46 | excitement = ("we all think",) + Fun().yay() +47 |-astonishment = ("we all feel",) + Fun.words + 47 |+astonishment = ("we all feel", *Fun.words) +48 48 | +49 49 | chain = ["a", "b", "c"] + eggs + list(("yes", "no", "pants") + zoob) +50 50 | + +RUF005.py:49:9: RUF005 [*] Consider `["a", "b", "c", *eggs, *list(("yes", "no", "pants") + zoob)]` instead of concatenation + | +49 | astonishment = ("we all feel",) + Fun.words +50 | +51 | chain = ["a", "b", "c"] + eggs + list(("yes", "no", "pants") + zoob) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ RUF005 +52 | +53 | baz = () + zoob + | + = help: Replace with `["a", "b", "c", *eggs, *list(("yes", "no", "pants") + zoob)]` ℹ Suggested fix -17 17 | excitement = ("we all think", ) + Fun().yay() -18 18 | astonishment = ("we all feel", ) + Fun.words -19 19 | -20 |-chain = ['a', 'b', 'c'] + eggs + list(('yes', 'no', 'pants') + zoob) - 20 |+chain = ["a", "b", "c", *eggs] + list(('yes', 'no', 'pants') + zoob) -21 21 | -22 22 | baz = () + zoob -23 23 | - -RUF005.py:20:39: RUF005 [*] Consider `("yes", "no", "pants", *zoob)` instead of concatenation - | -20 | astonishment = ("we all feel", ) + Fun.words -21 | -22 | chain = ['a', 'b', 'c'] + eggs + list(('yes', 'no', 'pants') + zoob) +46 46 | excitement = ("we all think",) + Fun().yay() +47 47 | astonishment = ("we all feel",) + Fun.words +48 48 | +49 |-chain = ["a", "b", "c"] + eggs + list(("yes", "no", "pants") + zoob) + 49 |+chain = ["a", "b", "c", *eggs, *list(("yes", "no", "pants") + zoob)] +50 50 | +51 51 | baz = () + zoob +52 52 | + +RUF005.py:49:39: RUF005 [*] Consider `("yes", "no", "pants", *zoob)` instead of concatenation + | +49 | astonishment = ("we all feel",) + Fun.words +50 | +51 | chain = ["a", "b", "c"] + eggs + list(("yes", "no", "pants") + zoob) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ RUF005 -23 | -24 | baz = () + zoob +52 | +53 | baz = () + zoob | = help: Replace with `("yes", "no", "pants", *zoob)` ℹ Suggested fix -17 17 | excitement = ("we all think", ) + Fun().yay() -18 18 | astonishment = ("we all feel", ) + Fun.words -19 19 | -20 |-chain = ['a', 'b', 'c'] + eggs + list(('yes', 'no', 'pants') + zoob) - 20 |+chain = ['a', 'b', 'c'] + eggs + list(("yes", "no", "pants", *zoob)) -21 21 | -22 22 | baz = () + zoob -23 23 | - -RUF005.py:22:7: RUF005 [*] Consider `(*zoob,)` instead of concatenation - | -22 | chain = ['a', 'b', 'c'] + eggs + list(('yes', 'no', 'pants') + zoob) -23 | -24 | baz = () + zoob +46 46 | excitement = ("we all think",) + Fun().yay() +47 47 | astonishment = ("we all feel",) + Fun.words +48 48 | +49 |-chain = ["a", "b", "c"] + eggs + list(("yes", "no", "pants") + zoob) + 49 |+chain = ["a", "b", "c"] + eggs + list(("yes", "no", "pants", *zoob)) +50 50 | +51 51 | baz = () + zoob +52 52 | + +RUF005.py:51:7: RUF005 [*] Consider `(*zoob,)` instead of concatenation + | +51 | chain = ["a", "b", "c"] + eggs + list(("yes", "no", "pants") + zoob) +52 | +53 | baz = () + zoob | ^^^^^^^^^ RUF005 -25 | -26 | first = [ +54 | +55 | [] + foo + [ | = help: Replace with `(*zoob,)` ℹ Suggested fix -19 19 | -20 20 | chain = ['a', 'b', 'c'] + eggs + list(('yes', 'no', 'pants') + zoob) -21 21 | -22 |-baz = () + zoob - 22 |+baz = (*zoob,) -23 23 | -24 24 | first = [ -25 25 | # The order - -RUF005.py:32:10: RUF005 Consider `[*first, 4, 5, 6]` instead of concatenation - | -32 | # to preserve -33 | ] -34 | second = first + [ - | __________^ -35 | | # please -36 | | 4, -37 | | # don't -38 | | 5, -39 | | # touch -40 | | 6, -41 | | ] +48 48 | +49 49 | chain = ["a", "b", "c"] + eggs + list(("yes", "no", "pants") + zoob) +50 50 | +51 |-baz = () + zoob + 51 |+baz = (*zoob,) +52 52 | +53 53 | [] + foo + [ +54 54 | ] + +RUF005.py:53:1: RUF005 [*] Consider `[*foo]` instead of concatenation + | +53 | baz = () + zoob +54 | +55 | / [] + foo + [ +56 | | ] | |_^ RUF005 -42 | -43 | [] + foo + [ +57 | +58 | pylint_call = [sys.executable, "-m", "pylint"] + args + [path] | - = help: Replace with `[*first, 4, 5, 6]` + = help: Replace with `[*foo]` -RUF005.py:41:1: RUF005 [*] Consider `[*foo]` instead of concatenation +ℹ Suggested fix +50 50 | +51 51 | baz = () + zoob +52 52 | +53 |-[] + foo + [ +54 |-] + 53 |+[*foo] +55 54 | +56 55 | pylint_call = [sys.executable, "-m", "pylint"] + args + [path] +57 56 | pylint_call_tuple = (sys.executable, "-m", "pylint") + args + (path, path2) + +RUF005.py:56:15: RUF005 [*] Consider `[sys.executable, "-m", "pylint", *args, path]` instead of concatenation | -41 | ] -42 | -43 | [] + foo + [ - | ^^^^^^^^ RUF005 -44 | ] +56 | ] +57 | +58 | pylint_call = [sys.executable, "-m", "pylint"] + args + [path] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ RUF005 +59 | pylint_call_tuple = (sys.executable, "-m", "pylint") + args + (path, path2) +60 | b = a + [2, 3] + [4] | - = help: Replace with `[*foo]` + = help: Replace with `[sys.executable, "-m", "pylint", *args, path]` ℹ Suggested fix -38 38 | 6, -39 39 | ] -40 40 | -41 |-[] + foo + [ - 41 |+[*foo] + [ -42 42 | ] -43 43 | -44 44 | [] + foo + [ # This will be preserved, but doesn't prevent the fix - -RUF005.py:44:1: RUF005 [*] Consider `[*foo]` instead of concatenation - | -44 | ] -45 | -46 | [] + foo + [ # This will be preserved, but doesn't prevent the fix - | ^^^^^^^^ RUF005 -47 | ] +53 53 | [] + foo + [ +54 54 | ] +55 55 | +56 |-pylint_call = [sys.executable, "-m", "pylint"] + args + [path] + 56 |+pylint_call = [sys.executable, "-m", "pylint", *args, path] +57 57 | pylint_call_tuple = (sys.executable, "-m", "pylint") + args + (path, path2) +58 58 | b = a + [2, 3] + [4] +59 59 | + +RUF005.py:57:21: RUF005 [*] Consider `(sys.executable, "-m", "pylint", *args, path, path2)` instead of concatenation | - = help: Replace with `[*foo]` +57 | pylint_call = [sys.executable, "-m", "pylint"] + args + [path] +58 | pylint_call_tuple = (sys.executable, "-m", "pylint") + args + (path, path2) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ RUF005 +59 | b = a + [2, 3] + [4] + | + = help: Replace with `(sys.executable, "-m", "pylint", *args, path, path2)` + +ℹ Suggested fix +54 54 | ] +55 55 | +56 56 | pylint_call = [sys.executable, "-m", "pylint"] + args + [path] +57 |-pylint_call_tuple = (sys.executable, "-m", "pylint") + args + (path, path2) + 57 |+pylint_call_tuple = (sys.executable, "-m", "pylint", *args, path, path2) +58 58 | b = a + [2, 3] + [4] +59 59 | +60 60 | # Uses the non-preferred quote style, which should be retained. + +RUF005.py:58:5: RUF005 [*] Consider `[*a, 2, 3, 4]` instead of concatenation + | +58 | pylint_call = [sys.executable, "-m", "pylint"] + args + [path] +59 | pylint_call_tuple = (sys.executable, "-m", "pylint") + args + (path, path2) +60 | b = a + [2, 3] + [4] + | ^^^^^^^^^^^^^^^^ RUF005 +61 | +62 | # Uses the non-preferred quote style, which should be retained. + | + = help: Replace with `[*a, 2, 3, 4]` + +ℹ Suggested fix +55 55 | +56 56 | pylint_call = [sys.executable, "-m", "pylint"] + args + [path] +57 57 | pylint_call_tuple = (sys.executable, "-m", "pylint") + args + (path, path2) +58 |-b = a + [2, 3] + [4] + 58 |+b = [*a, 2, 3, 4] +59 59 | +60 60 | # Uses the non-preferred quote style, which should be retained. +61 61 | f"{a() + ['b']}" + +RUF005.py:61:4: RUF005 [*] Consider `[*a(), 'b']` instead of concatenation + | +61 | # Uses the non-preferred quote style, which should be retained. +62 | f"{a() + ['b']}" + | ^^^^^^^^^^^ RUF005 +63 | +64 | ### + | + = help: Replace with `[*a(), 'b']` ℹ Suggested fix -41 41 | [] + foo + [ -42 42 | ] -43 43 | -44 |-[] + foo + [ # This will be preserved, but doesn't prevent the fix - 44 |+[*foo] + [ # This will be preserved, but doesn't prevent the fix -45 45 | ] -46 46 | -47 47 | # Uses the non-preferred quote style, which should be retained. +58 58 | b = a + [2, 3] + [4] +59 59 | +60 60 | # Uses the non-preferred quote style, which should be retained. +61 |-f"{a() + ['b']}" + 61 |+f"{[*a(), 'b']}" +62 62 | +63 63 | ### +64 64 | # Non-errors.