diff --git a/resources/test/src/R0205.py b/resources/test/src/R0205.py new file mode 100644 index 0000000000..6f581cabc4 --- /dev/null +++ b/resources/test/src/R0205.py @@ -0,0 +1,22 @@ +class A: + ... + + +class B(object): + ... + + +class C(B, object): + ... + + +def f(): + class D(object): + ... + + +object = A + + +class E(object): + ... diff --git a/resources/test/src/pyproject.toml b/resources/test/src/pyproject.toml index d9f8f6c5fc..b036ed57b7 100644 --- a/resources/test/src/pyproject.toml +++ b/resources/test/src/pyproject.toml @@ -14,4 +14,5 @@ select = [ "F831", "F841", "F901", + "R0205", ] diff --git a/src/check_ast.rs b/src/check_ast.rs index 98c3fbbc70..ca7a6e258f 100644 --- a/src/check_ast.rs +++ b/src/check_ast.rs @@ -108,11 +108,37 @@ impl Visitor for Checker<'_> { } } StmtKind::ClassDef { + name, bases, keywords, decorator_list, .. } => { + if self.settings.select.contains(&CheckCode::R0205) { + for expr in bases { + if let ExprKind::Name { id, .. } = &expr.node { + if id == "object" { + let scope = self.scopes.last().expect("No current scope found."); + match scope.values.get(id) { + None + | Some(Binding { + kind: BindingKind::Builtin, + .. + }) => { + self.checks.push(Check { + kind: CheckKind::UselessObjectInheritance( + name.to_string(), + ), + location: stmt.location, + }); + } + _ => {} + } + } + } + } + } + for expr in bases { self.visit_expr(expr, Some(stmt)) } diff --git a/src/checks.rs b/src/checks.rs index d61a950747..2007a6b1cb 100644 --- a/src/checks.rs +++ b/src/checks.rs @@ -20,6 +20,7 @@ pub enum CheckCode { F831, F841, F901, + R0205, } impl FromStr for CheckCode { @@ -39,6 +40,7 @@ impl FromStr for CheckCode { "F831" => Ok(CheckCode::F831), "F841" => Ok(CheckCode::F841), "F901" => Ok(CheckCode::F901), + "R0205" => Ok(CheckCode::R0205), _ => Err(anyhow::anyhow!("Unknown check code: {s}")), } } @@ -59,6 +61,7 @@ impl CheckCode { CheckCode::F831 => "F831", CheckCode::F841 => "F841", CheckCode::F901 => "F901", + CheckCode::R0205 => "R0205", } } @@ -77,6 +80,7 @@ impl CheckCode { CheckCode::F831 => &LintSource::AST, CheckCode::F841 => &LintSource::AST, CheckCode::F901 => &LintSource::AST, + CheckCode::R0205 => &LintSource::AST, } } } @@ -100,6 +104,7 @@ pub enum CheckKind { UndefinedName(String), UnusedImport(String), UnusedVariable(String), + UselessObjectInheritance(String), YieldOutsideFunction, } @@ -118,6 +123,7 @@ impl CheckKind { CheckKind::UndefinedName(_) => &CheckCode::F821, CheckKind::UnusedImport(_) => &CheckCode::F401, CheckKind::UnusedVariable(_) => &CheckCode::F841, + CheckKind::UselessObjectInheritance(_) => &CheckCode::R0205, CheckKind::YieldOutsideFunction => &CheckCode::F704, } } @@ -150,6 +156,9 @@ impl CheckKind { CheckKind::UnusedVariable(name) => { format!("Local variable `{name}` is assigned to but never used") } + CheckKind::UselessObjectInheritance(name) => { + format!("Class {name} inherits from object") + } CheckKind::YieldOutsideFunction => { "a `yield` or `yield from` statement outside of a function/method".to_string() } diff --git a/src/linter.rs b/src/linter.rs index bed1398798..b3070c97b8 100644 --- a/src/linter.rs +++ b/src/linter.rs @@ -451,4 +451,40 @@ mod tests { Ok(()) } + + #[test] + fn r0205() -> Result<()> { + let actual = check_path( + Path::new("./resources/test/src/R0205.py"), + &settings::Settings { + line_length: 88, + exclude: vec![], + select: BTreeSet::from([CheckCode::R0205]), + }, + &cache::Mode::None, + )?; + let expected = vec![ + Message { + kind: CheckKind::UselessObjectInheritance("B".to_string()), + location: Location::new(5, 1), + filename: "./resources/test/src/R0205.py".to_string(), + }, + Message { + kind: CheckKind::UselessObjectInheritance("C".to_string()), + location: Location::new(9, 1), + filename: "./resources/test/src/R0205.py".to_string(), + }, + Message { + kind: CheckKind::UselessObjectInheritance("D".to_string()), + location: Location::new(14, 5), + filename: "./resources/test/src/R0205.py".to_string(), + }, + ]; + assert_eq!(actual.len(), expected.len()); + for i in 0..actual.len() { + assert_eq!(actual[i], expected[i]); + } + + Ok(()) + } } diff --git a/src/pyproject.rs b/src/pyproject.rs index f794eceeee..9aa6f4502e 100644 --- a/src/pyproject.rs +++ b/src/pyproject.rs @@ -249,6 +249,7 @@ other-attribute = 1 CheckCode::F831, CheckCode::F841, CheckCode::F901, + CheckCode::R0205, ])), } );