From 116249d59b204ad4f33a8fb7dc257faca5d4311c Mon Sep 17 00:00:00 2001 From: wolfgangshi <1281588+lshi18@users.noreply.github.com> Date: Fri, 12 Apr 2024 06:08:42 +0300 Subject: [PATCH] [`pylint`] Implement rule to prefer augmented assignment (`PLR6104`) (#9932) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary Implement new rule: Prefer augmented assignment (#8877). It checks for the assignment statement with the form of ` = …` with a unsafe fix to use augmented assignment instead. ## Test Plan 1. Snapshot test is included in the PR. 2. Manually test with playground. --- .../pylint/non_augmented_assignment.py | 55 ++ .../src/checkers/ast/analyze/statement.rs | 3 + crates/ruff_linter/src/codes.rs | 1 + crates/ruff_linter/src/rules/pylint/mod.rs | 1 + .../ruff_linter/src/rules/pylint/rules/mod.rs | 2 + .../pylint/rules/non_augmented_assignment.rs | 202 +++++++ ...__PLR6104_non_augmented_assignment.py.snap | 518 ++++++++++++++++++ ruff.schema.json | 3 + 8 files changed, 785 insertions(+) create mode 100644 crates/ruff_linter/resources/test/fixtures/pylint/non_augmented_assignment.py create mode 100644 crates/ruff_linter/src/rules/pylint/rules/non_augmented_assignment.rs create mode 100644 crates/ruff_linter/src/rules/pylint/snapshots/ruff_linter__rules__pylint__tests__PLR6104_non_augmented_assignment.py.snap 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 new file mode 100644 index 00000000000000..41aee933843d61 --- /dev/null +++ b/crates/ruff_linter/resources/test/fixtures/pylint/non_augmented_assignment.py @@ -0,0 +1,55 @@ +# Errors +some_string = "some string" +index, a_number, to_multiply, to_divide, to_cube, timeDiffSeconds, flags = ( + 0, + 1, + 2, + 3, + 4, + 5, + 0x3, +) +a_list = [1, 2] +some_set = {"elem"} +mat1, mat2 = None, None + +some_string = some_string + "a very long end of string" +index = index - 1 +a_list = a_list + ["to concat"] +some_set = some_set | {"to concat"} +to_multiply = to_multiply * 5 +to_divide = to_divide / 5 +to_divide = to_divide // 5 +to_cube = to_cube**3 +to_cube = 3**to_cube +to_cube = to_cube**to_cube +timeDiffSeconds = timeDiffSeconds % 60 +flags = flags & 0x1 +flags = flags | 0x1 +flags = flags ^ 0x1 +flags = flags << 1 +flags = flags >> 1 +mat1 = mat1 @ mat2 +a_list[1] = a_list[1] + 1 + +a_list[0:2] = a_list[0:2] * 3 +a_list[:2] = a_list[:2] * 3 +a_list[1:] = a_list[1:] * 3 +a_list[:] = a_list[:] * 3 + +index = index * (index + 10) + + +class T: + def t(self): + self.a = self.a + 1 + + +obj = T() +obj.a = obj.a + 1 + +# OK +a_list[0] = a_list[:] * 3 +index = a_number = a_number + 1 +a_number = index = a_number + 1 +index = index * index + 10 diff --git a/crates/ruff_linter/src/checkers/ast/analyze/statement.rs b/crates/ruff_linter/src/checkers/ast/analyze/statement.rs index 96dcf5df556819..76f5eba8fe15f8 100644 --- a/crates/ruff_linter/src/checkers/ast/analyze/statement.rs +++ b/crates/ruff_linter/src/checkers/ast/analyze/statement.rs @@ -1479,6 +1479,9 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) { if checker.settings.rules.enabled(Rule::TypeBivariance) { pylint::rules::type_bivariance(checker, value); } + if checker.enabled(Rule::NonAugmentedAssignment) { + pylint::rules::non_augmented_assignment(checker, assign); + } if checker.settings.rules.enabled(Rule::UnsortedDunderAll) { ruff::rules::sort_dunder_all_assign(checker, assign); } diff --git a/crates/ruff_linter/src/codes.rs b/crates/ruff_linter/src/codes.rs index 276536f2f00dfa..b6033905b9807a 100644 --- a/crates/ruff_linter/src/codes.rs +++ b/crates/ruff_linter/src/codes.rs @@ -293,6 +293,7 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> { (Pylint, "R2004") => (RuleGroup::Stable, rules::pylint::rules::MagicValueComparison), (Pylint, "R2044") => (RuleGroup::Preview, rules::pylint::rules::EmptyComment), (Pylint, "R5501") => (RuleGroup::Stable, rules::pylint::rules::CollapsibleElseIf), + (Pylint, "R6104") => (RuleGroup::Preview, rules::pylint::rules::NonAugmentedAssignment), (Pylint, "R6201") => (RuleGroup::Preview, rules::pylint::rules::LiteralMembership), #[allow(deprecated)] (Pylint, "R6301") => (RuleGroup::Nursery, rules::pylint::rules::NoSelfUse), diff --git a/crates/ruff_linter/src/rules/pylint/mod.rs b/crates/ruff_linter/src/rules/pylint/mod.rs index ed77947eb8b62e..b71a5f92ed38e5 100644 --- a/crates/ruff_linter/src/rules/pylint/mod.rs +++ b/crates/ruff_linter/src/rules/pylint/mod.rs @@ -186,6 +186,7 @@ mod tests { Rule::UnnecessaryDictIndexLookup, Path::new("unnecessary_dict_index_lookup.py") )] + #[test_case(Rule::NonAugmentedAssignment, Path::new("non_augmented_assignment.py"))] #[test_case( Rule::UselessExceptionStatement, Path::new("useless_exception_statement.py") diff --git a/crates/ruff_linter/src/rules/pylint/rules/mod.rs b/crates/ruff_linter/src/rules/pylint/rules/mod.rs index 56cb0edd97f9ba..0c388ef91c5d15 100644 --- a/crates/ruff_linter/src/rules/pylint/rules/mod.rs +++ b/crates/ruff_linter/src/rules/pylint/rules/mod.rs @@ -47,6 +47,7 @@ pub(crate) use no_method_decorator::*; pub(crate) use no_self_use::*; pub(crate) use non_ascii_module_import::*; pub(crate) use non_ascii_name::*; +pub(crate) use non_augmented_assignment::*; pub(crate) use non_slot_assignment::*; pub(crate) use nonlocal_and_global::*; pub(crate) use nonlocal_without_binding::*; @@ -143,6 +144,7 @@ mod no_method_decorator; mod no_self_use; mod non_ascii_module_import; mod non_ascii_name; +mod non_augmented_assignment; mod non_slot_assignment; mod nonlocal_and_global; mod nonlocal_without_binding; 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 new file mode 100644 index 00000000000000..1a875934cbb5ae --- /dev/null +++ b/crates/ruff_linter/src/rules/pylint/rules/non_augmented_assignment.rs @@ -0,0 +1,202 @@ +use ast::{Expr, StmtAugAssign}; +use ruff_diagnostics::{AlwaysFixableViolation, Diagnostic, Edit, Fix}; +use ruff_macros::{derive_message_formats, violation}; +use ruff_python_ast as ast; +use ruff_python_ast::comparable::ComparableExpr; +use ruff_python_ast::Operator; +use ruff_python_codegen::Generator; +use ruff_text_size::{Ranged, TextRange}; + +use crate::checkers::ast::Checker; + +/// ## What it does +/// Checks for assignments that can be replaced with augmented assignment +/// statements. +/// +/// ## Why is this bad? +/// If an assignment statement consists of a binary operation in which one +/// operand is the same as the assignment target, it can be rewritten as an +/// augmented assignment. For example, `x = x + 1` can be rewritten as +/// `x += 1`. +/// +/// When performing such an operation, augmented assignments are more concise +/// and idiomatic. +/// +/// ## Example +/// ```python +/// x = x + 1 +/// ``` +/// +/// Use instead: +/// ```python +/// x += 1 +/// ``` +/// +/// ## Fix safety +/// This rule's fix is marked as unsafe, as augmented assignments have +/// different semantics when the target is a mutable data type, like a list or +/// dictionary. +/// +/// For example, consider the following: +/// +/// ```python +/// foo = [1] +/// bar = foo +/// foo = foo + [2] +/// assert (foo, bar) == ([1, 2], [1]) +/// ``` +/// +/// If the assignment is replaced with an augmented assignment, the update +/// operation will apply to both `foo` and `bar`, as they refer to the same +/// object: +/// +/// ```python +/// foo = [1] +/// bar = foo +/// foo += [2] +/// assert (foo, bar) == ([1, 2], [1, 2]) +/// ``` +#[violation] +pub struct NonAugmentedAssignment { + operator: AugmentedOperator, +} + +impl AlwaysFixableViolation for NonAugmentedAssignment { + #[derive_message_formats] + fn message(&self) -> String { + let NonAugmentedAssignment { operator } = self; + format!("Use `{operator}` to perform an augmented assignment directly") + } + + fn fix_title(&self) -> String { + "Replace with augmented assignment".to_string() + } +} + +/// PLR6104 +pub(crate) fn non_augmented_assignment(checker: &mut Checker, assign: &ast::StmtAssign) { + // Ignore multiple assignment targets. + let [target] = assign.targets.as_slice() else { + return; + }; + + // Match, e.g., `x = x + 1`. + let Expr::BinOp(value) = &*assign.value else { + return; + }; + + // Match, e.g., `x = x + 1`. + if ComparableExpr::from(target) == ComparableExpr::from(&value.left) { + let mut diagnostic = Diagnostic::new( + NonAugmentedAssignment { + operator: AugmentedOperator::from(value.op), + }, + assign.range(), + ); + diagnostic.set_fix(Fix::unsafe_edit(augmented_assignment( + checker.generator(), + target, + value.op, + &value.right, + assign.range(), + ))); + checker.diagnostics.push(diagnostic); + return; + } + + // Match, e.g., `x = 1 + x`. + if ComparableExpr::from(target) == ComparableExpr::from(&value.right) { + let mut diagnostic = Diagnostic::new( + NonAugmentedAssignment { + operator: AugmentedOperator::from(value.op), + }, + assign.range(), + ); + diagnostic.set_fix(Fix::unsafe_edit(augmented_assignment( + checker.generator(), + target, + value.op, + &value.left, + assign.range(), + ))); + checker.diagnostics.push(diagnostic); + } +} + +/// Generate a fix to convert an assignment statement to an augmented assignment. +/// +/// For example, given `x = x + 1`, the fix would be `x += 1`. +fn augmented_assignment( + generator: Generator, + target: &Expr, + operator: Operator, + right_operand: &Expr, + 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, + ) +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +enum AugmentedOperator { + Add, + BitAnd, + BitOr, + BitXor, + Div, + FloorDiv, + LShift, + MatMult, + Mod, + Mult, + Pow, + RShift, + Sub, +} + +impl From for AugmentedOperator { + fn from(value: Operator) -> Self { + match value { + Operator::Add => Self::Add, + Operator::BitAnd => Self::BitAnd, + Operator::BitOr => Self::BitOr, + Operator::BitXor => Self::BitXor, + Operator::Div => Self::Div, + Operator::FloorDiv => Self::FloorDiv, + Operator::LShift => Self::LShift, + Operator::MatMult => Self::MatMult, + Operator::Mod => Self::Mod, + Operator::Mult => Self::Mult, + Operator::Pow => Self::Pow, + Operator::RShift => Self::RShift, + Operator::Sub => Self::Sub, + } + } +} + +impl std::fmt::Display for AugmentedOperator { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Add => f.write_str("+="), + Self::BitAnd => f.write_str("&="), + Self::BitOr => f.write_str("|="), + Self::BitXor => f.write_str("^="), + Self::Div => f.write_str("/="), + Self::FloorDiv => f.write_str("//="), + Self::LShift => f.write_str("<<="), + Self::MatMult => f.write_str("@="), + Self::Mod => f.write_str("%="), + Self::Mult => f.write_str("*="), + Self::Pow => f.write_str("**="), + Self::RShift => f.write_str(">>="), + Self::Sub => f.write_str("-="), + } + } +} 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 new file mode 100644 index 00000000000000..9e2557b9ed7c8c --- /dev/null +++ b/crates/ruff_linter/src/rules/pylint/snapshots/ruff_linter__rules__pylint__tests__PLR6104_non_augmented_assignment.py.snap @@ -0,0 +1,518 @@ +--- +source: crates/ruff_linter/src/rules/pylint/mod.rs +--- +non_augmented_assignment.py:16:1: PLR6104 [*] Use `+=` to perform an augmented assignment directly + | +14 | mat1, mat2 = None, None +15 | +16 | some_string = some_string + "a very long end of string" + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PLR6104 +17 | index = index - 1 +18 | a_list = a_list + ["to concat"] + | + = help: Replace with augmented assignment + +ℹ Unsafe fix +13 13 | some_set = {"elem"} +14 14 | mat1, mat2 = None, None +15 15 | +16 |-some_string = some_string + "a very long end of string" + 16 |+some_string += "a very long end of string" +17 17 | index = index - 1 +18 18 | a_list = a_list + ["to concat"] +19 19 | some_set = some_set | {"to concat"} + +non_augmented_assignment.py:17:1: PLR6104 [*] Use `-=` to perform an augmented assignment directly + | +16 | some_string = some_string + "a very long end of string" +17 | index = index - 1 + | ^^^^^^^^^^^^^^^^^ PLR6104 +18 | a_list = a_list + ["to concat"] +19 | some_set = some_set | {"to concat"} + | + = help: Replace with augmented assignment + +ℹ Unsafe fix +14 14 | mat1, mat2 = None, None +15 15 | +16 16 | some_string = some_string + "a very long end of string" +17 |-index = index - 1 + 17 |+index -= 1 +18 18 | a_list = a_list + ["to concat"] +19 19 | some_set = some_set | {"to concat"} +20 20 | to_multiply = to_multiply * 5 + +non_augmented_assignment.py:18:1: PLR6104 [*] Use `+=` to perform an augmented assignment directly + | +16 | some_string = some_string + "a very long end of string" +17 | index = index - 1 +18 | a_list = a_list + ["to concat"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PLR6104 +19 | some_set = some_set | {"to concat"} +20 | to_multiply = to_multiply * 5 + | + = help: Replace with augmented assignment + +ℹ Unsafe fix +15 15 | +16 16 | some_string = some_string + "a very long end of string" +17 17 | index = index - 1 +18 |-a_list = a_list + ["to concat"] + 18 |+a_list += ["to concat"] +19 19 | some_set = some_set | {"to concat"} +20 20 | to_multiply = to_multiply * 5 +21 21 | to_divide = to_divide / 5 + +non_augmented_assignment.py:19:1: PLR6104 [*] Use `|=` to perform an augmented assignment directly + | +17 | index = index - 1 +18 | a_list = a_list + ["to concat"] +19 | some_set = some_set | {"to concat"} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PLR6104 +20 | to_multiply = to_multiply * 5 +21 | to_divide = to_divide / 5 + | + = help: Replace with augmented assignment + +ℹ Unsafe fix +16 16 | some_string = some_string + "a very long end of string" +17 17 | index = index - 1 +18 18 | a_list = a_list + ["to concat"] +19 |-some_set = some_set | {"to concat"} + 19 |+some_set |= {"to concat"} +20 20 | to_multiply = to_multiply * 5 +21 21 | to_divide = to_divide / 5 +22 22 | to_divide = to_divide // 5 + +non_augmented_assignment.py:20:1: PLR6104 [*] Use `*=` to perform an augmented assignment directly + | +18 | a_list = a_list + ["to concat"] +19 | some_set = some_set | {"to concat"} +20 | to_multiply = to_multiply * 5 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PLR6104 +21 | to_divide = to_divide / 5 +22 | to_divide = to_divide // 5 + | + = help: Replace with augmented assignment + +ℹ Unsafe fix +17 17 | index = index - 1 +18 18 | a_list = a_list + ["to concat"] +19 19 | some_set = some_set | {"to concat"} +20 |-to_multiply = to_multiply * 5 + 20 |+to_multiply *= 5 +21 21 | to_divide = to_divide / 5 +22 22 | to_divide = to_divide // 5 +23 23 | to_cube = to_cube**3 + +non_augmented_assignment.py:21:1: PLR6104 [*] Use `/=` to perform an augmented assignment directly + | +19 | some_set = some_set | {"to concat"} +20 | to_multiply = to_multiply * 5 +21 | to_divide = to_divide / 5 + | ^^^^^^^^^^^^^^^^^^^^^^^^^ PLR6104 +22 | to_divide = to_divide // 5 +23 | to_cube = to_cube**3 + | + = help: Replace with augmented assignment + +ℹ Unsafe fix +18 18 | a_list = a_list + ["to concat"] +19 19 | some_set = some_set | {"to concat"} +20 20 | to_multiply = to_multiply * 5 +21 |-to_divide = to_divide / 5 + 21 |+to_divide /= 5 +22 22 | to_divide = to_divide // 5 +23 23 | to_cube = to_cube**3 +24 24 | to_cube = 3**to_cube + +non_augmented_assignment.py:22:1: PLR6104 [*] Use `//=` to perform an augmented assignment directly + | +20 | to_multiply = to_multiply * 5 +21 | to_divide = to_divide / 5 +22 | to_divide = to_divide // 5 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ PLR6104 +23 | to_cube = to_cube**3 +24 | to_cube = 3**to_cube + | + = help: Replace with augmented assignment + +ℹ Unsafe fix +19 19 | some_set = some_set | {"to concat"} +20 20 | to_multiply = to_multiply * 5 +21 21 | to_divide = to_divide / 5 +22 |-to_divide = to_divide // 5 + 22 |+to_divide //= 5 +23 23 | to_cube = to_cube**3 +24 24 | to_cube = 3**to_cube +25 25 | to_cube = to_cube**to_cube + +non_augmented_assignment.py:23:1: PLR6104 [*] Use `**=` to perform an augmented assignment directly + | +21 | to_divide = to_divide / 5 +22 | to_divide = to_divide // 5 +23 | to_cube = to_cube**3 + | ^^^^^^^^^^^^^^^^^^^^ PLR6104 +24 | to_cube = 3**to_cube +25 | to_cube = to_cube**to_cube + | + = help: Replace with augmented assignment + +ℹ Unsafe fix +20 20 | to_multiply = to_multiply * 5 +21 21 | to_divide = to_divide / 5 +22 22 | to_divide = to_divide // 5 +23 |-to_cube = to_cube**3 + 23 |+to_cube **= 3 +24 24 | to_cube = 3**to_cube +25 25 | to_cube = to_cube**to_cube +26 26 | timeDiffSeconds = timeDiffSeconds % 60 + +non_augmented_assignment.py:24:1: PLR6104 [*] Use `**=` to perform an augmented assignment directly + | +22 | to_divide = to_divide // 5 +23 | to_cube = to_cube**3 +24 | to_cube = 3**to_cube + | ^^^^^^^^^^^^^^^^^^^^ PLR6104 +25 | to_cube = to_cube**to_cube +26 | timeDiffSeconds = timeDiffSeconds % 60 + | + = help: Replace with augmented assignment + +ℹ Unsafe fix +21 21 | to_divide = to_divide / 5 +22 22 | to_divide = to_divide // 5 +23 23 | to_cube = to_cube**3 +24 |-to_cube = 3**to_cube + 24 |+to_cube **= 3 +25 25 | to_cube = to_cube**to_cube +26 26 | timeDiffSeconds = timeDiffSeconds % 60 +27 27 | flags = flags & 0x1 + +non_augmented_assignment.py:25:1: PLR6104 [*] Use `**=` to perform an augmented assignment directly + | +23 | to_cube = to_cube**3 +24 | to_cube = 3**to_cube +25 | to_cube = to_cube**to_cube + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ PLR6104 +26 | timeDiffSeconds = timeDiffSeconds % 60 +27 | flags = flags & 0x1 + | + = help: Replace with augmented assignment + +ℹ Unsafe fix +22 22 | to_divide = to_divide // 5 +23 23 | to_cube = to_cube**3 +24 24 | to_cube = 3**to_cube +25 |-to_cube = to_cube**to_cube + 25 |+to_cube **= to_cube +26 26 | timeDiffSeconds = timeDiffSeconds % 60 +27 27 | flags = flags & 0x1 +28 28 | flags = flags | 0x1 + +non_augmented_assignment.py:26:1: PLR6104 [*] Use `%=` to perform an augmented assignment directly + | +24 | to_cube = 3**to_cube +25 | to_cube = to_cube**to_cube +26 | timeDiffSeconds = timeDiffSeconds % 60 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PLR6104 +27 | flags = flags & 0x1 +28 | flags = flags | 0x1 + | + = help: Replace with augmented assignment + +ℹ Unsafe fix +23 23 | to_cube = to_cube**3 +24 24 | to_cube = 3**to_cube +25 25 | to_cube = to_cube**to_cube +26 |-timeDiffSeconds = timeDiffSeconds % 60 + 26 |+timeDiffSeconds %= 60 +27 27 | flags = flags & 0x1 +28 28 | flags = flags | 0x1 +29 29 | flags = flags ^ 0x1 + +non_augmented_assignment.py:27:1: PLR6104 [*] Use `&=` to perform an augmented assignment directly + | +25 | to_cube = to_cube**to_cube +26 | timeDiffSeconds = timeDiffSeconds % 60 +27 | flags = flags & 0x1 + | ^^^^^^^^^^^^^^^^^^^ PLR6104 +28 | flags = flags | 0x1 +29 | flags = flags ^ 0x1 + | + = help: Replace with augmented assignment + +ℹ Unsafe fix +24 24 | to_cube = 3**to_cube +25 25 | to_cube = to_cube**to_cube +26 26 | timeDiffSeconds = timeDiffSeconds % 60 +27 |-flags = flags & 0x1 + 27 |+flags &= 1 +28 28 | flags = flags | 0x1 +29 29 | flags = flags ^ 0x1 +30 30 | flags = flags << 1 + +non_augmented_assignment.py:28:1: PLR6104 [*] Use `|=` to perform an augmented assignment directly + | +26 | timeDiffSeconds = timeDiffSeconds % 60 +27 | flags = flags & 0x1 +28 | flags = flags | 0x1 + | ^^^^^^^^^^^^^^^^^^^ PLR6104 +29 | flags = flags ^ 0x1 +30 | flags = flags << 1 + | + = help: Replace with augmented assignment + +ℹ Unsafe fix +25 25 | to_cube = to_cube**to_cube +26 26 | timeDiffSeconds = timeDiffSeconds % 60 +27 27 | flags = flags & 0x1 +28 |-flags = flags | 0x1 + 28 |+flags |= 1 +29 29 | flags = flags ^ 0x1 +30 30 | flags = flags << 1 +31 31 | flags = flags >> 1 + +non_augmented_assignment.py:29:1: PLR6104 [*] Use `^=` to perform an augmented assignment directly + | +27 | flags = flags & 0x1 +28 | flags = flags | 0x1 +29 | flags = flags ^ 0x1 + | ^^^^^^^^^^^^^^^^^^^ PLR6104 +30 | flags = flags << 1 +31 | flags = flags >> 1 + | + = help: Replace with augmented assignment + +ℹ Unsafe fix +26 26 | timeDiffSeconds = timeDiffSeconds % 60 +27 27 | flags = flags & 0x1 +28 28 | flags = flags | 0x1 +29 |-flags = flags ^ 0x1 + 29 |+flags ^= 1 +30 30 | flags = flags << 1 +31 31 | flags = flags >> 1 +32 32 | mat1 = mat1 @ mat2 + +non_augmented_assignment.py:30:1: PLR6104 [*] Use `<<=` to perform an augmented assignment directly + | +28 | flags = flags | 0x1 +29 | flags = flags ^ 0x1 +30 | flags = flags << 1 + | ^^^^^^^^^^^^^^^^^^ PLR6104 +31 | flags = flags >> 1 +32 | mat1 = mat1 @ mat2 + | + = help: Replace with augmented assignment + +ℹ Unsafe fix +27 27 | flags = flags & 0x1 +28 28 | flags = flags | 0x1 +29 29 | flags = flags ^ 0x1 +30 |-flags = flags << 1 + 30 |+flags <<= 1 +31 31 | flags = flags >> 1 +32 32 | mat1 = mat1 @ mat2 +33 33 | a_list[1] = a_list[1] + 1 + +non_augmented_assignment.py:31:1: PLR6104 [*] Use `>>=` to perform an augmented assignment directly + | +29 | flags = flags ^ 0x1 +30 | flags = flags << 1 +31 | flags = flags >> 1 + | ^^^^^^^^^^^^^^^^^^ PLR6104 +32 | mat1 = mat1 @ mat2 +33 | a_list[1] = a_list[1] + 1 + | + = help: Replace with augmented assignment + +ℹ Unsafe fix +28 28 | flags = flags | 0x1 +29 29 | flags = flags ^ 0x1 +30 30 | flags = flags << 1 +31 |-flags = flags >> 1 + 31 |+flags >>= 1 +32 32 | mat1 = mat1 @ mat2 +33 33 | a_list[1] = a_list[1] + 1 +34 34 | + +non_augmented_assignment.py:32:1: PLR6104 [*] Use `@=` to perform an augmented assignment directly + | +30 | flags = flags << 1 +31 | flags = flags >> 1 +32 | mat1 = mat1 @ mat2 + | ^^^^^^^^^^^^^^^^^^ PLR6104 +33 | a_list[1] = a_list[1] + 1 + | + = help: Replace with augmented assignment + +ℹ Unsafe fix +29 29 | flags = flags ^ 0x1 +30 30 | flags = flags << 1 +31 31 | flags = flags >> 1 +32 |-mat1 = mat1 @ mat2 + 32 |+mat1 @= mat2 +33 33 | a_list[1] = a_list[1] + 1 +34 34 | +35 35 | a_list[0:2] = a_list[0:2] * 3 + +non_augmented_assignment.py:33:1: PLR6104 [*] Use `+=` to perform an augmented assignment directly + | +31 | flags = flags >> 1 +32 | mat1 = mat1 @ mat2 +33 | a_list[1] = a_list[1] + 1 + | ^^^^^^^^^^^^^^^^^^^^^^^^^ PLR6104 +34 | +35 | a_list[0:2] = a_list[0:2] * 3 + | + = help: Replace with augmented assignment + +ℹ Unsafe fix +30 30 | flags = flags << 1 +31 31 | flags = flags >> 1 +32 32 | mat1 = mat1 @ mat2 +33 |-a_list[1] = a_list[1] + 1 + 33 |+a_list[1] += 1 +34 34 | +35 35 | a_list[0:2] = a_list[0:2] * 3 +36 36 | a_list[:2] = a_list[:2] * 3 + +non_augmented_assignment.py:35:1: PLR6104 [*] Use `*=` to perform an augmented assignment directly + | +33 | a_list[1] = a_list[1] + 1 +34 | +35 | a_list[0:2] = a_list[0:2] * 3 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PLR6104 +36 | a_list[:2] = a_list[:2] * 3 +37 | a_list[1:] = a_list[1:] * 3 + | + = help: Replace with augmented assignment + +ℹ Unsafe fix +32 32 | mat1 = mat1 @ mat2 +33 33 | a_list[1] = a_list[1] + 1 +34 34 | +35 |-a_list[0:2] = a_list[0:2] * 3 + 35 |+a_list[0:2] *= 3 +36 36 | a_list[:2] = a_list[:2] * 3 +37 37 | a_list[1:] = a_list[1:] * 3 +38 38 | a_list[:] = a_list[:] * 3 + +non_augmented_assignment.py:36:1: PLR6104 [*] Use `*=` to perform an augmented assignment directly + | +35 | a_list[0:2] = a_list[0:2] * 3 +36 | a_list[:2] = a_list[:2] * 3 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ PLR6104 +37 | a_list[1:] = a_list[1:] * 3 +38 | a_list[:] = a_list[:] * 3 + | + = help: Replace with augmented assignment + +ℹ Unsafe fix +33 33 | a_list[1] = a_list[1] + 1 +34 34 | +35 35 | a_list[0:2] = a_list[0:2] * 3 +36 |-a_list[:2] = a_list[:2] * 3 + 36 |+a_list[:2] *= 3 +37 37 | a_list[1:] = a_list[1:] * 3 +38 38 | a_list[:] = a_list[:] * 3 +39 39 | + +non_augmented_assignment.py:37:1: PLR6104 [*] Use `*=` to perform an augmented assignment directly + | +35 | a_list[0:2] = a_list[0:2] * 3 +36 | a_list[:2] = a_list[:2] * 3 +37 | a_list[1:] = a_list[1:] * 3 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ PLR6104 +38 | a_list[:] = a_list[:] * 3 + | + = help: Replace with augmented assignment + +ℹ Unsafe fix +34 34 | +35 35 | a_list[0:2] = a_list[0:2] * 3 +36 36 | a_list[:2] = a_list[:2] * 3 +37 |-a_list[1:] = a_list[1:] * 3 + 37 |+a_list[1:] *= 3 +38 38 | a_list[:] = a_list[:] * 3 +39 39 | +40 40 | index = index * (index + 10) + +non_augmented_assignment.py:38:1: PLR6104 [*] Use `*=` to perform an augmented assignment directly + | +36 | a_list[:2] = a_list[:2] * 3 +37 | a_list[1:] = a_list[1:] * 3 +38 | a_list[:] = a_list[:] * 3 + | ^^^^^^^^^^^^^^^^^^^^^^^^^ PLR6104 +39 | +40 | index = index * (index + 10) + | + = help: Replace with augmented assignment + +ℹ Unsafe fix +35 35 | a_list[0:2] = a_list[0:2] * 3 +36 36 | a_list[:2] = a_list[:2] * 3 +37 37 | a_list[1:] = a_list[1:] * 3 +38 |-a_list[:] = a_list[:] * 3 + 38 |+a_list[:] *= 3 +39 39 | +40 40 | index = index * (index + 10) +41 41 | + +non_augmented_assignment.py:40:1: PLR6104 [*] Use `*=` to perform an augmented assignment directly + | +38 | a_list[:] = a_list[:] * 3 +39 | +40 | index = index * (index + 10) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PLR6104 + | + = help: Replace with augmented assignment + +ℹ Unsafe fix +37 37 | a_list[1:] = a_list[1:] * 3 +38 38 | a_list[:] = a_list[:] * 3 +39 39 | +40 |-index = index * (index + 10) + 40 |+index *= index + 10 +41 41 | +42 42 | +43 43 | class T: + +non_augmented_assignment.py:45:9: PLR6104 [*] Use `+=` to perform an augmented assignment directly + | +43 | class T: +44 | def t(self): +45 | self.a = self.a + 1 + | ^^^^^^^^^^^^^^^^^^^ PLR6104 + | + = help: Replace with augmented assignment + +ℹ Unsafe fix +42 42 | +43 43 | class T: +44 44 | def t(self): +45 |- self.a = self.a + 1 + 45 |+ self.a += 1 +46 46 | +47 47 | +48 48 | obj = T() + +non_augmented_assignment.py:49:1: PLR6104 [*] Use `+=` to perform an augmented assignment directly + | +48 | obj = T() +49 | obj.a = obj.a + 1 + | ^^^^^^^^^^^^^^^^^ PLR6104 +50 | +51 | # OK + | + = help: Replace with augmented assignment + +ℹ Unsafe fix +46 46 | +47 47 | +48 48 | obj = T() +49 |-obj.a = obj.a + 1 + 49 |+obj.a += 1 +50 50 | +51 51 | # OK +52 52 | a_list[0] = a_list[:] * 3 diff --git a/ruff.schema.json b/ruff.schema.json index c236c4a936f293..7082075329f9e4 100644 --- a/ruff.schema.json +++ b/ruff.schema.json @@ -3371,6 +3371,9 @@ "PLR550", "PLR5501", "PLR6", + "PLR61", + "PLR610", + "PLR6104", "PLR62", "PLR620", "PLR6201",