From 2982250c7bfa4067dfa7a56b93f4ecf0f6c51d59 Mon Sep 17 00:00:00 2001 From: Charlie Marsh Date: Fri, 26 Jul 2024 09:50:29 -0400 Subject: [PATCH] Avoid recommending no-argument super in slots dataclass --- .../test/fixtures/pyupgrade/UP008.py | 16 +++++++++ .../rules/super_call_with_parameters.rs | 34 ++++++++++++++++++- ...er__rules__pyupgrade__tests__UP008.py.snap | 18 ++++++++++ 3 files changed, 67 insertions(+), 1 deletion(-) diff --git a/crates/ruff_linter/resources/test/fixtures/pyupgrade/UP008.py b/crates/ruff_linter/resources/test/fixtures/pyupgrade/UP008.py index 047d90f30dc06..e9d68ebc3a73e 100644 --- a/crates/ruff_linter/resources/test/fixtures/pyupgrade/UP008.py +++ b/crates/ruff_linter/resources/test/fixtures/pyupgrade/UP008.py @@ -63,3 +63,19 @@ def method(inner_self): InnerClass().method() defined_outside = defined_outside + + +from dataclasses import dataclass + + +@dataclass +class DataClass: + def normal(self): + super(DataClass, self).f() # Error + super().f() # OK + + +@dataclass(slots=True) +def normal(self): + super(DataClass, self).f() # OK + super().f() # OK (`TypeError` in practice) diff --git a/crates/ruff_linter/src/rules/pyupgrade/rules/super_call_with_parameters.rs b/crates/ruff_linter/src/rules/pyupgrade/rules/super_call_with_parameters.rs index a01934676b899..06fb6ee050381 100644 --- a/crates/ruff_linter/src/rules/pyupgrade/rules/super_call_with_parameters.rs +++ b/crates/ruff_linter/src/rules/pyupgrade/rules/super_call_with_parameters.rs @@ -102,7 +102,9 @@ pub(crate) fn super_call_with_parameters(checker: &mut Checker, call: &ast::Expr // Find the enclosing class definition (if any). let Some(Stmt::ClassDef(ast::StmtClassDef { - name: parent_name, .. + name: parent_name, + decorator_list, + .. })) = parents.find(|stmt| stmt.is_class_def_stmt()) else { return; @@ -126,6 +128,36 @@ pub(crate) fn super_call_with_parameters(checker: &mut Checker, call: &ast::Expr drop(parents); + // If the class is an `@dataclass` with `slots=True`, calling `super()` without arguments raises + // a `TypeError`. + // + // See: https://docs.python.org/3/library/dataclasses.html#dataclasses.dataclass + if decorator_list.iter().any(|decorator| { + let Expr::Call(ast::ExprCall { + func, arguments, .. + }) = &decorator.expression + else { + return false; + }; + + if checker + .semantic() + .resolve_qualified_name(func) + .is_some_and(|name| name.segments() == ["dataclasses", "dataclass"]) + { + arguments.find_keyword("slots").map_or(false, |keyword| { + matches!( + keyword.value, + Expr::BooleanLiteral(ast::ExprBooleanLiteral { value: true, .. }) + ) + }) + } else { + false + } + }) { + return; + } + let mut diagnostic = Diagnostic::new(SuperCallWithParameters, call.arguments.range()); diagnostic.set_fix(Fix::unsafe_edit(Edit::deletion( call.arguments.start() + TextSize::new(1), diff --git a/crates/ruff_linter/src/rules/pyupgrade/snapshots/ruff_linter__rules__pyupgrade__tests__UP008.py.snap b/crates/ruff_linter/src/rules/pyupgrade/snapshots/ruff_linter__rules__pyupgrade__tests__UP008.py.snap index 04c5276bedb3b..da6e68284da36 100644 --- a/crates/ruff_linter/src/rules/pyupgrade/snapshots/ruff_linter__rules__pyupgrade__tests__UP008.py.snap +++ b/crates/ruff_linter/src/rules/pyupgrade/snapshots/ruff_linter__rules__pyupgrade__tests__UP008.py.snap @@ -107,4 +107,22 @@ UP008.py:50:18: UP008 [*] Use `super()` instead of `super(__class__, self)` 52 52 | 53 53 | outer_argument() +UP008.py:74:14: UP008 [*] Use `super()` instead of `super(__class__, self)` + | +72 | class DataClass: +73 | def normal(self): +74 | super(DataClass, self).f() # Error + | ^^^^^^^^^^^^^^^^^ UP008 +75 | super().f() # OK + | + = help: Remove `__super__` parameters +ℹ Unsafe fix +71 71 | @dataclass +72 72 | class DataClass: +73 73 | def normal(self): +74 |- super(DataClass, self).f() # Error + 74 |+ super().f() # Error +75 75 | super().f() # OK +76 76 | +77 77 |