diff --git a/crates/ruff_linter/resources/test/fixtures/pylint/unnecessary_dunder_call.py b/crates/ruff_linter/resources/test/fixtures/pylint/unnecessary_dunder_call.py index d8971714befe8..2f46174fd3822 100644 --- a/crates/ruff_linter/resources/test/fixtures/pylint/unnecessary_dunder_call.py +++ b/crates/ruff_linter/resources/test/fixtures/pylint/unnecessary_dunder_call.py @@ -48,6 +48,21 @@ # Calls print(a.__call__()) # PLC2801 (no fix, intentional) +class Foo: + def __init__(self, v): + self.v = v + + def __add__(self, other): + self.v += other + return self + + def get_v(self): + return self.v + +foo = Foo(1) +foo.__add__(2).get_v() # PLC2801 + + # Lambda expressions blah = lambda: a.__add__(1) # PLC2801 @@ -72,13 +87,22 @@ # Subscripts print({"a": a.__add__(1)}["a"]) # PLC2801 +# https://github.com/astral-sh/ruff/issues/15745 +print("x".__add__("y")[0]) # PLC2801 # Starred print(*[a.__add__(1)]) # PLC2801 +list1 = [1, 2, 3] +list2 = [4, 5, 6] +print([*list1.__add__(list2)]) # PLC2801 + # Slices print([a.__add__(1), a.__sub__(1)][0:1]) # PLC2801 +# Attribute access +# https://github.com/astral-sh/ruff/issues/15745 +print(1j.__add__(1.0).real) # PLC2801 class Thing: def __init__(self, stuff: Any) -> None: diff --git a/crates/ruff_linter/src/rules/pylint/rules/unnecessary_dunder_call.rs b/crates/ruff_linter/src/rules/pylint/rules/unnecessary_dunder_call.rs index 9120d39d5ce86..2f910875789a9 100644 --- a/crates/ruff_linter/src/rules/pylint/rules/unnecessary_dunder_call.rs +++ b/crates/ruff_linter/src/rules/pylint/rules/unnecessary_dunder_call.rs @@ -1,6 +1,6 @@ use ruff_diagnostics::{Diagnostic, Edit, Fix, FixAvailability, Violation}; use ruff_macros::{derive_message_formats, ViolationMetadata}; -use ruff_python_ast::{self as ast, Expr, Stmt}; +use ruff_python_ast::{self as ast, BoolOp, Expr, Operator, Stmt, UnaryOp}; use ruff_python_semantic::SemanticModel; use ruff_text_size::Ranged; @@ -107,7 +107,9 @@ pub(crate) fn unnecessary_dunder_call(checker: &mut Checker, call: &ast::ExprCal return; } - let mut fixed: Option = None; + // If a fix is available, we'll store the text of the fixed expression here + // along with the precedence of the resulting expression. + let mut fixed: Option<(String, OperatorPrecedence)> = None; let mut title: Option = None; if let Some(dunder) = DunderReplacement::from_method(attr) { @@ -116,37 +118,52 @@ pub(crate) fn unnecessary_dunder_call(checker: &mut Checker, call: &ast::ExprCal if !checker.semantic().has_builtin_binding(replacement) { return; } - fixed = Some(format!( - "{}({})", - replacement, - checker.locator().slice(value.as_ref()), + fixed = Some(( + format!( + "{}({})", + replacement, + checker.locator().slice(value.as_ref()), + ), + OperatorPrecedence::CallAttribute, )); title = Some(message.to_string()); } - ([arg], DunderReplacement::Operator(replacement, message)) => { + ([arg], DunderReplacement::Operator(replacement, message, precedence)) => { let value_slice = checker.locator().slice(value.as_ref()); let arg_slice = checker.locator().slice(arg); - if can_be_represented_without_parentheses(arg) { + if OperatorPrecedence::from_expr(arg) > precedence { // if it's something that can reasonably be removed from parentheses, // we'll do that. - fixed = Some(format!("{value_slice} {replacement} {arg_slice}")); + fixed = Some(( + format!("{value_slice} {replacement} {arg_slice}"), + precedence, + )); } else { - fixed = Some(format!("{value_slice} {replacement} ({arg_slice})")); + fixed = Some(( + format!("{value_slice} {replacement} ({arg_slice})"), + precedence, + )); } title = Some(message.to_string()); } - ([arg], DunderReplacement::ROperator(replacement, message)) => { + ([arg], DunderReplacement::ROperator(replacement, message, precedence)) => { let value_slice = checker.locator().slice(value.as_ref()); let arg_slice = checker.locator().slice(arg); - if arg.is_attribute_expr() || arg.is_name_expr() || arg.is_literal_expr() { + if OperatorPrecedence::from_expr(arg) > precedence { // if it's something that can reasonably be removed from parentheses, // we'll do that. - fixed = Some(format!("{arg_slice} {replacement} {value_slice}")); + fixed = Some(( + format!("{arg_slice} {replacement} {value_slice}"), + precedence, + )); } else { - fixed = Some(format!("({arg_slice}) {replacement} {value_slice}")); + fixed = Some(( + format!("({arg_slice}) {replacement} {value_slice}"), + precedence, + )); } title = Some(message.to_string()); } @@ -165,22 +182,23 @@ pub(crate) fn unnecessary_dunder_call(checker: &mut Checker, call: &ast::ExprCal call.range(), ); - if let Some(mut fixed) = fixed { + if let Some((mut fixed, precedence)) = fixed { let dunder = DunderReplacement::from_method(attr); // We never need to wrap builtin functions in extra parens // since function calls have high precedence let wrap_in_paren = (!matches!(dunder, Some(DunderReplacement::Builtin(_,_)))) - // By the looks of it, we don't need to wrap the expression in - // parens if it's the only argument to a call expression. - // Being in any other kind of expression though, we *will* - // add parens. e.g. `print(a.__add__(3))` -> `print(a + 3)` - // instead of `print((a + 3))` - // and `x = 2 * a.__add__(3)` -> `x = 2 * (a + 3)` + // If parent expression has higher precedence then the new replacement, + // it would associate with either the left operand (e.g. naive change from `a * b.__add__(c)` + // becomes `a * b + c` which is incorrect) or the right operand (e.g. naive change from + // `a.__add__(b).attr` becomes `a + b.attr` which is also incorrect). + // This rule doesn't apply to function calls despite them having higher + // precedence than any of our replacement, since they already wrap around + // our expression e.g. `print(a.__add__(3))` -> `print(a + 3)` && checker .semantic() .current_expression_parent() - .is_some_and(|parent| !can_be_represented_without_parentheses(parent)); + .is_some_and(|parent| !parent.is_call_expr() && OperatorPrecedence::from_expr(parent) > precedence); if wrap_in_paren { fixed = format!("({fixed})"); @@ -240,9 +258,9 @@ fn allowed_dunder_constants(dunder_method: &str, target_version: PythonVersion) #[derive(Debug, Copy, Clone)] enum DunderReplacement { /// A dunder method that is an operator. - Operator(&'static str, &'static str), + Operator(&'static str, &'static str, OperatorPrecedence), /// A dunder method that is a right-side operator. - ROperator(&'static str, &'static str), + ROperator(&'static str, &'static str, OperatorPrecedence), /// A dunder method that is a builtin. Builtin(&'static str, &'static str), /// A dunder method that is a message only. @@ -252,48 +270,212 @@ enum DunderReplacement { impl DunderReplacement { fn from_method(dunder_method: &str) -> Option { match dunder_method { - "__add__" => Some(Self::Operator("+", "Use `+` operator")), - "__and__" => Some(Self::Operator("&", "Use `&` operator")), - "__contains__" => Some(Self::ROperator("in", "Use `in` operator")), - "__eq__" => Some(Self::Operator("==", "Use `==` operator")), - "__floordiv__" => Some(Self::Operator("//", "Use `//` operator")), - "__ge__" => Some(Self::Operator(">=", "Use `>=` operator")), - "__gt__" => Some(Self::Operator(">", "Use `>` operator")), - "__iadd__" => Some(Self::Operator("+=", "Use `+=` operator")), - "__iand__" => Some(Self::Operator("&=", "Use `&=` operator")), - "__ifloordiv__" => Some(Self::Operator("//=", "Use `//=` operator")), - "__ilshift__" => Some(Self::Operator("<<=", "Use `<<=` operator")), - "__imod__" => Some(Self::Operator("%=", "Use `%=` operator")), - "__imul__" => Some(Self::Operator("*=", "Use `*=` operator")), - "__ior__" => Some(Self::Operator("|=", "Use `|=` operator")), - "__ipow__" => Some(Self::Operator("**=", "Use `**=` operator")), - "__irshift__" => Some(Self::Operator(">>=", "Use `>>=` operator")), - "__isub__" => Some(Self::Operator("-=", "Use `-=` operator")), - "__itruediv__" => Some(Self::Operator("/=", "Use `/=` operator")), - "__ixor__" => Some(Self::Operator("^=", "Use `^=` operator")), - "__le__" => Some(Self::Operator("<=", "Use `<=` operator")), - "__lshift__" => Some(Self::Operator("<<", "Use `<<` operator")), - "__lt__" => Some(Self::Operator("<", "Use `<` operator")), - "__mod__" => Some(Self::Operator("%", "Use `%` operator")), - "__mul__" => Some(Self::Operator("*", "Use `*` operator")), - "__ne__" => Some(Self::Operator("!=", "Use `!=` operator")), - "__or__" => Some(Self::Operator("|", "Use `|` operator")), - "__rshift__" => Some(Self::Operator(">>", "Use `>>` operator")), - "__sub__" => Some(Self::Operator("-", "Use `-` operator")), - "__truediv__" => Some(Self::Operator("/", "Use `/` operator")), - "__xor__" => Some(Self::Operator("^", "Use `^` operator")), - - "__radd__" => Some(Self::ROperator("+", "Use `+` operator")), - "__rand__" => Some(Self::ROperator("&", "Use `&` operator")), - "__rfloordiv__" => Some(Self::ROperator("//", "Use `//` operator")), - "__rlshift__" => Some(Self::ROperator("<<", "Use `<<` operator")), - "__rmod__" => Some(Self::ROperator("%", "Use `%` operator")), - "__rmul__" => Some(Self::ROperator("*", "Use `*` operator")), - "__ror__" => Some(Self::ROperator("|", "Use `|` operator")), - "__rrshift__" => Some(Self::ROperator(">>", "Use `>>` operator")), - "__rsub__" => Some(Self::ROperator("-", "Use `-` operator")), - "__rtruediv__" => Some(Self::ROperator("/", "Use `/` operator")), - "__rxor__" => Some(Self::ROperator("^", "Use `^` operator")), + "__add__" => Some(Self::Operator( + "+", + "Use `+` operator", + OperatorPrecedence::AddSub, + )), + "__and__" => Some(Self::Operator( + "&", + "Use `&` operator", + OperatorPrecedence::BitAnd, + )), + "__contains__" => Some(Self::ROperator( + "in", + "Use `in` operator", + OperatorPrecedence::ComparisonsMembershipIdentity, + )), + "__eq__" => Some(Self::Operator( + "==", + "Use `==` operator", + OperatorPrecedence::ComparisonsMembershipIdentity, + )), + "__floordiv__" => Some(Self::Operator( + "//", + "Use `//` operator", + OperatorPrecedence::MulDivRemain, + )), + "__ge__" => Some(Self::Operator( + ">=", + "Use `>=` operator", + OperatorPrecedence::ComparisonsMembershipIdentity, + )), + "__gt__" => Some(Self::Operator( + ">", + "Use `>` operator", + OperatorPrecedence::ComparisonsMembershipIdentity, + )), + "__iadd__" => Some(Self::Operator( + "+=", + "Use `+=` operator", + OperatorPrecedence::Assign, + )), + "__iand__" => Some(Self::Operator( + "&=", + "Use `&=` operator", + OperatorPrecedence::Assign, + )), + "__ifloordiv__" => Some(Self::Operator( + "//=", + "Use `//=` operator", + OperatorPrecedence::Assign, + )), + "__ilshift__" => Some(Self::Operator( + "<<=", + "Use `<<=` operator", + OperatorPrecedence::Assign, + )), + "__imod__" => Some(Self::Operator( + "%=", + "Use `%=` operator", + OperatorPrecedence::Assign, + )), + "__imul__" => Some(Self::Operator( + "*=", + "Use `*=` operator", + OperatorPrecedence::Assign, + )), + "__ior__" => Some(Self::Operator( + "|=", + "Use `|=` operator", + OperatorPrecedence::Assign, + )), + "__ipow__" => Some(Self::Operator( + "**=", + "Use `**=` operator", + OperatorPrecedence::Assign, + )), + "__irshift__" => Some(Self::Operator( + ">>=", + "Use `>>=` operator", + OperatorPrecedence::Assign, + )), + "__isub__" => Some(Self::Operator( + "-=", + "Use `-=` operator", + OperatorPrecedence::Assign, + )), + "__itruediv__" => Some(Self::Operator( + "/=", + "Use `/=` operator", + OperatorPrecedence::Assign, + )), + "__ixor__" => Some(Self::Operator( + "^=", + "Use `^=` operator", + OperatorPrecedence::Assign, + )), + "__le__" => Some(Self::Operator( + "<=", + "Use `<=` operator", + OperatorPrecedence::ComparisonsMembershipIdentity, + )), + "__lshift__" => Some(Self::Operator( + "<<", + "Use `<<` operator", + OperatorPrecedence::LeftRightShift, + )), + "__lt__" => Some(Self::Operator( + "<", + "Use `<` operator", + OperatorPrecedence::ComparisonsMembershipIdentity, + )), + "__mod__" => Some(Self::Operator( + "%", + "Use `%` operator", + OperatorPrecedence::MulDivRemain, + )), + "__mul__" => Some(Self::Operator( + "*", + "Use `*` operator", + OperatorPrecedence::MulDivRemain, + )), + "__ne__" => Some(Self::Operator( + "!=", + "Use `!=` operator", + OperatorPrecedence::ComparisonsMembershipIdentity, + )), + "__or__" => Some(Self::Operator( + "|", + "Use `|` operator", + OperatorPrecedence::BitXorOr, + )), + "__rshift__" => Some(Self::Operator( + ">>", + "Use `>>` operator", + OperatorPrecedence::LeftRightShift, + )), + "__sub__" => Some(Self::Operator( + "-", + "Use `-` operator", + OperatorPrecedence::AddSub, + )), + "__truediv__" => Some(Self::Operator( + "/", + "Use `/` operator", + OperatorPrecedence::MulDivRemain, + )), + "__xor__" => Some(Self::Operator( + "^", + "Use `^` operator", + OperatorPrecedence::BitXorOr, + )), + + "__radd__" => Some(Self::ROperator( + "+", + "Use `+` operator", + OperatorPrecedence::AddSub, + )), + "__rand__" => Some(Self::ROperator( + "&", + "Use `&` operator", + OperatorPrecedence::BitAnd, + )), + "__rfloordiv__" => Some(Self::ROperator( + "//", + "Use `//` operator", + OperatorPrecedence::MulDivRemain, + )), + "__rlshift__" => Some(Self::ROperator( + "<<", + "Use `<<` operator", + OperatorPrecedence::LeftRightShift, + )), + "__rmod__" => Some(Self::ROperator( + "%", + "Use `%` operator", + OperatorPrecedence::MulDivRemain, + )), + "__rmul__" => Some(Self::ROperator( + "*", + "Use `*` operator", + OperatorPrecedence::MulDivRemain, + )), + "__ror__" => Some(Self::ROperator( + "|", + "Use `|` operator", + OperatorPrecedence::BitXorOr, + )), + "__rrshift__" => Some(Self::ROperator( + ">>", + "Use `>>` operator", + OperatorPrecedence::LeftRightShift, + )), + "__rsub__" => Some(Self::ROperator( + "-", + "Use `-` operator", + OperatorPrecedence::AddSub, + )), + "__rtruediv__" => Some(Self::ROperator( + "/", + "Use `/` operator", + OperatorPrecedence::MulDivRemain, + )), + "__rxor__" => Some(Self::ROperator( + "^", + "Use `^` operator", + OperatorPrecedence::BitXorOr, + )), "__aiter__" => Some(Self::Builtin("aiter", "Use `aiter()` builtin")), "__anext__" => Some(Self::Builtin("anext", "Use `anext()` builtin")), @@ -391,23 +573,166 @@ fn in_dunder_method_definition(semantic: &SemanticModel) -> bool { }) } -/// Returns `true` if the [`Expr`] can be represented without parentheses. -fn can_be_represented_without_parentheses(expr: &Expr) -> bool { - expr.is_attribute_expr() - || expr.is_name_expr() - || expr.is_literal_expr() - || expr.is_call_expr() - || expr.is_lambda_expr() - || expr.is_if_expr() - || expr.is_generator_expr() - || expr.is_subscript_expr() - || expr.is_starred_expr() - || expr.is_slice_expr() - || expr.is_dict_expr() - || expr.is_dict_comp_expr() - || expr.is_list_expr() - || expr.is_list_comp_expr() - || expr.is_tuple_expr() - || expr.is_set_comp_expr() - || expr.is_set_expr() +/// Represents the precedence levels for Python expressions. +/// Variants at the top have lower precedence and variants at the bottom have +/// higher precedence. +/// +/// See: +#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] +enum OperatorPrecedence { + /// The lowest (virtual) precedence level + None, + /// Precedence of `yield` and `yield from` expressions. + Yield, + /// Precedence of assignment expressions (`name := expr`). + Assign, + /// Precedence of starred expressions (`*expr`). + Starred, + /// Precedence of lambda expressions (`lambda args: expr`). + Lambda, + /// Precedence of if/else expressions (`expr if cond else expr`). + IfElse, + /// Precedence of boolean `or` expressions. + Or, + /// Precedence of boolean `and` expressions. + And, + /// Precedence of boolean `not` expressions. + Not, + /// Precedence of comparisons (`<`, `<=`, `>`, `>=`, `!=`, `==`), + /// memberships (`in`, `not in`) and identity tests (`is`, `is not`). + ComparisonsMembershipIdentity, + /// Precedence of bitwise `|` and `^` operators. + BitXorOr, + /// Precedence of bitwise `&` operator. + BitAnd, + /// Precedence of left and right shift expressions (`<<`, `>>`). + LeftRightShift, + /// Precedence of addition and subtraction expressions (`+`, `-`). + AddSub, + /// Precedence of multiplication (`*`), matrix multiplication (`@`), division (`/`), + /// floor division (`//`) and remainder (`%`) expressions. + MulDivRemain, + /// Precedence of unary positive (`+`), negative (`-`), and bitwise NOT (`~`) expressions. + PosNegBitNot, + /// Precedence of exponentiation expressions (`**`). + Exponent, + /// Precedence of `await` expressions. + Await, + /// Precedence of call expressions (`()`), attribute access (`.`), and subscript (`[]`) expressions. + CallAttribute, + /// Precedence of atomic expressions (literals, names, containers). + Atomic, +} + +impl OperatorPrecedence { + fn from_expr(expr: &Expr) -> Self { + match expr { + // Binding or parenthesized expression, list display, dictionary display, set display + Expr::Tuple(_) + | Expr::Dict(_) + | Expr::Set(_) + | Expr::ListComp(_) + | Expr::List(_) + | Expr::SetComp(_) + | Expr::DictComp(_) + | Expr::Generator(_) + | Expr::Name(_) + | Expr::StringLiteral(_) + | Expr::BytesLiteral(_) + | Expr::NumberLiteral(_) + | Expr::BooleanLiteral(_) + | Expr::NoneLiteral(_) + | Expr::EllipsisLiteral(_) + | Expr::FString(_) => Self::Atomic, + // Subscription, slicing, call, attribute reference + Expr::Attribute(_) | Expr::Subscript(_) | Expr::Call(_) | Expr::Slice(_) => { + Self::CallAttribute + } + + // Await expression + Expr::Await(_) => Self::Await, + + // Exponentiation ** + // Handled below along with other binary operators + + // Unary operators: +x, -x, ~x (except boolean not) + Expr::UnaryOp(operator) => match operator.op { + UnaryOp::UAdd | UnaryOp::USub | UnaryOp::Invert => Self::PosNegBitNot, + UnaryOp::Not => Self::Not, + }, + + // Math binary ops + Expr::BinOp(binary_operation) => Self::from(binary_operation.op), + + // Comparisons: <, <=, >, >=, ==, !=, in, not in, is, is not + Expr::Compare(_) => Self::ComparisonsMembershipIdentity, + + // Boolean not + // Handled above in unary operators + + // Boolean operations: and, or + Expr::BoolOp(bool_op) => Self::from(bool_op.op), + + // Conditional expressions: x if y else z + Expr::If(_) => Self::IfElse, + + // Lambda expressions + Expr::Lambda(_) => Self::Lambda, + + // Unpacking also omitted in the docs, but has almost the lowest precedence, + // except for assignment & yield expressions. E.g. `[*(v := [1,2])]` is valid + // but `[*v := [1,2]] would fail on incorrect syntax because * will associate + // `v` before the assignment. + Expr::Starred(_) => Self::Starred, + + // Assignment expressions (aka named) + Expr::Named(_) => Self::Assign, + + // Although omitted in docs, yield expressions may be used inside an expression + // but must be parenthesized. So for our purposes we assume they just have + // the lowest "real" precedence. + Expr::Yield(_) | Expr::YieldFrom(_) => Self::Yield, + + // Not a real python expression, so treat as lowest as well + Expr::IpyEscapeCommand(_) => Self::None, + } + } +} + +impl From<&Expr> for OperatorPrecedence { + fn from(expr: &Expr) -> Self { + Self::from_expr(expr) + } +} + +impl From for OperatorPrecedence { + fn from(operator: Operator) -> Self { + match operator { + // Multiplication, matrix multiplication, division, floor division, remainder: + // *, @, /, //, % + Operator::Mult + | Operator::MatMult + | Operator::Div + | Operator::Mod + | Operator::FloorDiv => Self::MulDivRemain, + // Addition, subtraction + Operator::Add | Operator::Sub => Self::AddSub, + // Bitwise shifts: <<, >> + Operator::LShift | Operator::RShift => Self::LeftRightShift, + // Bitwise operations: &, ^, | + Operator::BitAnd => Self::BitAnd, + Operator::BitXor | Operator::BitOr => Self::BitXorOr, + // Exponentiation ** + Operator::Pow => Self::Exponent, + } + } +} + +impl From for OperatorPrecedence { + fn from(operator: BoolOp) -> Self { + match operator { + BoolOp::And => Self::And, + BoolOp::Or => Self::Or, + } + } } diff --git a/crates/ruff_linter/src/rules/pylint/snapshots/ruff_linter__rules__pylint__tests__PLC2801_unnecessary_dunder_call.py.snap b/crates/ruff_linter/src/rules/pylint/snapshots/ruff_linter__rules__pylint__tests__PLC2801_unnecessary_dunder_call.py.snap index e3ba6a0048eeb..4d897e73d97b6 100644 --- a/crates/ruff_linter/src/rules/pylint/snapshots/ruff_linter__rules__pylint__tests__PLC2801_unnecessary_dunder_call.py.snap +++ b/crates/ruff_linter/src/rules/pylint/snapshots/ruff_linter__rules__pylint__tests__PLC2801_unnecessary_dunder_call.py.snap @@ -750,370 +750,455 @@ unnecessary_dunder_call.py:49:7: PLC2801 Unnecessary dunder call to `__call__` 49 | print(a.__call__()) # PLC2801 (no fix, intentional) | ^^^^^^^^^^^^ PLC2801 50 | -51 | # Lambda expressions +51 | class Foo: | -unnecessary_dunder_call.py:52:16: PLC2801 [*] Unnecessary dunder call to `__add__`. Use `+` operator. +unnecessary_dunder_call.py:63:1: PLC2801 [*] Unnecessary dunder call to `__add__`. Use `+` operator. | -51 | # Lambda expressions -52 | blah = lambda: a.__add__(1) # PLC2801 +62 | foo = Foo(1) +63 | foo.__add__(2).get_v() # PLC2801 + | ^^^^^^^^^^^^^^ PLC2801 + | + = help: Use `+` operator + +ℹ Unsafe fix +60 60 | return self.v +61 61 | +62 62 | foo = Foo(1) +63 |-foo.__add__(2).get_v() # PLC2801 + 63 |+(foo + 2).get_v() # PLC2801 +64 64 | +65 65 | +66 66 | # Lambda expressions + +unnecessary_dunder_call.py:67:16: PLC2801 [*] Unnecessary dunder call to `__add__`. Use `+` operator. + | +66 | # Lambda expressions +67 | blah = lambda: a.__add__(1) # PLC2801 | ^^^^^^^^^^^^ PLC2801 -53 | -54 | # If expressions +68 | +69 | # If expressions | = help: Use `+` operator ℹ Unsafe fix -49 49 | print(a.__call__()) # PLC2801 (no fix, intentional) -50 50 | -51 51 | # Lambda expressions -52 |-blah = lambda: a.__add__(1) # PLC2801 - 52 |+blah = lambda: a + 1 # PLC2801 -53 53 | -54 54 | # If expressions -55 55 | print(a.__add__(1) if a > 0 else a.__sub__(1)) # PLC2801 - -unnecessary_dunder_call.py:55:7: PLC2801 [*] Unnecessary dunder call to `__add__`. Use `+` operator. - | -54 | # If expressions -55 | print(a.__add__(1) if a > 0 else a.__sub__(1)) # PLC2801 +64 64 | +65 65 | +66 66 | # Lambda expressions +67 |-blah = lambda: a.__add__(1) # PLC2801 + 67 |+blah = lambda: a + 1 # PLC2801 +68 68 | +69 69 | # If expressions +70 70 | print(a.__add__(1) if a > 0 else a.__sub__(1)) # PLC2801 + +unnecessary_dunder_call.py:70:7: PLC2801 [*] Unnecessary dunder call to `__add__`. Use `+` operator. + | +69 | # If expressions +70 | print(a.__add__(1) if a > 0 else a.__sub__(1)) # PLC2801 | ^^^^^^^^^^^^ PLC2801 -56 | -57 | # Dict/Set/List/Tuple +71 | +72 | # Dict/Set/List/Tuple | = help: Use `+` operator ℹ Unsafe fix -52 52 | blah = lambda: a.__add__(1) # PLC2801 -53 53 | -54 54 | # If expressions -55 |-print(a.__add__(1) if a > 0 else a.__sub__(1)) # PLC2801 - 55 |+print(a + 1 if a > 0 else a.__sub__(1)) # PLC2801 -56 56 | -57 57 | # Dict/Set/List/Tuple -58 58 | print({"a": a.__add__(1)}) # PLC2801 - -unnecessary_dunder_call.py:55:34: PLC2801 [*] Unnecessary dunder call to `__sub__`. Use `-` operator. - | -54 | # If expressions -55 | print(a.__add__(1) if a > 0 else a.__sub__(1)) # PLC2801 +67 67 | blah = lambda: a.__add__(1) # PLC2801 +68 68 | +69 69 | # If expressions +70 |-print(a.__add__(1) if a > 0 else a.__sub__(1)) # PLC2801 + 70 |+print(a + 1 if a > 0 else a.__sub__(1)) # PLC2801 +71 71 | +72 72 | # Dict/Set/List/Tuple +73 73 | print({"a": a.__add__(1)}) # PLC2801 + +unnecessary_dunder_call.py:70:34: PLC2801 [*] Unnecessary dunder call to `__sub__`. Use `-` operator. + | +69 | # If expressions +70 | print(a.__add__(1) if a > 0 else a.__sub__(1)) # PLC2801 | ^^^^^^^^^^^^ PLC2801 -56 | -57 | # Dict/Set/List/Tuple +71 | +72 | # Dict/Set/List/Tuple | = help: Use `-` operator ℹ Unsafe fix -52 52 | blah = lambda: a.__add__(1) # PLC2801 -53 53 | -54 54 | # If expressions -55 |-print(a.__add__(1) if a > 0 else a.__sub__(1)) # PLC2801 - 55 |+print(a.__add__(1) if a > 0 else a - 1) # PLC2801 -56 56 | -57 57 | # Dict/Set/List/Tuple -58 58 | print({"a": a.__add__(1)}) # PLC2801 - -unnecessary_dunder_call.py:58:13: PLC2801 [*] Unnecessary dunder call to `__add__`. Use `+` operator. - | -57 | # Dict/Set/List/Tuple -58 | print({"a": a.__add__(1)}) # PLC2801 +67 67 | blah = lambda: a.__add__(1) # PLC2801 +68 68 | +69 69 | # If expressions +70 |-print(a.__add__(1) if a > 0 else a.__sub__(1)) # PLC2801 + 70 |+print(a.__add__(1) if a > 0 else a - 1) # PLC2801 +71 71 | +72 72 | # Dict/Set/List/Tuple +73 73 | print({"a": a.__add__(1)}) # PLC2801 + +unnecessary_dunder_call.py:73:13: PLC2801 [*] Unnecessary dunder call to `__add__`. Use `+` operator. + | +72 | # Dict/Set/List/Tuple +73 | print({"a": a.__add__(1)}) # PLC2801 | ^^^^^^^^^^^^ PLC2801 -59 | print({a.__add__(1)}) # PLC2801 -60 | print([a.__add__(1)]) # PLC2801 +74 | print({a.__add__(1)}) # PLC2801 +75 | print([a.__add__(1)]) # PLC2801 | = help: Use `+` operator ℹ Unsafe fix -55 55 | print(a.__add__(1) if a > 0 else a.__sub__(1)) # PLC2801 -56 56 | -57 57 | # Dict/Set/List/Tuple -58 |-print({"a": a.__add__(1)}) # PLC2801 - 58 |+print({"a": a + 1}) # PLC2801 -59 59 | print({a.__add__(1)}) # PLC2801 -60 60 | print([a.__add__(1)]) # PLC2801 -61 61 | print((a.__add__(1),)) # PLC2801 - -unnecessary_dunder_call.py:59:8: PLC2801 [*] Unnecessary dunder call to `__add__`. Use `+` operator. - | -57 | # Dict/Set/List/Tuple -58 | print({"a": a.__add__(1)}) # PLC2801 -59 | print({a.__add__(1)}) # PLC2801 +70 70 | print(a.__add__(1) if a > 0 else a.__sub__(1)) # PLC2801 +71 71 | +72 72 | # Dict/Set/List/Tuple +73 |-print({"a": a.__add__(1)}) # PLC2801 + 73 |+print({"a": (a + 1)}) # PLC2801 +74 74 | print({a.__add__(1)}) # PLC2801 +75 75 | print([a.__add__(1)]) # PLC2801 +76 76 | print((a.__add__(1),)) # PLC2801 + +unnecessary_dunder_call.py:74:8: PLC2801 [*] Unnecessary dunder call to `__add__`. Use `+` operator. + | +72 | # Dict/Set/List/Tuple +73 | print({"a": a.__add__(1)}) # PLC2801 +74 | print({a.__add__(1)}) # PLC2801 | ^^^^^^^^^^^^ PLC2801 -60 | print([a.__add__(1)]) # PLC2801 -61 | print((a.__add__(1),)) # PLC2801 +75 | print([a.__add__(1)]) # PLC2801 +76 | print((a.__add__(1),)) # PLC2801 | = help: Use `+` operator ℹ Unsafe fix -56 56 | -57 57 | # Dict/Set/List/Tuple -58 58 | print({"a": a.__add__(1)}) # PLC2801 -59 |-print({a.__add__(1)}) # PLC2801 - 59 |+print({a + 1}) # PLC2801 -60 60 | print([a.__add__(1)]) # PLC2801 -61 61 | print((a.__add__(1),)) # PLC2801 -62 62 | - -unnecessary_dunder_call.py:60:8: PLC2801 [*] Unnecessary dunder call to `__add__`. Use `+` operator. - | -58 | print({"a": a.__add__(1)}) # PLC2801 -59 | print({a.__add__(1)}) # PLC2801 -60 | print([a.__add__(1)]) # PLC2801 +71 71 | +72 72 | # Dict/Set/List/Tuple +73 73 | print({"a": a.__add__(1)}) # PLC2801 +74 |-print({a.__add__(1)}) # PLC2801 + 74 |+print({(a + 1)}) # PLC2801 +75 75 | print([a.__add__(1)]) # PLC2801 +76 76 | print((a.__add__(1),)) # PLC2801 +77 77 | + +unnecessary_dunder_call.py:75:8: PLC2801 [*] Unnecessary dunder call to `__add__`. Use `+` operator. + | +73 | print({"a": a.__add__(1)}) # PLC2801 +74 | print({a.__add__(1)}) # PLC2801 +75 | print([a.__add__(1)]) # PLC2801 | ^^^^^^^^^^^^ PLC2801 -61 | print((a.__add__(1),)) # PLC2801 +76 | print((a.__add__(1),)) # PLC2801 | = help: Use `+` operator ℹ Unsafe fix -57 57 | # Dict/Set/List/Tuple -58 58 | print({"a": a.__add__(1)}) # PLC2801 -59 59 | print({a.__add__(1)}) # PLC2801 -60 |-print([a.__add__(1)]) # PLC2801 - 60 |+print([a + 1]) # PLC2801 -61 61 | print((a.__add__(1),)) # PLC2801 -62 62 | -63 63 | # Comprehension variants - -unnecessary_dunder_call.py:61:8: PLC2801 [*] Unnecessary dunder call to `__add__`. Use `+` operator. - | -59 | print({a.__add__(1)}) # PLC2801 -60 | print([a.__add__(1)]) # PLC2801 -61 | print((a.__add__(1),)) # PLC2801 +72 72 | # Dict/Set/List/Tuple +73 73 | print({"a": a.__add__(1)}) # PLC2801 +74 74 | print({a.__add__(1)}) # PLC2801 +75 |-print([a.__add__(1)]) # PLC2801 + 75 |+print([(a + 1)]) # PLC2801 +76 76 | print((a.__add__(1),)) # PLC2801 +77 77 | +78 78 | # Comprehension variants + +unnecessary_dunder_call.py:76:8: PLC2801 [*] Unnecessary dunder call to `__add__`. Use `+` operator. + | +74 | print({a.__add__(1)}) # PLC2801 +75 | print([a.__add__(1)]) # PLC2801 +76 | print((a.__add__(1),)) # PLC2801 | ^^^^^^^^^^^^ PLC2801 -62 | -63 | # Comprehension variants +77 | +78 | # Comprehension variants | = help: Use `+` operator ℹ Unsafe fix -58 58 | print({"a": a.__add__(1)}) # PLC2801 -59 59 | print({a.__add__(1)}) # PLC2801 -60 60 | print([a.__add__(1)]) # PLC2801 -61 |-print((a.__add__(1),)) # PLC2801 - 61 |+print((a + 1,)) # PLC2801 -62 62 | -63 63 | # Comprehension variants -64 64 | print({i: i.__add__(1) for i in range(5)}) # PLC2801 - -unnecessary_dunder_call.py:64:11: PLC2801 [*] Unnecessary dunder call to `__add__`. Use `+` operator. - | -63 | # Comprehension variants -64 | print({i: i.__add__(1) for i in range(5)}) # PLC2801 +73 73 | print({"a": a.__add__(1)}) # PLC2801 +74 74 | print({a.__add__(1)}) # PLC2801 +75 75 | print([a.__add__(1)]) # PLC2801 +76 |-print((a.__add__(1),)) # PLC2801 + 76 |+print(((a + 1),)) # PLC2801 +77 77 | +78 78 | # Comprehension variants +79 79 | print({i: i.__add__(1) for i in range(5)}) # PLC2801 + +unnecessary_dunder_call.py:79:11: PLC2801 [*] Unnecessary dunder call to `__add__`. Use `+` operator. + | +78 | # Comprehension variants +79 | print({i: i.__add__(1) for i in range(5)}) # PLC2801 | ^^^^^^^^^^^^ PLC2801 -65 | print({i.__add__(1) for i in range(5)}) # PLC2801 -66 | print([i.__add__(1) for i in range(5)]) # PLC2801 +80 | print({i.__add__(1) for i in range(5)}) # PLC2801 +81 | print([i.__add__(1) for i in range(5)]) # PLC2801 | = help: Use `+` operator ℹ Unsafe fix -61 61 | print((a.__add__(1),)) # PLC2801 -62 62 | -63 63 | # Comprehension variants -64 |-print({i: i.__add__(1) for i in range(5)}) # PLC2801 - 64 |+print({i: i + 1 for i in range(5)}) # PLC2801 -65 65 | print({i.__add__(1) for i in range(5)}) # PLC2801 -66 66 | print([i.__add__(1) for i in range(5)]) # PLC2801 -67 67 | print((i.__add__(1) for i in range(5))) # PLC2801 - -unnecessary_dunder_call.py:65:8: PLC2801 [*] Unnecessary dunder call to `__add__`. Use `+` operator. - | -63 | # Comprehension variants -64 | print({i: i.__add__(1) for i in range(5)}) # PLC2801 -65 | print({i.__add__(1) for i in range(5)}) # PLC2801 +76 76 | print((a.__add__(1),)) # PLC2801 +77 77 | +78 78 | # Comprehension variants +79 |-print({i: i.__add__(1) for i in range(5)}) # PLC2801 + 79 |+print({i: (i + 1) for i in range(5)}) # PLC2801 +80 80 | print({i.__add__(1) for i in range(5)}) # PLC2801 +81 81 | print([i.__add__(1) for i in range(5)]) # PLC2801 +82 82 | print((i.__add__(1) for i in range(5))) # PLC2801 + +unnecessary_dunder_call.py:80:8: PLC2801 [*] Unnecessary dunder call to `__add__`. Use `+` operator. + | +78 | # Comprehension variants +79 | print({i: i.__add__(1) for i in range(5)}) # PLC2801 +80 | print({i.__add__(1) for i in range(5)}) # PLC2801 | ^^^^^^^^^^^^ PLC2801 -66 | print([i.__add__(1) for i in range(5)]) # PLC2801 -67 | print((i.__add__(1) for i in range(5))) # PLC2801 +81 | print([i.__add__(1) for i in range(5)]) # PLC2801 +82 | print((i.__add__(1) for i in range(5))) # PLC2801 | = help: Use `+` operator ℹ Unsafe fix -62 62 | -63 63 | # Comprehension variants -64 64 | print({i: i.__add__(1) for i in range(5)}) # PLC2801 -65 |-print({i.__add__(1) for i in range(5)}) # PLC2801 - 65 |+print({i + 1 for i in range(5)}) # PLC2801 -66 66 | print([i.__add__(1) for i in range(5)]) # PLC2801 -67 67 | print((i.__add__(1) for i in range(5))) # PLC2801 -68 68 | - -unnecessary_dunder_call.py:66:8: PLC2801 [*] Unnecessary dunder call to `__add__`. Use `+` operator. - | -64 | print({i: i.__add__(1) for i in range(5)}) # PLC2801 -65 | print({i.__add__(1) for i in range(5)}) # PLC2801 -66 | print([i.__add__(1) for i in range(5)]) # PLC2801 +77 77 | +78 78 | # Comprehension variants +79 79 | print({i: i.__add__(1) for i in range(5)}) # PLC2801 +80 |-print({i.__add__(1) for i in range(5)}) # PLC2801 + 80 |+print({(i + 1) for i in range(5)}) # PLC2801 +81 81 | print([i.__add__(1) for i in range(5)]) # PLC2801 +82 82 | print((i.__add__(1) for i in range(5))) # PLC2801 +83 83 | + +unnecessary_dunder_call.py:81:8: PLC2801 [*] Unnecessary dunder call to `__add__`. Use `+` operator. + | +79 | print({i: i.__add__(1) for i in range(5)}) # PLC2801 +80 | print({i.__add__(1) for i in range(5)}) # PLC2801 +81 | print([i.__add__(1) for i in range(5)]) # PLC2801 | ^^^^^^^^^^^^ PLC2801 -67 | print((i.__add__(1) for i in range(5))) # PLC2801 +82 | print((i.__add__(1) for i in range(5))) # PLC2801 | = help: Use `+` operator ℹ Unsafe fix -63 63 | # Comprehension variants -64 64 | print({i: i.__add__(1) for i in range(5)}) # PLC2801 -65 65 | print({i.__add__(1) for i in range(5)}) # PLC2801 -66 |-print([i.__add__(1) for i in range(5)]) # PLC2801 - 66 |+print([i + 1 for i in range(5)]) # PLC2801 -67 67 | print((i.__add__(1) for i in range(5))) # PLC2801 -68 68 | -69 69 | # Generators - -unnecessary_dunder_call.py:67:8: PLC2801 [*] Unnecessary dunder call to `__add__`. Use `+` operator. - | -65 | print({i.__add__(1) for i in range(5)}) # PLC2801 -66 | print([i.__add__(1) for i in range(5)]) # PLC2801 -67 | print((i.__add__(1) for i in range(5))) # PLC2801 +78 78 | # Comprehension variants +79 79 | print({i: i.__add__(1) for i in range(5)}) # PLC2801 +80 80 | print({i.__add__(1) for i in range(5)}) # PLC2801 +81 |-print([i.__add__(1) for i in range(5)]) # PLC2801 + 81 |+print([(i + 1) for i in range(5)]) # PLC2801 +82 82 | print((i.__add__(1) for i in range(5))) # PLC2801 +83 83 | +84 84 | # Generators + +unnecessary_dunder_call.py:82:8: PLC2801 [*] Unnecessary dunder call to `__add__`. Use `+` operator. + | +80 | print({i.__add__(1) for i in range(5)}) # PLC2801 +81 | print([i.__add__(1) for i in range(5)]) # PLC2801 +82 | print((i.__add__(1) for i in range(5))) # PLC2801 | ^^^^^^^^^^^^ PLC2801 -68 | -69 | # Generators +83 | +84 | # Generators | = help: Use `+` operator ℹ Unsafe fix -64 64 | print({i: i.__add__(1) for i in range(5)}) # PLC2801 -65 65 | print({i.__add__(1) for i in range(5)}) # PLC2801 -66 66 | print([i.__add__(1) for i in range(5)]) # PLC2801 -67 |-print((i.__add__(1) for i in range(5))) # PLC2801 - 67 |+print((i + 1 for i in range(5))) # PLC2801 -68 68 | -69 69 | # Generators -70 70 | gen = (i.__add__(1) for i in range(5)) # PLC2801 - -unnecessary_dunder_call.py:70:8: PLC2801 [*] Unnecessary dunder call to `__add__`. Use `+` operator. - | -69 | # Generators -70 | gen = (i.__add__(1) for i in range(5)) # PLC2801 +79 79 | print({i: i.__add__(1) for i in range(5)}) # PLC2801 +80 80 | print({i.__add__(1) for i in range(5)}) # PLC2801 +81 81 | print([i.__add__(1) for i in range(5)]) # PLC2801 +82 |-print((i.__add__(1) for i in range(5))) # PLC2801 + 82 |+print(((i + 1) for i in range(5))) # PLC2801 +83 83 | +84 84 | # Generators +85 85 | gen = (i.__add__(1) for i in range(5)) # PLC2801 + +unnecessary_dunder_call.py:85:8: PLC2801 [*] Unnecessary dunder call to `__add__`. Use `+` operator. + | +84 | # Generators +85 | gen = (i.__add__(1) for i in range(5)) # PLC2801 | ^^^^^^^^^^^^ PLC2801 -71 | print(next(gen)) +86 | print(next(gen)) | = help: Use `+` operator ℹ Unsafe fix -67 67 | print((i.__add__(1) for i in range(5))) # PLC2801 -68 68 | -69 69 | # Generators -70 |-gen = (i.__add__(1) for i in range(5)) # PLC2801 - 70 |+gen = (i + 1 for i in range(5)) # PLC2801 -71 71 | print(next(gen)) -72 72 | -73 73 | # Subscripts - -unnecessary_dunder_call.py:74:13: PLC2801 [*] Unnecessary dunder call to `__add__`. Use `+` operator. - | -73 | # Subscripts -74 | print({"a": a.__add__(1)}["a"]) # PLC2801 +82 82 | print((i.__add__(1) for i in range(5))) # PLC2801 +83 83 | +84 84 | # Generators +85 |-gen = (i.__add__(1) for i in range(5)) # PLC2801 + 85 |+gen = ((i + 1) for i in range(5)) # PLC2801 +86 86 | print(next(gen)) +87 87 | +88 88 | # Subscripts + +unnecessary_dunder_call.py:89:13: PLC2801 [*] Unnecessary dunder call to `__add__`. Use `+` operator. + | +88 | # Subscripts +89 | print({"a": a.__add__(1)}["a"]) # PLC2801 | ^^^^^^^^^^^^ PLC2801 -75 | -76 | # Starred +90 | # https://github.com/astral-sh/ruff/issues/15745 +91 | print("x".__add__("y")[0]) # PLC2801 | = help: Use `+` operator ℹ Unsafe fix -71 71 | print(next(gen)) -72 72 | -73 73 | # Subscripts -74 |-print({"a": a.__add__(1)}["a"]) # PLC2801 - 74 |+print({"a": a + 1}["a"]) # PLC2801 -75 75 | -76 76 | # Starred -77 77 | print(*[a.__add__(1)]) # PLC2801 - -unnecessary_dunder_call.py:77:9: PLC2801 [*] Unnecessary dunder call to `__add__`. Use `+` operator. - | -76 | # Starred -77 | print(*[a.__add__(1)]) # PLC2801 - | ^^^^^^^^^^^^ PLC2801 -78 | -79 | # Slices +86 86 | print(next(gen)) +87 87 | +88 88 | # Subscripts +89 |-print({"a": a.__add__(1)}["a"]) # PLC2801 + 89 |+print({"a": (a + 1)}["a"]) # PLC2801 +90 90 | # https://github.com/astral-sh/ruff/issues/15745 +91 91 | print("x".__add__("y")[0]) # PLC2801 +92 92 | + +unnecessary_dunder_call.py:91:7: PLC2801 [*] Unnecessary dunder call to `__add__`. Use `+` operator. + | +89 | print({"a": a.__add__(1)}["a"]) # PLC2801 +90 | # https://github.com/astral-sh/ruff/issues/15745 +91 | print("x".__add__("y")[0]) # PLC2801 + | ^^^^^^^^^^^^^^^^ PLC2801 +92 | +93 | # Starred | = help: Use `+` operator ℹ Unsafe fix -74 74 | print({"a": a.__add__(1)}["a"]) # PLC2801 -75 75 | -76 76 | # Starred -77 |-print(*[a.__add__(1)]) # PLC2801 - 77 |+print(*[a + 1]) # PLC2801 -78 78 | -79 79 | # Slices -80 80 | print([a.__add__(1), a.__sub__(1)][0:1]) # PLC2801 - -unnecessary_dunder_call.py:80:8: PLC2801 [*] Unnecessary dunder call to `__add__`. Use `+` operator. - | -79 | # Slices -80 | print([a.__add__(1), a.__sub__(1)][0:1]) # PLC2801 - | ^^^^^^^^^^^^ PLC2801 +88 88 | # Subscripts +89 89 | print({"a": a.__add__(1)}["a"]) # PLC2801 +90 90 | # https://github.com/astral-sh/ruff/issues/15745 +91 |-print("x".__add__("y")[0]) # PLC2801 + 91 |+print(("x" + "y")[0]) # PLC2801 +92 92 | +93 93 | # Starred +94 94 | print(*[a.__add__(1)]) # PLC2801 + +unnecessary_dunder_call.py:94:9: PLC2801 [*] Unnecessary dunder call to `__add__`. Use `+` operator. + | +93 | # Starred +94 | print(*[a.__add__(1)]) # PLC2801 + | ^^^^^^^^^^^^ PLC2801 +95 | +96 | list1 = [1, 2, 3] | = help: Use `+` operator ℹ Unsafe fix -77 77 | print(*[a.__add__(1)]) # PLC2801 -78 78 | -79 79 | # Slices -80 |-print([a.__add__(1), a.__sub__(1)][0:1]) # PLC2801 - 80 |+print([a + 1, a.__sub__(1)][0:1]) # PLC2801 -81 81 | -82 82 | -83 83 | class Thing: +91 91 | print("x".__add__("y")[0]) # PLC2801 +92 92 | +93 93 | # Starred +94 |-print(*[a.__add__(1)]) # PLC2801 + 94 |+print(*[(a + 1)]) # PLC2801 +95 95 | +96 96 | list1 = [1, 2, 3] +97 97 | list2 = [4, 5, 6] + +unnecessary_dunder_call.py:98:9: PLC2801 [*] Unnecessary dunder call to `__add__`. Use `+` operator. + | + 96 | list1 = [1, 2, 3] + 97 | list2 = [4, 5, 6] + 98 | print([*list1.__add__(list2)]) # PLC2801 + | ^^^^^^^^^^^^^^^^^^^^ PLC2801 + 99 | +100 | # Slices + | + = help: Use `+` operator -unnecessary_dunder_call.py:80:22: PLC2801 [*] Unnecessary dunder call to `__sub__`. Use `-` operator. - | -79 | # Slices -80 | print([a.__add__(1), a.__sub__(1)][0:1]) # PLC2801 - | ^^^^^^^^^^^^ PLC2801 - | - = help: Use `-` operator +ℹ Unsafe fix +95 95 | +96 96 | list1 = [1, 2, 3] +97 97 | list2 = [4, 5, 6] +98 |-print([*list1.__add__(list2)]) # PLC2801 + 98 |+print([*list1 + list2]) # PLC2801 +99 99 | +100 100 | # Slices +101 101 | print([a.__add__(1), a.__sub__(1)][0:1]) # PLC2801 + +unnecessary_dunder_call.py:101:8: PLC2801 [*] Unnecessary dunder call to `__add__`. Use `+` operator. + | +100 | # Slices +101 | print([a.__add__(1), a.__sub__(1)][0:1]) # PLC2801 + | ^^^^^^^^^^^^ PLC2801 +102 | +103 | # Attribute access + | + = help: Use `+` operator + +ℹ Unsafe fix +98 98 | print([*list1.__add__(list2)]) # PLC2801 +99 99 | +100 100 | # Slices +101 |-print([a.__add__(1), a.__sub__(1)][0:1]) # PLC2801 + 101 |+print([(a + 1), a.__sub__(1)][0:1]) # PLC2801 +102 102 | +103 103 | # Attribute access +104 104 | # https://github.com/astral-sh/ruff/issues/15745 + +unnecessary_dunder_call.py:101:22: PLC2801 [*] Unnecessary dunder call to `__sub__`. Use `-` operator. + | +100 | # Slices +101 | print([a.__add__(1), a.__sub__(1)][0:1]) # PLC2801 + | ^^^^^^^^^^^^ PLC2801 +102 | +103 | # Attribute access + | + = help: Use `-` operator ℹ Unsafe fix -77 77 | print(*[a.__add__(1)]) # PLC2801 -78 78 | -79 79 | # Slices -80 |-print([a.__add__(1), a.__sub__(1)][0:1]) # PLC2801 - 80 |+print([a.__add__(1), a - 1][0:1]) # PLC2801 -81 81 | -82 82 | -83 83 | class Thing: - -unnecessary_dunder_call.py:92:16: PLC2801 Unnecessary dunder call to `__getattribute__`. Access attribute directly or use getattr built-in function. - | -91 | def do_thing(self, item): -92 | return object.__getattribute__(self, item) # PLC2801 - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PLC2801 -93 | -94 | def use_descriptor(self, item): - | - = help: Access attribute directly or use getattr built-in function - -unnecessary_dunder_call.py:104:1: PLC2801 [*] Unnecessary dunder call to `__contains__`. Use `in` operator. +98 98 | print([*list1.__add__(list2)]) # PLC2801 +99 99 | +100 100 | # Slices +101 |-print([a.__add__(1), a.__sub__(1)][0:1]) # PLC2801 + 101 |+print([a.__add__(1), (a - 1)][0:1]) # PLC2801 +102 102 | +103 103 | # Attribute access +104 104 | # https://github.com/astral-sh/ruff/issues/15745 + +unnecessary_dunder_call.py:105:7: PLC2801 [*] Unnecessary dunder call to `__add__`. Use `+` operator. + | +103 | # Attribute access +104 | # https://github.com/astral-sh/ruff/issues/15745 +105 | print(1j.__add__(1.0).real) # PLC2801 + | ^^^^^^^^^^^^^^^ PLC2801 +106 | +107 | class Thing: + | + = help: Use `+` operator + +ℹ Unsafe fix +102 102 | +103 103 | # Attribute access +104 104 | # https://github.com/astral-sh/ruff/issues/15745 +105 |-print(1j.__add__(1.0).real) # PLC2801 + 105 |+print((1j + 1.0).real) # PLC2801 +106 106 | +107 107 | class Thing: +108 108 | def __init__(self, stuff: Any) -> None: + +unnecessary_dunder_call.py:116:16: PLC2801 Unnecessary dunder call to `__getattribute__`. Access attribute directly or use getattr built-in function. + | +115 | def do_thing(self, item): +116 | return object.__getattribute__(self, item) # PLC2801 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PLC2801 +117 | +118 | def use_descriptor(self, item): + | + = help: Access attribute directly or use getattr built-in function + +unnecessary_dunder_call.py:128:1: PLC2801 [*] Unnecessary dunder call to `__contains__`. Use `in` operator. | -102 | blah = dict[{"a": 1}.__delitem__("a")] # OK -103 | -104 | "abc".__contains__("a") +126 | blah = dict[{"a": 1}.__delitem__("a")] # OK +127 | +128 | "abc".__contains__("a") | ^^^^^^^^^^^^^^^^^^^^^^^ PLC2801 -105 | -106 | # https://github.com/astral-sh/ruff/issues/14597 +129 | +130 | # https://github.com/astral-sh/ruff/issues/14597 | = help: Use `in` operator ℹ Unsafe fix -101 101 | -102 102 | blah = dict[{"a": 1}.__delitem__("a")] # OK -103 103 | -104 |-"abc".__contains__("a") - 104 |+"a" in "abc" -105 105 | -106 106 | # https://github.com/astral-sh/ruff/issues/14597 -107 107 | assert "abc".__str__() == "abc" - -unnecessary_dunder_call.py:107:8: PLC2801 [*] Unnecessary dunder call to `__str__`. Use `str()` builtin. +125 125 | +126 126 | blah = dict[{"a": 1}.__delitem__("a")] # OK +127 127 | +128 |-"abc".__contains__("a") + 128 |+"a" in "abc" +129 129 | +130 130 | # https://github.com/astral-sh/ruff/issues/14597 +131 131 | assert "abc".__str__() == "abc" + +unnecessary_dunder_call.py:131:8: PLC2801 [*] Unnecessary dunder call to `__str__`. Use `str()` builtin. | -106 | # https://github.com/astral-sh/ruff/issues/14597 -107 | assert "abc".__str__() == "abc" +130 | # https://github.com/astral-sh/ruff/issues/14597 +131 | assert "abc".__str__() == "abc" | ^^^^^^^^^^^^^^^ PLC2801 | = help: Use `str()` builtin ℹ Unsafe fix -104 104 | "abc".__contains__("a") -105 105 | -106 106 | # https://github.com/astral-sh/ruff/issues/14597 -107 |-assert "abc".__str__() == "abc" - 107 |+assert str("abc") == "abc" +128 128 | "abc".__contains__("a") +129 129 | +130 130 | # https://github.com/astral-sh/ruff/issues/14597 +131 |-assert "abc".__str__() == "abc" + 131 |+assert str("abc") == "abc"