Add base-class inheritance detection to flake8-django rules (#9151)

## Summary

As elsewhere, this only applies to classes defined within the same file.

Closes https://github.com/astral-sh/ruff/issues/9150.
This commit is contained in:
Charlie Marsh 2023-12-15 13:01:32 -05:00 committed by GitHub
parent 82731b8194
commit 6ecf844214
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 189 additions and 199 deletions

View file

@ -0,0 +1,57 @@
use rustc_hash::FxHashSet;
use ruff_python_ast as ast;
use ruff_python_ast::call_path::CallPath;
use ruff_python_ast::helpers::map_subscript;
use crate::{BindingId, SemanticModel};
/// Return `true` if any base class of a class definition matches a predicate.
pub fn any_over_body(
class_def: &ast::StmtClassDef,
semantic: &SemanticModel,
func: &dyn Fn(CallPath) -> bool,
) -> bool {
fn inner(
class_def: &ast::StmtClassDef,
semantic: &SemanticModel,
func: &dyn Fn(CallPath) -> bool,
seen: &mut FxHashSet<BindingId>,
) -> bool {
class_def.bases().iter().any(|expr| {
// If the base class itself matches the pattern, then this does too.
// Ex) `class Foo(BaseModel): ...`
if semantic
.resolve_call_path(map_subscript(expr))
.is_some_and(func)
{
return true;
}
// If the base class extends a class that matches the pattern, then this does too.
// Ex) `class Bar(BaseModel): ...; class Foo(Bar): ...`
if let Some(id) = semantic.lookup_attribute(map_subscript(expr)) {
if seen.insert(id) {
let binding = semantic.binding(id);
if let Some(base_class) = binding
.kind
.as_class_definition()
.map(|id| &semantic.scopes[*id])
.and_then(|scope| scope.kind.as_class())
{
if inner(base_class, semantic, func, seen) {
return true;
}
}
}
}
false
})
}
if class_def.bases().is_empty() {
return false;
}
inner(class_def, semantic, func, &mut FxHashSet::default())
}

View file

@ -1,3 +1,4 @@
pub mod class;
pub mod function_type;
pub mod imports;
pub mod logging;