Skip to content

Commit

Permalink
Respect same-file Enum subclasses
Browse files Browse the repository at this point in the history
  • Loading branch information
charliermarsh committed Apr 18, 2024
1 parent 16cc9bd commit 62f3ddf
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 26 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,11 @@ class Good(str): # Ok

class Fine(str, Enum): # Ok
__slots__ = ["foo"]


class SubEnum(Enum):
pass


class Ok(str, SubEnum): # Ok
pass
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use ruff_python_ast::{Arguments, Expr, Stmt, StmtClassDef};
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::identifier::Identifier;
use ruff_python_semantic::SemanticModel;
use ruff_python_semantic::{analyze, SemanticModel};

use crate::checkers::ast::Checker;
use crate::rules::flake8_slots::rules::helpers::has_slots;
Expand Down Expand Up @@ -55,33 +55,44 @@ pub(crate) fn no_slots_in_str_subclass(checker: &mut Checker, stmt: &Stmt, class
return;
};

if is_str_subclass(bases, checker.semantic()) {
if !has_slots(&class.body) {
checker
.diagnostics
.push(Diagnostic::new(NoSlotsInStrSubclass, stmt.identifier()));
}
if !is_str_subclass(bases, checker.semantic()) {
return;
}

// Ignore subclasses of `enum.Enum` et al.
if is_enum_subclass(class, checker.semantic()) {
return;
}

if has_slots(&class.body) {
return;
}

checker
.diagnostics
.push(Diagnostic::new(NoSlotsInStrSubclass, stmt.identifier()));
}

/// Return `true` if the class is a subclass of `str`, but _not_ a subclass of `enum.Enum`,
/// `enum.IntEnum`, etc.
/// Return `true` if the class is a subclass of `str`.
fn is_str_subclass(bases: &[Expr], semantic: &SemanticModel) -> bool {
let mut is_str_subclass = false;
for base in bases {
if let Some(qualified_name) = semantic.resolve_qualified_name(base) {
match qualified_name.segments() {
["" | "builtins", "str"] => {
is_str_subclass = true;
}
["enum", "Enum" | "IntEnum" | "StrEnum" | "Flag" | "IntFlag" | "ReprEnum" | "EnumCheck"] =>
{
// Ignore enum classes.
return false;
}
_ => {}
}
}
}
is_str_subclass
bases.iter().any(|base| {
semantic
.resolve_qualified_name(base)
.is_some_and(|qualified_name| {
matches!(qualified_name.segments(), ["" | "builtins", "str"])
})
})
}

/// Returns `true` if the class is an enum subclass, at any depth.
fn is_enum_subclass(class_def: &StmtClassDef, semantic: &SemanticModel) -> bool {
analyze::class::any_qualified_name(class_def, semantic, &|qualified_name| {
matches!(
qualified_name.segments(),
[
"enum",
"Enum" | "IntEnum" | "StrEnum" | "Flag" | "IntFlag" | "ReprEnum" | "EnumCheck"
]
)
})
}

0 comments on commit 62f3ddf

Please sign in to comment.