diff --git a/crates/ruff_linter/resources/test/fixtures/pylint/binary_op_and_normal_assignment.py b/crates/ruff_linter/resources/test/fixtures/pylint/binary_op_and_normal_assignment.py new file mode 100644 index 0000000000000..bf42979d15fcd --- /dev/null +++ b/crates/ruff_linter/resources/test/fixtures/pylint/binary_op_and_normal_assignment.py @@ -0,0 +1,44 @@ +some_string = "some string" # PLR6104 +index, a_number, to_multiply, to_divide, to_cube, timeDiffSeconds, flags = 0, 1, 2, 3, 4, 5, 0x3 # PLR6104 +a_list = [1,2] # PLR6104 +some_set = {"elem"} # PLR6104 +mat1, mat2 = None, None # PLR6104 + +some_string = ( + some_string + + "a very long end of string" +) # PLR6104 +index = index - 1 # PLR6104 +a_list = a_list + ["to concat"] # PLR6104 +some_set = some_set | {"to concat"} # PLR6104 +to_multiply = to_multiply * 5 # PLR6104 +to_divide = to_divide / 5 # PLR6104 +to_divide = to_divide // 5 # PLR6104 +to_cube = to_cube ** 3 # PLR6104 +timeDiffSeconds = timeDiffSeconds % 60 # PLR6104 +flags = flags & 0x1 # PLR6104 +flags = flags | 0x1 # PLR6104 +flags = flags ^ 0x1 # PLR6104 +flags = flags << 1 # PLR6104 +flags = flags >> 1 # PLR6104 +mat1 = mat1 @ mat2 # PLR6104 +a_list[1] = a_list[1] + 1 # PLR6104 + +a_list[0:2] = a_list[0:2] * 3 # PLR6104 +a_list[:2] = a_list[:2] * 3 # PLR6104 +a_list[1:] = a_list[1:] * 3 # PLR6104 +a_list[:] = a_list[:] * 3 # PLR6104 + +index = index * (index + 10) # PLR6104 + +class T: + def t(self): + self.a = self.a + 1 # PLR6104 + +obj = T() +obj.a = obj.a + 1 # PLR6104 + +a_list[0] = a_list[:] * 3 # OK +index = a_number = a_number + 1 # OK +a_number = index = a_number + 1 # OK +index = index * index + 10 # OK \ No newline at end of file diff --git a/crates/ruff_linter/src/checkers/ast/analyze/statement.rs b/crates/ruff_linter/src/checkers/ast/analyze/statement.rs index 37bc90fcaca9d..3e43551309bfd 100644 --- a/crates/ruff_linter/src/checkers/ast/analyze/statement.rs +++ b/crates/ruff_linter/src/checkers/ast/analyze/statement.rs @@ -1473,6 +1473,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::BinaryOpAndNormalAssignment) { + pylint::rules::binary_op_and_normal_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 8e275cc3712e0..a2580c2a3744f 100644 --- a/crates/ruff_linter/src/codes.rs +++ b/crates/ruff_linter/src/codes.rs @@ -283,6 +283,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::BinaryOpAndNormalAssignment), (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 518dd781459d5..06b6c6cebe097 100644 --- a/crates/ruff_linter/src/rules/pylint/mod.rs +++ b/crates/ruff_linter/src/rules/pylint/mod.rs @@ -175,6 +175,10 @@ mod tests { Rule::UnnecessaryDictIndexLookup, Path::new("unnecessary_dict_index_lookup.py") )] + #[test_case( + Rule::BinaryOpAndNormalAssignment, + Path::new("binary_op_and_normal_assignment.py") + )] fn rules(rule_code: Rule, path: &Path) -> Result<()> { let snapshot = format!("{}_{}", rule_code.noqa_code(), path.to_string_lossy()); let diagnostics = test_path( diff --git a/crates/ruff_linter/src/rules/pylint/rules/binary_op_and_normal_assignment.rs b/crates/ruff_linter/src/rules/pylint/rules/binary_op_and_normal_assignment.rs new file mode 100644 index 0000000000000..622cef0931f77 --- /dev/null +++ b/crates/ruff_linter/src/rules/pylint/rules/binary_op_and_normal_assignment.rs @@ -0,0 +1,230 @@ +use ast::{Expr, StmtAugAssign}; +use ruff_diagnostics::{Diagnostic, Edit, Fix, FixAvailability, Violation}; +use ruff_macros::{derive_message_formats, violation}; +use ruff_python_ast as ast; +use ruff_text_size::{Ranged, TextRange}; + +use crate::checkers::ast::Checker; + +/// ## What it does +/// Checks for normal assignment statements whose target is the same as the +/// left operand of a binary operator, in which cases, augmented assignment +/// could potentially be used instead. +/// +/// ## Why is this bad? +/// Augmented assignment operators are more concise to perform a binary +/// operation and assign results back to one of the operands. +/// +/// ## Example +/// ```python +/// a = a + 1 +/// ``` +/// +/// Use instead: +/// ```python +/// a += 1 +/// ``` +/// +/// ## Fix safety +/// This rule's fix is marked as being unsafe, in that it could alter semantics +/// of the given Python code in some scenarios. +/// +/// For example, the following code using mutable data types as the assignment +/// target +/// ```python +/// a = [1] +/// b = a +/// a = a + [2] +/// assert (a, b) == ([1, 2], [1]) +/// ``` +/// +/// is not the same as +/// ```python +/// a = [1] +/// b = a +/// a += [2] +/// assert (a, b) == ([1, 2], [1, 2]) +/// ``` +#[violation] +pub struct BinaryOpAndNormalAssignment; + +impl Violation for BinaryOpAndNormalAssignment { + const FIX_AVAILABILITY: FixAvailability = FixAvailability::Sometimes; + + #[derive_message_formats] + fn message(&self) -> String { + format!( + "Normal assignment with left operand of binary operator being the same as the target." + ) + } + + fn fix_title(&self) -> Option { + Some("Use augmented assignment instead.".to_string()) + } +} + +pub(crate) fn binary_op_and_normal_assignment( + checker: &mut Checker, + assign @ ast::StmtAssign { value, targets, .. }: &ast::StmtAssign, +) { + if targets.len() != 1 { + return; + } + let target = targets.first().unwrap(); + + let rhs_expr = value + .as_bin_op_expr() + .map(|e| (e.left.as_ref(), e.op, e.right.as_ref())); + if rhs_expr.is_none() { + return; + } + let (left_operand, operator, right_operand) = rhs_expr.unwrap(); + + if name_expr(target, left_operand) + || object_attribute_expr(target, left_operand) + || index_subscript_expr(target, left_operand) + || slice_subscript_expr(target, left_operand) + { + let diagnostic = Diagnostic::new(BinaryOpAndNormalAssignment, assign.range()).with_fix( + generate_fix(checker, target, operator, right_operand, assign.range()), + ); + checker.diagnostics.push(diagnostic); + } +} + +fn name_expr(target: &Expr, left_operand: &Expr) -> bool { + matches!( + ( + target.as_name_expr(), + left_operand.as_name_expr() + ), + ( + Some(ast::ExprName { + id: target_name_id, .. + }), + Some(ast::ExprName { + id: left_name_id, .. + }), + ) if target_name_id == left_name_id + ) +} + +fn object_attribute_expr(target: &Expr, left_operand: &Expr) -> bool { + matches!(( + target + .as_attribute_expr() + .and_then(|attr| attr.value.as_name_expr()) + .map(|name| &name.id), + target + .as_attribute_expr() + .map(|attr| attr.attr.as_str()), + left_operand + .as_attribute_expr() + .and_then(|attr| attr.value.as_name_expr()) + .map(|name| &name.id), + left_operand + .as_attribute_expr() + .map(|attr| attr.attr.as_str()) + ), + ( + Some(target_name_id), + Some(target_attr_id), + Some(left_name_id), + Some(left_attr_id) + ) + if target_name_id == left_name_id && target_attr_id == left_attr_id + ) +} + +fn index_subscript_expr(target: &Expr, left_operand: &Expr) -> bool { + matches!(( + target + .as_subscript_expr() + .and_then(|subs| subs.value.as_name_expr()) + .map(|name| &name.id), + target + .as_subscript_expr() + .and_then(|subs| subs.slice.as_number_literal_expr()) + .map(|num| &num.value), + left_operand + .as_subscript_expr() + .and_then(|subs| subs.value.as_name_expr()) + .map(|name| &name.id), + left_operand + .as_subscript_expr() + .and_then(|subs| subs.slice.as_number_literal_expr()) + .map(|num| &num.value), + ), + ( + Some(target_name), + Some(target_subs), + Some(left_name), + Some(left_subs) + ) + if target_name == left_name && target_subs == left_subs + ) +} + +fn slice_subscript_expr(target: &Expr, left_operand: &Expr) -> bool { + match ( + target + .as_subscript_expr() + .and_then(|subs| subs.value.as_name_expr()) + .map(|name| &name.id), + target + .as_subscript_expr() + .and_then(|subs| subs.slice.as_slice_expr()), + left_operand + .as_subscript_expr() + .and_then(|subs| subs.value.as_name_expr()) + .map(|name| &name.id), + left_operand + .as_subscript_expr() + .and_then(|subs| subs.slice.as_slice_expr()), + ) { + (Some(target_name), Some(target_slice), Some(left_name), Some(left_slice)) + if target_name == left_name => + { + let target_lower = target_slice + .lower + .as_ref() + .and_then(|lower| lower.as_number_literal_expr()) + .map(|num| &num.value); + let target_upper = target_slice + .upper + .as_ref() + .and_then(|upper| upper.as_number_literal_expr()) + .map(|num| &num.value); + let left_lower = left_slice + .lower + .as_ref() + .and_then(|lower| lower.as_number_literal_expr()) + .map(|num| &num.value); + let left_upper = left_slice + .upper + .as_ref() + .and_then(|upper| upper.as_number_literal_expr()) + .map(|num| &num.value); + + target_lower == left_lower && target_upper == left_upper + } + _ => false, + } +} + +fn generate_fix( + checker: &Checker, + target: &Expr, + operator: ast::Operator, + right_operand: &Expr, + text_range: TextRange, +) -> Fix { + let new_stmt = ast::Stmt::AugAssign(StmtAugAssign { + range: TextRange::default(), + target: Box::new(target.clone()), + op: operator, + value: Box::new(right_operand.clone()), + }); + let content = checker.generator().stmt(&new_stmt); + Fix::unsafe_edit(Edit::range_replacement(content, text_range)) +} diff --git a/crates/ruff_linter/src/rules/pylint/rules/mod.rs b/crates/ruff_linter/src/rules/pylint/rules/mod.rs index 3ba5d1061b7d2..13062391c01fb 100644 --- a/crates/ruff_linter/src/rules/pylint/rules/mod.rs +++ b/crates/ruff_linter/src/rules/pylint/rules/mod.rs @@ -7,6 +7,7 @@ pub(crate) use bad_str_strip_call::*; pub(crate) use bad_string_format_character::BadStringFormatCharacter; pub(crate) use bad_string_format_type::*; pub(crate) use bidirectional_unicode::*; +pub(crate) use binary_op_and_normal_assignment::*; pub(crate) use binary_op_exception::*; pub(crate) use collapsible_else_if::*; pub(crate) use compare_to_empty_string::*; @@ -92,6 +93,7 @@ mod bad_str_strip_call; pub(crate) mod bad_string_format_character; mod bad_string_format_type; mod bidirectional_unicode; +mod binary_op_and_normal_assignment; mod binary_op_exception; mod collapsible_else_if; mod compare_to_empty_string; diff --git a/crates/ruff_linter/src/rules/pylint/snapshots/ruff_linter__rules__pylint__tests__PLR6104_binary_op_and_normal_assignment.py.snap b/crates/ruff_linter/src/rules/pylint/snapshots/ruff_linter__rules__pylint__tests__PLR6104_binary_op_and_normal_assignment.py.snap new file mode 100644 index 0000000000000..96885dc4293aa --- /dev/null +++ b/crates/ruff_linter/src/rules/pylint/snapshots/ruff_linter__rules__pylint__tests__PLR6104_binary_op_and_normal_assignment.py.snap @@ -0,0 +1,489 @@ +--- +source: crates/ruff_linter/src/rules/pylint/mod.rs +--- +binary_op_and_normal_assignment.py:7:1: PLR6104 [*] Normal assignment with left operand of binary operator being the same as the target. + | + 5 | mat1, mat2 = None, None # PLR6104 + 6 | + 7 | / some_string = ( + 8 | | some_string + 9 | | + "a very long end of string" +10 | | ) # PLR6104 + | |_^ PLR6104 +11 | index = index - 1 # PLR6104 +12 | a_list = a_list + ["to concat"] # PLR6104 + | + = help: Use augmented assignment instead. + +ℹ Unsafe fix +4 4 | some_set = {"elem"} # PLR6104 +5 5 | mat1, mat2 = None, None # PLR6104 +6 6 | +7 |-some_string = ( +8 |- some_string +9 |- + "a very long end of string" +10 |-) # PLR6104 + 7 |+some_string += "a very long end of string" # PLR6104 +11 8 | index = index - 1 # PLR6104 +12 9 | a_list = a_list + ["to concat"] # PLR6104 +13 10 | some_set = some_set | {"to concat"} # PLR6104 + +binary_op_and_normal_assignment.py:11:1: PLR6104 [*] Normal assignment with left operand of binary operator being the same as the target. + | + 9 | + "a very long end of string" +10 | ) # PLR6104 +11 | index = index - 1 # PLR6104 + | ^^^^^^^^^^^^^^^^^ PLR6104 +12 | a_list = a_list + ["to concat"] # PLR6104 +13 | some_set = some_set | {"to concat"} # PLR6104 + | + = help: Use augmented assignment instead. + +ℹ Unsafe fix +8 8 | some_string +9 9 | + "a very long end of string" +10 10 | ) # PLR6104 +11 |-index = index - 1 # PLR6104 + 11 |+index -= 1 # PLR6104 +12 12 | a_list = a_list + ["to concat"] # PLR6104 +13 13 | some_set = some_set | {"to concat"} # PLR6104 +14 14 | to_multiply = to_multiply * 5 # PLR6104 + +binary_op_and_normal_assignment.py:12:1: PLR6104 [*] Normal assignment with left operand of binary operator being the same as the target. + | +10 | ) # PLR6104 +11 | index = index - 1 # PLR6104 +12 | a_list = a_list + ["to concat"] # PLR6104 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PLR6104 +13 | some_set = some_set | {"to concat"} # PLR6104 +14 | to_multiply = to_multiply * 5 # PLR6104 + | + = help: Use augmented assignment instead. + +ℹ Unsafe fix +9 9 | + "a very long end of string" +10 10 | ) # PLR6104 +11 11 | index = index - 1 # PLR6104 +12 |-a_list = a_list + ["to concat"] # PLR6104 + 12 |+a_list += ["to concat"] # PLR6104 +13 13 | some_set = some_set | {"to concat"} # PLR6104 +14 14 | to_multiply = to_multiply * 5 # PLR6104 +15 15 | to_divide = to_divide / 5 # PLR6104 + +binary_op_and_normal_assignment.py:13:1: PLR6104 [*] Normal assignment with left operand of binary operator being the same as the target. + | +11 | index = index - 1 # PLR6104 +12 | a_list = a_list + ["to concat"] # PLR6104 +13 | some_set = some_set | {"to concat"} # PLR6104 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PLR6104 +14 | to_multiply = to_multiply * 5 # PLR6104 +15 | to_divide = to_divide / 5 # PLR6104 + | + = help: Use augmented assignment instead. + +ℹ Unsafe fix +10 10 | ) # PLR6104 +11 11 | index = index - 1 # PLR6104 +12 12 | a_list = a_list + ["to concat"] # PLR6104 +13 |-some_set = some_set | {"to concat"} # PLR6104 + 13 |+some_set |= {"to concat"} # PLR6104 +14 14 | to_multiply = to_multiply * 5 # PLR6104 +15 15 | to_divide = to_divide / 5 # PLR6104 +16 16 | to_divide = to_divide // 5 # PLR6104 + +binary_op_and_normal_assignment.py:14:1: PLR6104 [*] Normal assignment with left operand of binary operator being the same as the target. + | +12 | a_list = a_list + ["to concat"] # PLR6104 +13 | some_set = some_set | {"to concat"} # PLR6104 +14 | to_multiply = to_multiply * 5 # PLR6104 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PLR6104 +15 | to_divide = to_divide / 5 # PLR6104 +16 | to_divide = to_divide // 5 # PLR6104 + | + = help: Use augmented assignment instead. + +ℹ Unsafe fix +11 11 | index = index - 1 # PLR6104 +12 12 | a_list = a_list + ["to concat"] # PLR6104 +13 13 | some_set = some_set | {"to concat"} # PLR6104 +14 |-to_multiply = to_multiply * 5 # PLR6104 + 14 |+to_multiply *= 5 # PLR6104 +15 15 | to_divide = to_divide / 5 # PLR6104 +16 16 | to_divide = to_divide // 5 # PLR6104 +17 17 | to_cube = to_cube ** 3 # PLR6104 + +binary_op_and_normal_assignment.py:15:1: PLR6104 [*] Normal assignment with left operand of binary operator being the same as the target. + | +13 | some_set = some_set | {"to concat"} # PLR6104 +14 | to_multiply = to_multiply * 5 # PLR6104 +15 | to_divide = to_divide / 5 # PLR6104 + | ^^^^^^^^^^^^^^^^^^^^^^^^^ PLR6104 +16 | to_divide = to_divide // 5 # PLR6104 +17 | to_cube = to_cube ** 3 # PLR6104 + | + = help: Use augmented assignment instead. + +ℹ Unsafe fix +12 12 | a_list = a_list + ["to concat"] # PLR6104 +13 13 | some_set = some_set | {"to concat"} # PLR6104 +14 14 | to_multiply = to_multiply * 5 # PLR6104 +15 |-to_divide = to_divide / 5 # PLR6104 + 15 |+to_divide /= 5 # PLR6104 +16 16 | to_divide = to_divide // 5 # PLR6104 +17 17 | to_cube = to_cube ** 3 # PLR6104 +18 18 | timeDiffSeconds = timeDiffSeconds % 60 # PLR6104 + +binary_op_and_normal_assignment.py:16:1: PLR6104 [*] Normal assignment with left operand of binary operator being the same as the target. + | +14 | to_multiply = to_multiply * 5 # PLR6104 +15 | to_divide = to_divide / 5 # PLR6104 +16 | to_divide = to_divide // 5 # PLR6104 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ PLR6104 +17 | to_cube = to_cube ** 3 # PLR6104 +18 | timeDiffSeconds = timeDiffSeconds % 60 # PLR6104 + | + = help: Use augmented assignment instead. + +ℹ Unsafe fix +13 13 | some_set = some_set | {"to concat"} # PLR6104 +14 14 | to_multiply = to_multiply * 5 # PLR6104 +15 15 | to_divide = to_divide / 5 # PLR6104 +16 |-to_divide = to_divide // 5 # PLR6104 + 16 |+to_divide //= 5 # PLR6104 +17 17 | to_cube = to_cube ** 3 # PLR6104 +18 18 | timeDiffSeconds = timeDiffSeconds % 60 # PLR6104 +19 19 | flags = flags & 0x1 # PLR6104 + +binary_op_and_normal_assignment.py:17:1: PLR6104 [*] Normal assignment with left operand of binary operator being the same as the target. + | +15 | to_divide = to_divide / 5 # PLR6104 +16 | to_divide = to_divide // 5 # PLR6104 +17 | to_cube = to_cube ** 3 # PLR6104 + | ^^^^^^^^^^^^^^^^^^^^^^ PLR6104 +18 | timeDiffSeconds = timeDiffSeconds % 60 # PLR6104 +19 | flags = flags & 0x1 # PLR6104 + | + = help: Use augmented assignment instead. + +ℹ Unsafe fix +14 14 | to_multiply = to_multiply * 5 # PLR6104 +15 15 | to_divide = to_divide / 5 # PLR6104 +16 16 | to_divide = to_divide // 5 # PLR6104 +17 |-to_cube = to_cube ** 3 # PLR6104 + 17 |+to_cube **= 3 # PLR6104 +18 18 | timeDiffSeconds = timeDiffSeconds % 60 # PLR6104 +19 19 | flags = flags & 0x1 # PLR6104 +20 20 | flags = flags | 0x1 # PLR6104 + +binary_op_and_normal_assignment.py:18:1: PLR6104 [*] Normal assignment with left operand of binary operator being the same as the target. + | +16 | to_divide = to_divide // 5 # PLR6104 +17 | to_cube = to_cube ** 3 # PLR6104 +18 | timeDiffSeconds = timeDiffSeconds % 60 # PLR6104 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PLR6104 +19 | flags = flags & 0x1 # PLR6104 +20 | flags = flags | 0x1 # PLR6104 + | + = help: Use augmented assignment instead. + +ℹ Unsafe fix +15 15 | to_divide = to_divide / 5 # PLR6104 +16 16 | to_divide = to_divide // 5 # PLR6104 +17 17 | to_cube = to_cube ** 3 # PLR6104 +18 |-timeDiffSeconds = timeDiffSeconds % 60 # PLR6104 + 18 |+timeDiffSeconds %= 60 # PLR6104 +19 19 | flags = flags & 0x1 # PLR6104 +20 20 | flags = flags | 0x1 # PLR6104 +21 21 | flags = flags ^ 0x1 # PLR6104 + +binary_op_and_normal_assignment.py:19:1: PLR6104 [*] Normal assignment with left operand of binary operator being the same as the target. + | +17 | to_cube = to_cube ** 3 # PLR6104 +18 | timeDiffSeconds = timeDiffSeconds % 60 # PLR6104 +19 | flags = flags & 0x1 # PLR6104 + | ^^^^^^^^^^^^^^^^^^^ PLR6104 +20 | flags = flags | 0x1 # PLR6104 +21 | flags = flags ^ 0x1 # PLR6104 + | + = help: Use augmented assignment instead. + +ℹ Unsafe fix +16 16 | to_divide = to_divide // 5 # PLR6104 +17 17 | to_cube = to_cube ** 3 # PLR6104 +18 18 | timeDiffSeconds = timeDiffSeconds % 60 # PLR6104 +19 |-flags = flags & 0x1 # PLR6104 + 19 |+flags &= 1 # PLR6104 +20 20 | flags = flags | 0x1 # PLR6104 +21 21 | flags = flags ^ 0x1 # PLR6104 +22 22 | flags = flags << 1 # PLR6104 + +binary_op_and_normal_assignment.py:20:1: PLR6104 [*] Normal assignment with left operand of binary operator being the same as the target. + | +18 | timeDiffSeconds = timeDiffSeconds % 60 # PLR6104 +19 | flags = flags & 0x1 # PLR6104 +20 | flags = flags | 0x1 # PLR6104 + | ^^^^^^^^^^^^^^^^^^^ PLR6104 +21 | flags = flags ^ 0x1 # PLR6104 +22 | flags = flags << 1 # PLR6104 + | + = help: Use augmented assignment instead. + +ℹ Unsafe fix +17 17 | to_cube = to_cube ** 3 # PLR6104 +18 18 | timeDiffSeconds = timeDiffSeconds % 60 # PLR6104 +19 19 | flags = flags & 0x1 # PLR6104 +20 |-flags = flags | 0x1 # PLR6104 + 20 |+flags |= 1 # PLR6104 +21 21 | flags = flags ^ 0x1 # PLR6104 +22 22 | flags = flags << 1 # PLR6104 +23 23 | flags = flags >> 1 # PLR6104 + +binary_op_and_normal_assignment.py:21:1: PLR6104 [*] Normal assignment with left operand of binary operator being the same as the target. + | +19 | flags = flags & 0x1 # PLR6104 +20 | flags = flags | 0x1 # PLR6104 +21 | flags = flags ^ 0x1 # PLR6104 + | ^^^^^^^^^^^^^^^^^^^ PLR6104 +22 | flags = flags << 1 # PLR6104 +23 | flags = flags >> 1 # PLR6104 + | + = help: Use augmented assignment instead. + +ℹ Unsafe fix +18 18 | timeDiffSeconds = timeDiffSeconds % 60 # PLR6104 +19 19 | flags = flags & 0x1 # PLR6104 +20 20 | flags = flags | 0x1 # PLR6104 +21 |-flags = flags ^ 0x1 # PLR6104 + 21 |+flags ^= 1 # PLR6104 +22 22 | flags = flags << 1 # PLR6104 +23 23 | flags = flags >> 1 # PLR6104 +24 24 | mat1 = mat1 @ mat2 # PLR6104 + +binary_op_and_normal_assignment.py:22:1: PLR6104 [*] Normal assignment with left operand of binary operator being the same as the target. + | +20 | flags = flags | 0x1 # PLR6104 +21 | flags = flags ^ 0x1 # PLR6104 +22 | flags = flags << 1 # PLR6104 + | ^^^^^^^^^^^^^^^^^^ PLR6104 +23 | flags = flags >> 1 # PLR6104 +24 | mat1 = mat1 @ mat2 # PLR6104 + | + = help: Use augmented assignment instead. + +ℹ Unsafe fix +19 19 | flags = flags & 0x1 # PLR6104 +20 20 | flags = flags | 0x1 # PLR6104 +21 21 | flags = flags ^ 0x1 # PLR6104 +22 |-flags = flags << 1 # PLR6104 + 22 |+flags <<= 1 # PLR6104 +23 23 | flags = flags >> 1 # PLR6104 +24 24 | mat1 = mat1 @ mat2 # PLR6104 +25 25 | a_list[1] = a_list[1] + 1 # PLR6104 + +binary_op_and_normal_assignment.py:23:1: PLR6104 [*] Normal assignment with left operand of binary operator being the same as the target. + | +21 | flags = flags ^ 0x1 # PLR6104 +22 | flags = flags << 1 # PLR6104 +23 | flags = flags >> 1 # PLR6104 + | ^^^^^^^^^^^^^^^^^^ PLR6104 +24 | mat1 = mat1 @ mat2 # PLR6104 +25 | a_list[1] = a_list[1] + 1 # PLR6104 + | + = help: Use augmented assignment instead. + +ℹ Unsafe fix +20 20 | flags = flags | 0x1 # PLR6104 +21 21 | flags = flags ^ 0x1 # PLR6104 +22 22 | flags = flags << 1 # PLR6104 +23 |-flags = flags >> 1 # PLR6104 + 23 |+flags >>= 1 # PLR6104 +24 24 | mat1 = mat1 @ mat2 # PLR6104 +25 25 | a_list[1] = a_list[1] + 1 # PLR6104 +26 26 | + +binary_op_and_normal_assignment.py:24:1: PLR6104 [*] Normal assignment with left operand of binary operator being the same as the target. + | +22 | flags = flags << 1 # PLR6104 +23 | flags = flags >> 1 # PLR6104 +24 | mat1 = mat1 @ mat2 # PLR6104 + | ^^^^^^^^^^^^^^^^^^ PLR6104 +25 | a_list[1] = a_list[1] + 1 # PLR6104 + | + = help: Use augmented assignment instead. + +ℹ Unsafe fix +21 21 | flags = flags ^ 0x1 # PLR6104 +22 22 | flags = flags << 1 # PLR6104 +23 23 | flags = flags >> 1 # PLR6104 +24 |-mat1 = mat1 @ mat2 # PLR6104 + 24 |+mat1 @= mat2 # PLR6104 +25 25 | a_list[1] = a_list[1] + 1 # PLR6104 +26 26 | +27 27 | a_list[0:2] = a_list[0:2] * 3 # PLR6104 + +binary_op_and_normal_assignment.py:25:1: PLR6104 [*] Normal assignment with left operand of binary operator being the same as the target. + | +23 | flags = flags >> 1 # PLR6104 +24 | mat1 = mat1 @ mat2 # PLR6104 +25 | a_list[1] = a_list[1] + 1 # PLR6104 + | ^^^^^^^^^^^^^^^^^^^^^^^^^ PLR6104 +26 | +27 | a_list[0:2] = a_list[0:2] * 3 # PLR6104 + | + = help: Use augmented assignment instead. + +ℹ Unsafe fix +22 22 | flags = flags << 1 # PLR6104 +23 23 | flags = flags >> 1 # PLR6104 +24 24 | mat1 = mat1 @ mat2 # PLR6104 +25 |-a_list[1] = a_list[1] + 1 # PLR6104 + 25 |+a_list[1] += 1 # PLR6104 +26 26 | +27 27 | a_list[0:2] = a_list[0:2] * 3 # PLR6104 +28 28 | a_list[:2] = a_list[:2] * 3 # PLR6104 + +binary_op_and_normal_assignment.py:27:1: PLR6104 [*] Normal assignment with left operand of binary operator being the same as the target. + | +25 | a_list[1] = a_list[1] + 1 # PLR6104 +26 | +27 | a_list[0:2] = a_list[0:2] * 3 # PLR6104 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PLR6104 +28 | a_list[:2] = a_list[:2] * 3 # PLR6104 +29 | a_list[1:] = a_list[1:] * 3 # PLR6104 + | + = help: Use augmented assignment instead. + +ℹ Unsafe fix +24 24 | mat1 = mat1 @ mat2 # PLR6104 +25 25 | a_list[1] = a_list[1] + 1 # PLR6104 +26 26 | +27 |-a_list[0:2] = a_list[0:2] * 3 # PLR6104 + 27 |+a_list[0:2] *= 3 # PLR6104 +28 28 | a_list[:2] = a_list[:2] * 3 # PLR6104 +29 29 | a_list[1:] = a_list[1:] * 3 # PLR6104 +30 30 | a_list[:] = a_list[:] * 3 # PLR6104 + +binary_op_and_normal_assignment.py:28:1: PLR6104 [*] Normal assignment with left operand of binary operator being the same as the target. + | +27 | a_list[0:2] = a_list[0:2] * 3 # PLR6104 +28 | a_list[:2] = a_list[:2] * 3 # PLR6104 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ PLR6104 +29 | a_list[1:] = a_list[1:] * 3 # PLR6104 +30 | a_list[:] = a_list[:] * 3 # PLR6104 + | + = help: Use augmented assignment instead. + +ℹ Unsafe fix +25 25 | a_list[1] = a_list[1] + 1 # PLR6104 +26 26 | +27 27 | a_list[0:2] = a_list[0:2] * 3 # PLR6104 +28 |-a_list[:2] = a_list[:2] * 3 # PLR6104 + 28 |+a_list[:2] *= 3 # PLR6104 +29 29 | a_list[1:] = a_list[1:] * 3 # PLR6104 +30 30 | a_list[:] = a_list[:] * 3 # PLR6104 +31 31 | + +binary_op_and_normal_assignment.py:29:1: PLR6104 [*] Normal assignment with left operand of binary operator being the same as the target. + | +27 | a_list[0:2] = a_list[0:2] * 3 # PLR6104 +28 | a_list[:2] = a_list[:2] * 3 # PLR6104 +29 | a_list[1:] = a_list[1:] * 3 # PLR6104 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ PLR6104 +30 | a_list[:] = a_list[:] * 3 # PLR6104 + | + = help: Use augmented assignment instead. + +ℹ Unsafe fix +26 26 | +27 27 | a_list[0:2] = a_list[0:2] * 3 # PLR6104 +28 28 | a_list[:2] = a_list[:2] * 3 # PLR6104 +29 |-a_list[1:] = a_list[1:] * 3 # PLR6104 + 29 |+a_list[1:] *= 3 # PLR6104 +30 30 | a_list[:] = a_list[:] * 3 # PLR6104 +31 31 | +32 32 | index = index * (index + 10) # PLR6104 + +binary_op_and_normal_assignment.py:30:1: PLR6104 [*] Normal assignment with left operand of binary operator being the same as the target. + | +28 | a_list[:2] = a_list[:2] * 3 # PLR6104 +29 | a_list[1:] = a_list[1:] * 3 # PLR6104 +30 | a_list[:] = a_list[:] * 3 # PLR6104 + | ^^^^^^^^^^^^^^^^^^^^^^^^^ PLR6104 +31 | +32 | index = index * (index + 10) # PLR6104 + | + = help: Use augmented assignment instead. + +ℹ Unsafe fix +27 27 | a_list[0:2] = a_list[0:2] * 3 # PLR6104 +28 28 | a_list[:2] = a_list[:2] * 3 # PLR6104 +29 29 | a_list[1:] = a_list[1:] * 3 # PLR6104 +30 |-a_list[:] = a_list[:] * 3 # PLR6104 + 30 |+a_list[:] *= 3 # PLR6104 +31 31 | +32 32 | index = index * (index + 10) # PLR6104 +33 33 | + +binary_op_and_normal_assignment.py:32:1: PLR6104 [*] Normal assignment with left operand of binary operator being the same as the target. + | +30 | a_list[:] = a_list[:] * 3 # PLR6104 +31 | +32 | index = index * (index + 10) # PLR6104 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PLR6104 +33 | +34 | class T: + | + = help: Use augmented assignment instead. + +ℹ Unsafe fix +29 29 | a_list[1:] = a_list[1:] * 3 # PLR6104 +30 30 | a_list[:] = a_list[:] * 3 # PLR6104 +31 31 | +32 |-index = index * (index + 10) # PLR6104 + 32 |+index *= index + 10 # PLR6104 +33 33 | +34 34 | class T: +35 35 | def t(self): + +binary_op_and_normal_assignment.py:36:9: PLR6104 [*] Normal assignment with left operand of binary operator being the same as the target. + | +34 | class T: +35 | def t(self): +36 | self.a = self.a + 1 # PLR6104 + | ^^^^^^^^^^^^^^^^^^^ PLR6104 +37 | +38 | obj = T() + | + = help: Use augmented assignment instead. + +ℹ Unsafe fix +33 33 | +34 34 | class T: +35 35 | def t(self): +36 |- self.a = self.a + 1 # PLR6104 + 36 |+ self.a += 1 # PLR6104 +37 37 | +38 38 | obj = T() +39 39 | obj.a = obj.a + 1 # PLR6104 + +binary_op_and_normal_assignment.py:39:1: PLR6104 [*] Normal assignment with left operand of binary operator being the same as the target. + | +38 | obj = T() +39 | obj.a = obj.a + 1 # PLR6104 + | ^^^^^^^^^^^^^^^^^ PLR6104 +40 | +41 | a_list[0] = a_list[:] * 3 # OK + | + = help: Use augmented assignment instead. + +ℹ Unsafe fix +36 36 | self.a = self.a + 1 # PLR6104 +37 37 | +38 38 | obj = T() +39 |-obj.a = obj.a + 1 # PLR6104 + 39 |+obj.a += 1 # PLR6104 +40 40 | +41 41 | a_list[0] = a_list[:] * 3 # OK +42 42 | index = a_number = a_number + 1 # OK + + diff --git a/ruff.schema.json b/ruff.schema.json index 369c973256cc5..363bb29219afa 100644 --- a/ruff.schema.json +++ b/ruff.schema.json @@ -3294,6 +3294,9 @@ "PLR550", "PLR5501", "PLR6", + "PLR61", + "PLR610", + "PLR6104", "PLR62", "PLR620", "PLR6201",