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 ac1c797bcb824..6af9bfe4e5970 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 @@ -127,7 +127,7 @@ pub(crate) fn unnecessary_dunder_call(checker: &mut Checker, call: &ast::ExprCal 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 can_be_represented_without_parentheses(arg) { // if it's something that can reasonably be removed from parentheses, // we'll do that. fixed = Some(format!("{value_slice} {replacement} {arg_slice}")); @@ -174,7 +174,7 @@ pub(crate) fn unnecessary_dunder_call(checker: &mut Checker, call: &ast::ExprCal let wrap_in_paren = checker .semantic() .current_expression_parent() - .is_some_and(|parent| !parent.is_call_expr()); + .is_some_and(|parent| !can_be_represented_without_parentheses(parent)); if wrap_in_paren { fixed = format!("({fixed})"); @@ -384,3 +384,24 @@ fn in_dunder_method_definition(semantic: &SemanticModel) -> bool { func_def.name.starts_with("__") && func_def.name.ends_with("__") }) } + +/// 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_exp_expr() + || expr.is_generator_exp_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() +}