[pylint] Preserve original value format (PLR6104) (#14978)

## Summary

Resolves #11672.

## Test Plan

`cargo nextest run` and `cargo insta test`.

---------

Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
This commit is contained in:
InSync 2024-12-17 22:07:07 +07:00 committed by GitHub
parent 463046ae07
commit c9fdb1f5e3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 292 additions and 31 deletions

View file

@ -50,6 +50,50 @@ class T:
obj = T() obj = T()
obj.a = obj.a + 1 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 # OK
a_list[0] = a_list[:] * 3 a_list[0] = a_list[:] * 3
index = a_number = a_number + 1 index = a_number = a_number + 1

View file

@ -1,10 +1,10 @@
use ast::{Expr, StmtAugAssign}; use ast::Expr;
use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Edit, Fix}; use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Edit, Fix};
use ruff_macros::{derive_message_formats, ViolationMetadata}; use ruff_macros::{derive_message_formats, ViolationMetadata};
use ruff_python_ast as ast; use ruff_python_ast as ast;
use ruff_python_ast::comparable::ComparableExpr; use ruff_python_ast::comparable::ComparableExpr;
use ruff_python_ast::Operator; use ruff_python_ast::parenthesize::parenthesized_range;
use ruff_python_codegen::Generator; use ruff_python_ast::{AstNode, ExprBinOp, ExpressionRef, Operator};
use ruff_text_size::{Ranged, TextRange}; use ruff_text_size::{Ranged, TextRange};
use crate::checkers::ast::Checker; 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) { if ComparableExpr::from(target) == ComparableExpr::from(&value.left) {
let mut diagnostic = Diagnostic::new(NonAugmentedAssignment { operator }, assign.range()); let mut diagnostic = Diagnostic::new(NonAugmentedAssignment { operator }, assign.range());
diagnostic.set_fix(Fix::unsafe_edit(augmented_assignment( diagnostic.set_fix(Fix::unsafe_edit(augmented_assignment(
checker.generator(), checker,
target, target,
value.op, operator,
&value.right, &value.right,
assign.range(), value,
assign.range,
))); )));
checker.diagnostics.push(diagnostic); checker.diagnostics.push(diagnostic);
return; 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()); let mut diagnostic = Diagnostic::new(NonAugmentedAssignment { operator }, assign.range());
diagnostic.set_fix(Fix::unsafe_edit(augmented_assignment( diagnostic.set_fix(Fix::unsafe_edit(augmented_assignment(
checker.generator(), checker,
target, target,
value.op, operator,
&value.left, &value.left,
assign.range(), value,
assign.range,
))); )));
checker.diagnostics.push(diagnostic); 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`. /// For example, given `x = x + 1`, the fix would be `x += 1`.
fn augmented_assignment( fn augmented_assignment(
generator: Generator, checker: &Checker,
target: &Expr, target: &Expr,
operator: Operator, operator: AugmentedOperator,
right_operand: &Expr, right_operand: &Expr,
original_expr: &ExprBinOp,
range: TextRange, range: TextRange,
) -> Edit { ) -> Edit {
Edit::range_replacement( let locator = checker.locator();
generator.stmt(&ast::Stmt::AugAssign(StmtAugAssign {
range: TextRange::default(), let right_operand_ref = ExpressionRef::from(right_operand);
target: Box::new(target.clone()), let parent = original_expr.as_any_node_ref();
op: operator, let comment_ranges = checker.comment_ranges();
value: Box::new(right_operand.clone()), let source = checker.source();
})),
range, 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)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]

View file

@ -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 27 27 | to_cube = to_cube**to_cube
28 28 | timeDiffSeconds = timeDiffSeconds % 60 28 28 | timeDiffSeconds = timeDiffSeconds % 60
29 |-flags = flags & 0x1 29 |-flags = flags & 0x1
29 |+flags &= 1 29 |+flags &= 0x1
30 30 | flags = flags | 0x1 30 30 | flags = flags | 0x1
31 31 | flags = flags ^ 0x1 31 31 | flags = flags ^ 0x1
32 32 | flags = flags << 1 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 28 28 | timeDiffSeconds = timeDiffSeconds % 60
29 29 | flags = flags & 0x1 29 29 | flags = flags & 0x1
30 |-flags = flags | 0x1 30 |-flags = flags | 0x1
30 |+flags |= 1 30 |+flags |= 0x1
31 31 | flags = flags ^ 0x1 31 31 | flags = flags ^ 0x1
32 32 | flags = flags << 1 32 32 | flags = flags << 1
33 33 | 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 29 29 | flags = flags & 0x1
30 30 | flags = flags | 0x1 30 30 | flags = flags | 0x1
31 |-flags = flags ^ 0x1 31 |-flags = flags ^ 0x1
31 |+flags ^= 1 31 |+flags ^= 0x1
32 32 | flags = flags << 1 32 32 | flags = flags << 1
33 33 | flags = flags >> 1 33 33 | flags = flags >> 1
34 34 | mat1 = mat1 @ mat2 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 40 40 | a_list[:] = a_list[:] * 3
41 41 | 41 41 |
42 |-index = index * (index + 10) 42 |-index = index * (index + 10)
42 |+index *= index + 10 42 |+index *= (index + 10)
43 43 | 43 43 |
44 44 | 44 44 |
45 45 | class T: 45 45 | class T:
@ -524,8 +524,6 @@ non_augmented_assignment.py:51:1: PLR6104 [*] Use `+=` to perform an augmented a
50 | obj = T() 50 | obj = T()
51 | obj.a = obj.a + 1 51 | obj.a = obj.a + 1
| ^^^^^^^^^^^^^^^^^ PLR6104 | ^^^^^^^^^^^^^^^^^ PLR6104
52 |
53 | # OK
| |
= help: Replace with augmented assignment = 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 = obj.a + 1
51 |+obj.a += 1 51 |+obj.a += 1
52 52 | 52 52 |
53 53 | # OK 53 53 |
54 54 | a_list[0] = a_list[:] * 3 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

View file

@ -249,13 +249,13 @@ pub enum SimpleTokenKind {
/// `;` /// `;`
Semi, Semi,
/// '/' /// `/`
Slash, Slash,
/// '*' /// `*`
Star, Star,
/// `.`. /// `.`
Dot, Dot,
/// `+` /// `+`