diff --git a/README.md b/README.md index 29099fe22f..6766c027e3 100644 --- a/README.md +++ b/README.md @@ -263,6 +263,10 @@ Beyond rule-set parity, ruff suffers from the following limitations vis-à-vis F | R001 | UselessObjectInheritance | Class `...` inherits from object | | R002 | NoAssertEquals | `assertEquals` is deprecated, use `assertEqual` instead | | M001 | UnusedNOQA | Unused `noqa` directive | +| A001 | BuiltinVariableShadowing | Variable `...` is shadowing a python builtin | +| A002 | BuiltinArgumentShadowing | Argument `...` is shadowing a python builtin | +| A003 | BuiltinAttributeShadowing | class attribute `...` is shadowing a python builtin | +| SPR001 | SuperCallWithParameters | Use `super()` instead of `super(__class__, self)` | ## Integrations diff --git a/resources/test/fixtures/SPR001.py b/resources/test/fixtures/SPR001.py new file mode 100644 index 0000000000..a6af5b472b --- /dev/null +++ b/resources/test/fixtures/SPR001.py @@ -0,0 +1,22 @@ +class Parent: + def method(self): + pass + + def wrong(self): + pass + + +class Child(Parent): + def method(self): + parent = super() # ok + super().method() # ok + Parent.method(self) # ok + Parent.super(1, 2) # ok + + def wrong(self): + parent = super(Child, self) # wrong + super(Child, self).method # wrong + super( + Child, + self, + ).method() # wrong diff --git a/src/ast/checks.rs b/src/ast/checks.rs index 73dde5eca6..fdf84678d0 100644 --- a/src/ast/checks.rs +++ b/src/ast/checks.rs @@ -680,3 +680,18 @@ pub fn check_builtin_shadowing( None } } + +// flake8-super +/// Check that `super()` has no args +pub fn check_super_args(expr: &Expr, args: &Vec) -> Option { + if let ExprKind::Name { id, .. } = &expr.node { + if id == "super" && !args.is_empty() { + return Some(Check::new( + CheckKind::SuperCallWithParameters, + expr.location, + )); + } + } + + None +} diff --git a/src/check_ast.rs b/src/check_ast.rs index 451195e64a..2f74c2e0fb 100644 --- a/src/check_ast.rs +++ b/src/check_ast.rs @@ -689,13 +689,20 @@ where } ExprContext::Del => self.handle_node_delete(expr), }, - ExprKind::Call { func, .. } => { + ExprKind::Call { func, args, .. } => { if self.settings.select.contains(&CheckCode::R002) { if let Some(check) = checks::check_assert_equals(func, self.autofix) { self.checks.push(check) } } + // flake8-super + if self.settings.select.contains(&CheckCode::SPR001) { + if let Some(check) = checks::check_super_args(func, args) { + self.checks.push(check) + } + } + if let ExprKind::Name { id, ctx } = &func.node { if id == "locals" && matches!(ctx, ExprContext::Load) { let scope = &mut self.scopes[*(self diff --git a/src/checks.rs b/src/checks.rs index a13000c182..7512e30a83 100644 --- a/src/checks.rs +++ b/src/checks.rs @@ -4,7 +4,7 @@ use anyhow::Result; use rustpython_parser::ast::Location; use serde::{Deserialize, Serialize}; -pub const DEFAULT_CHECK_CODES: [CheckCode; 45] = [ +pub const DEFAULT_CHECK_CODES: [CheckCode; 46] = [ CheckCode::E402, CheckCode::E501, CheckCode::E711, @@ -51,9 +51,11 @@ pub const DEFAULT_CHECK_CODES: [CheckCode; 45] = [ CheckCode::A001, CheckCode::A002, CheckCode::A003, + // flake8-super + CheckCode::SPR001, ]; -pub const ALL_CHECK_CODES: [CheckCode; 48] = [ +pub const ALL_CHECK_CODES: [CheckCode; 49] = [ CheckCode::E402, CheckCode::E501, CheckCode::E711, @@ -103,6 +105,8 @@ pub const ALL_CHECK_CODES: [CheckCode; 48] = [ CheckCode::A001, CheckCode::A002, CheckCode::A003, + // flake8-super + CheckCode::SPR001, ]; #[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize, Hash, PartialOrd, Ord)] @@ -156,6 +160,8 @@ pub enum CheckCode { A001, A002, A003, + // flake8-super + SPR001, } impl FromStr for CheckCode { @@ -212,6 +218,8 @@ impl FromStr for CheckCode { "A001" => Ok(CheckCode::A001), "A002" => Ok(CheckCode::A002), "A003" => Ok(CheckCode::A003), + // flake8-super + "SPR001" => Ok(CheckCode::SPR001), _ => Err(anyhow::anyhow!("Unknown check code: {s}")), } } @@ -269,6 +277,8 @@ impl CheckCode { CheckCode::A001 => "A001", CheckCode::A002 => "A002", CheckCode::A003 => "A003", + // flake8-super + CheckCode::SPR001 => "SPR001", } } @@ -333,6 +343,8 @@ impl CheckCode { CheckCode::A001 => CheckKind::BuiltinVariableShadowing("...".to_string()), CheckCode::A002 => CheckKind::BuiltinArgumentShadowing("...".to_string()), CheckCode::A003 => CheckKind::BuiltinAttributeShadowing("...".to_string()), + // flake8-super + CheckCode::SPR001 => CheckKind::SuperCallWithParameters, } } } @@ -401,6 +413,8 @@ pub enum CheckKind { BuiltinVariableShadowing(String), BuiltinArgumentShadowing(String), BuiltinAttributeShadowing(String), + // flake8-super + SuperCallWithParameters, } impl CheckKind { @@ -458,6 +472,8 @@ impl CheckKind { CheckKind::BuiltinVariableShadowing(_) => "BuiltinVariableShadowing", CheckKind::BuiltinArgumentShadowing(_) => "BuiltinArgumentShadowing", CheckKind::BuiltinAttributeShadowing(_) => "BuiltinAttributeShadowing", + // flake8-super + CheckKind::SuperCallWithParameters => "SuperCallWithParameters", } } @@ -513,6 +529,8 @@ impl CheckKind { CheckKind::BuiltinVariableShadowing(_) => &CheckCode::A001, CheckKind::BuiltinArgumentShadowing(_) => &CheckCode::A002, CheckKind::BuiltinAttributeShadowing(_) => &CheckCode::A003, + // flake8-super + CheckKind::SuperCallWithParameters => &CheckCode::SPR001, } } @@ -657,6 +675,10 @@ impl CheckKind { CheckKind::BuiltinAttributeShadowing(name) => { format!("class attribute `{name}` is shadowing a python builtin") } + // flake8-super + CheckKind::SuperCallWithParameters => { + "Use `super()` instead of `super(__class__, self)`".to_string() + } } } diff --git a/src/linter.rs b/src/linter.rs index c48bdfd881..179b176893 100644 --- a/src/linter.rs +++ b/src/linter.rs @@ -762,4 +762,16 @@ mod tests { insta::assert_yaml_snapshot!(checks); Ok(()) } + + #[test] + fn spr001() -> Result<()> { + let mut checks = check_path( + Path::new("./resources/test/fixtures/SPR001.py"), + &settings::Settings::for_rule(CheckCode::SPR001), + &fixer::Mode::Generate, + )?; + checks.sort_by_key(|check| check.location); + insta::assert_yaml_snapshot!(checks); + Ok(()) + } } diff --git a/src/snapshots/ruff__linter__tests__spr001.snap b/src/snapshots/ruff__linter__tests__spr001.snap new file mode 100644 index 0000000000..f292739eef --- /dev/null +++ b/src/snapshots/ruff__linter__tests__spr001.snap @@ -0,0 +1,19 @@ +--- +source: src/linter.rs +expression: checks +--- +- kind: SuperCallWithParameters + location: + row: 17 + column: 18 + fix: ~ +- kind: SuperCallWithParameters + location: + row: 18 + column: 9 + fix: ~ +- kind: SuperCallWithParameters + location: + row: 19 + column: 9 + fix: ~