diff --git a/crates/ruff_linter/resources/test/fixtures/pylint/non_augmented_assignment.py b/crates/ruff_linter/resources/test/fixtures/pylint/non_augmented_assignment.py index af6dbfaaa0..ed4fac06d3 100644 --- a/crates/ruff_linter/resources/test/fixtures/pylint/non_augmented_assignment.py +++ b/crates/ruff_linter/resources/test/fixtures/pylint/non_augmented_assignment.py @@ -50,6 +50,50 @@ class T: obj = T() obj.a = obj.a + 1 + +a = a+-1 + +# Regression tests for https://github.com/astral-sh/ruff/issues/11672 +test = 0x5 +test = test + 0xBA + +test2 = b"" +test2 = test2 + b"\000" + +test3 = "" +test3 = test3 + ( a := R"" + f"oo" ) + +test4 = [] +test4 = test4 + ( e + for e in + range(10) + ) + +test5 = test5 + ( + 4 + * + 10 +) + +test6 = test6 + \ + ( + 4 + * + 10 + ) + +test7 = \ + 100 \ + + test7 + +test8 = \ + 886 \ + + \ + \ + test8 + + # OK a_list[0] = a_list[:] * 3 index = a_number = a_number + 1 diff --git a/crates/ruff_linter/src/rules/pylint/rules/non_augmented_assignment.rs b/crates/ruff_linter/src/rules/pylint/rules/non_augmented_assignment.rs index b2de97bb62..97155c9464 100644 --- a/crates/ruff_linter/src/rules/pylint/rules/non_augmented_assignment.rs +++ b/crates/ruff_linter/src/rules/pylint/rules/non_augmented_assignment.rs @@ -1,10 +1,10 @@ -use ast::{Expr, StmtAugAssign}; +use ast::Expr; use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Edit, Fix}; use ruff_macros::{derive_message_formats, ViolationMetadata}; use ruff_python_ast as ast; use ruff_python_ast::comparable::ComparableExpr; -use ruff_python_ast::Operator; -use ruff_python_codegen::Generator; +use ruff_python_ast::parenthesize::parenthesized_range; +use ruff_python_ast::{AstNode, ExprBinOp, ExpressionRef, Operator}; use ruff_text_size::{Ranged, TextRange}; use crate::checkers::ast::Checker; @@ -103,11 +103,12 @@ pub(crate) fn non_augmented_assignment(checker: &mut Checker, assign: &ast::Stmt if ComparableExpr::from(target) == ComparableExpr::from(&value.left) { let mut diagnostic = Diagnostic::new(NonAugmentedAssignment { operator }, assign.range()); diagnostic.set_fix(Fix::unsafe_edit(augmented_assignment( - checker.generator(), + checker, target, - value.op, + operator, &value.right, - assign.range(), + value, + assign.range, ))); checker.diagnostics.push(diagnostic); return; @@ -121,11 +122,12 @@ pub(crate) fn non_augmented_assignment(checker: &mut Checker, assign: &ast::Stmt { let mut diagnostic = Diagnostic::new(NonAugmentedAssignment { operator }, assign.range()); diagnostic.set_fix(Fix::unsafe_edit(augmented_assignment( - checker.generator(), + checker, target, - value.op, + operator, &value.left, - assign.range(), + value, + assign.range, ))); checker.diagnostics.push(diagnostic); } @@ -135,21 +137,30 @@ pub(crate) fn non_augmented_assignment(checker: &mut Checker, assign: &ast::Stmt /// /// For example, given `x = x + 1`, the fix would be `x += 1`. fn augmented_assignment( - generator: Generator, + checker: &Checker, target: &Expr, - operator: Operator, + operator: AugmentedOperator, right_operand: &Expr, + original_expr: &ExprBinOp, range: TextRange, ) -> Edit { - Edit::range_replacement( - generator.stmt(&ast::Stmt::AugAssign(StmtAugAssign { - range: TextRange::default(), - target: Box::new(target.clone()), - op: operator, - value: Box::new(right_operand.clone()), - })), - range, - ) + let locator = checker.locator(); + + let right_operand_ref = ExpressionRef::from(right_operand); + let parent = original_expr.as_any_node_ref(); + let comment_ranges = checker.comment_ranges(); + let source = checker.source(); + + let right_operand_range = + parenthesized_range(right_operand_ref, parent, comment_ranges, source) + .unwrap_or(right_operand.range()); + let right_operand_expr = locator.slice(right_operand_range); + + let target_expr = locator.slice(target); + + let new_content = format!("{target_expr} {operator} {right_operand_expr}"); + + Edit::range_replacement(new_content, range) } #[derive(Debug, Clone, Copy, PartialEq, Eq)] diff --git a/crates/ruff_linter/src/rules/pylint/snapshots/ruff_linter__rules__pylint__tests__PLR6104_non_augmented_assignment.py.snap b/crates/ruff_linter/src/rules/pylint/snapshots/ruff_linter__rules__pylint__tests__PLR6104_non_augmented_assignment.py.snap index fdf1eb6d3a..9255a22035 100644 --- a/crates/ruff_linter/src/rules/pylint/snapshots/ruff_linter__rules__pylint__tests__PLR6104_non_augmented_assignment.py.snap +++ b/crates/ruff_linter/src/rules/pylint/snapshots/ruff_linter__rules__pylint__tests__PLR6104_non_augmented_assignment.py.snap @@ -269,7 +269,7 @@ non_augmented_assignment.py:29:1: PLR6104 [*] Use `&=` to perform an augmented a 27 27 | to_cube = to_cube**to_cube 28 28 | timeDiffSeconds = timeDiffSeconds % 60 29 |-flags = flags & 0x1 - 29 |+flags &= 1 + 29 |+flags &= 0x1 30 30 | flags = flags | 0x1 31 31 | flags = flags ^ 0x1 32 32 | flags = flags << 1 @@ -290,7 +290,7 @@ non_augmented_assignment.py:30:1: PLR6104 [*] Use `|=` to perform an augmented a 28 28 | timeDiffSeconds = timeDiffSeconds % 60 29 29 | flags = flags & 0x1 30 |-flags = flags | 0x1 - 30 |+flags |= 1 + 30 |+flags |= 0x1 31 31 | flags = flags ^ 0x1 32 32 | flags = flags << 1 33 33 | flags = flags >> 1 @@ -311,7 +311,7 @@ non_augmented_assignment.py:31:1: PLR6104 [*] Use `^=` to perform an augmented a 29 29 | flags = flags & 0x1 30 30 | flags = flags | 0x1 31 |-flags = flags ^ 0x1 - 31 |+flags ^= 1 + 31 |+flags ^= 0x1 32 32 | flags = flags << 1 33 33 | flags = flags >> 1 34 34 | mat1 = mat1 @ mat2 @@ -495,7 +495,7 @@ non_augmented_assignment.py:42:1: PLR6104 [*] Use `*=` to perform an augmented a 40 40 | a_list[:] = a_list[:] * 3 41 41 | 42 |-index = index * (index + 10) - 42 |+index *= index + 10 + 42 |+index *= (index + 10) 43 43 | 44 44 | 45 45 | class T: @@ -524,8 +524,6 @@ non_augmented_assignment.py:51:1: PLR6104 [*] Use `+=` to perform an augmented a 50 | obj = T() 51 | obj.a = obj.a + 1 | ^^^^^^^^^^^^^^^^^ PLR6104 -52 | -53 | # OK | = help: Replace with augmented assignment @@ -536,5 +534,213 @@ non_augmented_assignment.py:51:1: PLR6104 [*] Use `+=` to perform an augmented a 51 |-obj.a = obj.a + 1 51 |+obj.a += 1 52 52 | -53 53 | # OK -54 54 | a_list[0] = a_list[:] * 3 +53 53 | +54 54 | a = a+-1 + +non_augmented_assignment.py:54:1: PLR6104 [*] Use `+=` to perform an augmented assignment directly + | +54 | a = a+-1 + | ^^^^^^^^ PLR6104 +55 | +56 | # Regression tests for https://github.com/astral-sh/ruff/issues/11672 + | + = help: Replace with augmented assignment + +ℹ Unsafe fix +51 51 | obj.a = obj.a + 1 +52 52 | +53 53 | +54 |-a = a+-1 + 54 |+a += -1 +55 55 | +56 56 | # Regression tests for https://github.com/astral-sh/ruff/issues/11672 +57 57 | test = 0x5 + +non_augmented_assignment.py:58:1: PLR6104 [*] Use `+=` to perform an augmented assignment directly + | +56 | # Regression tests for https://github.com/astral-sh/ruff/issues/11672 +57 | test = 0x5 +58 | test = test + 0xBA + | ^^^^^^^^^^^^^^^^^^ PLR6104 +59 | +60 | test2 = b"" + | + = help: Replace with augmented assignment + +ℹ Unsafe fix +55 55 | +56 56 | # Regression tests for https://github.com/astral-sh/ruff/issues/11672 +57 57 | test = 0x5 +58 |-test = test + 0xBA + 58 |+test += 0xBA +59 59 | +60 60 | test2 = b"" +61 61 | test2 = test2 + b"\000" + +non_augmented_assignment.py:61:1: PLR6104 [*] Use `+=` to perform an augmented assignment directly + | +60 | test2 = b"" +61 | test2 = test2 + b"\000" + | ^^^^^^^^^^^^^^^^^^^^^^^ PLR6104 +62 | +63 | test3 = "" + | + = help: Replace with augmented assignment + +ℹ Unsafe fix +58 58 | test = test + 0xBA +59 59 | +60 60 | test2 = b"" +61 |-test2 = test2 + b"\000" + 61 |+test2 += b"\000" +62 62 | +63 63 | test3 = "" +64 64 | test3 = test3 + ( a := R"" + +non_augmented_assignment.py:64:1: PLR6104 [*] Use `+=` to perform an augmented assignment directly + | +63 | test3 = "" +64 | / test3 = test3 + ( a := R"" +65 | | f"oo" ) + | |__________________________________^ PLR6104 +66 | +67 | test4 = [] + | + = help: Replace with augmented assignment + +ℹ Unsafe fix +61 61 | test2 = test2 + b"\000" +62 62 | +63 63 | test3 = "" +64 |-test3 = test3 + ( a := R"" + 64 |+test3 += ( a := R"" +65 65 | f"oo" ) +66 66 | +67 67 | test4 = [] + +non_augmented_assignment.py:68:1: PLR6104 [*] Use `+=` to perform an augmented assignment directly + | +67 | test4 = [] +68 | / test4 = test4 + ( e +69 | | for e in +70 | | range(10) +71 | | ) + | |___________________^ PLR6104 +72 | +73 | test5 = test5 + ( + | + = help: Replace with augmented assignment + +ℹ Unsafe fix +65 65 | f"oo" ) +66 66 | +67 67 | test4 = [] +68 |-test4 = test4 + ( e + 68 |+test4 += ( e +69 69 | for e in +70 70 | range(10) +71 71 | ) + +non_augmented_assignment.py:73:1: PLR6104 [*] Use `+=` to perform an augmented assignment directly + | +71 | ) +72 | +73 | / test5 = test5 + ( +74 | | 4 +75 | | * +76 | | 10 +77 | | ) + | |_^ PLR6104 +78 | +79 | test6 = test6 + \ + | + = help: Replace with augmented assignment + +ℹ Unsafe fix +70 70 | range(10) +71 71 | ) +72 72 | +73 |-test5 = test5 + ( + 73 |+test5 += ( +74 74 | 4 +75 75 | * +76 76 | 10 + +non_augmented_assignment.py:79:1: PLR6104 [*] Use `+=` to perform an augmented assignment directly + | +77 | ) +78 | +79 | / test6 = test6 + \ +80 | | ( +81 | | 4 +82 | | * +83 | | 10 +84 | | ) + | |_________^ PLR6104 +85 | +86 | test7 = \ + | + = help: Replace with augmented assignment + +ℹ Unsafe fix +76 76 | 10 +77 77 | ) +78 78 | +79 |-test6 = test6 + \ +80 |- ( + 79 |+test6 += ( +81 80 | 4 +82 81 | * +83 82 | 10 + +non_augmented_assignment.py:86:1: PLR6104 [*] Use `+=` to perform an augmented assignment directly + | +84 | ) +85 | +86 | / test7 = \ +87 | | 100 \ +88 | | + test7 + | |___________^ PLR6104 +89 | +90 | test8 = \ + | + = help: Replace with augmented assignment + +ℹ Unsafe fix +83 83 | 10 +84 84 | ) +85 85 | +86 |-test7 = \ +87 |- 100 \ +88 |- + test7 + 86 |+test7 += 100 +89 87 | +90 88 | test8 = \ +91 89 | 886 \ + +non_augmented_assignment.py:90:1: PLR6104 [*] Use `+=` to perform an augmented assignment directly + | +88 | + test7 +89 | +90 | / test8 = \ +91 | | 886 \ +92 | | + \ +93 | | \ +94 | | test8 + | |_________^ PLR6104 + | + = help: Replace with augmented assignment + +ℹ Unsafe fix +87 87 | 100 \ +88 88 | + test7 +89 89 | +90 |-test8 = \ +91 |- 886 \ +92 |- + \ +93 |- \ +94 |- test8 + 90 |+test8 += 886 +95 91 | +96 92 | +97 93 | # OK diff --git a/crates/ruff_python_trivia/src/tokenizer.rs b/crates/ruff_python_trivia/src/tokenizer.rs index 9b42665235..4b0e1860f5 100644 --- a/crates/ruff_python_trivia/src/tokenizer.rs +++ b/crates/ruff_python_trivia/src/tokenizer.rs @@ -249,13 +249,13 @@ pub enum SimpleTokenKind { /// `;` Semi, - /// '/' + /// `/` Slash, - /// '*' + /// `*` Star, - /// `.`. + /// `.` Dot, /// `+`