diff --git a/crates/ruff_linter/src/rules/flake8_bugbear/rules/abstract_base_class.rs b/crates/ruff_linter/src/rules/flake8_bugbear/rules/abstract_base_class.rs index 0d94b43cedfd12..f20686061769aa 100644 --- a/crates/ruff_linter/src/rules/flake8_bugbear/rules/abstract_base_class.rs +++ b/crates/ruff_linter/src/rules/flake8_bugbear/rules/abstract_base_class.rs @@ -11,12 +11,13 @@ use crate::checkers::ast::Checker; use crate::registry::Rule; /// ## What it does -/// Checks for abstract classes without abstract methods. +/// Checks for abstract classes without abstract methods or abstract class variables. +/// Annotated but unassigned class variables are regarded as abstract. /// /// ## Why is this bad? /// Abstract base classes are used to define interfaces. If an abstract base -/// class has no abstract methods, you may have forgotten to add an abstract -/// method to the class or omitted an `@abstractmethod` decorator. +/// class has no abstract methods or abstract class variables, you may have forgotten +/// to add an abstract method to the class or omitted an `@abstractmethod` decorator. /// /// If the class is _not_ meant to be used as an interface, consider removing /// the `ABC` base class from the class definition. @@ -24,9 +25,12 @@ use crate::registry::Rule; /// ## Example /// ```python /// from abc import ABC +/// from typing import ClassVar /// /// /// class Foo(ABC): +/// class_var: ClassVar[str] = "assigned" +/// /// def method(self): /// bar() /// ``` @@ -34,9 +38,12 @@ use crate::registry::Rule; /// Use instead: /// ```python /// from abc import ABC, abstractmethod +/// from typing import ClassVar /// /// /// class Foo(ABC): +/// class_var: ClassVar[str] # unassigned +/// /// @abstractmethod /// def method(self): /// bar() @@ -44,6 +51,7 @@ use crate::registry::Rule; /// /// ## References /// - [Python documentation: `abc`](https://docs.python.org/3/library/abc.html) +/// - [Python documentation: `typing.ClassVar`](https://docs.python.org/3/library/typing.html#typing.ClassVar) #[violation] pub struct AbstractBaseClassWithoutAbstractMethod { name: String, @@ -53,7 +61,7 @@ impl Violation for AbstractBaseClassWithoutAbstractMethod { #[derive_message_formats] fn message(&self) -> String { let AbstractBaseClassWithoutAbstractMethod { name } = self; - format!("`{name}` is an abstract base class, but it has no abstract methods") + format!("`{name}` is an abstract base class, but it has neither abstract methods nor abstract class variables") } } diff --git a/scripts/generate_mkdocs.py b/scripts/generate_mkdocs.py index e2b3a23e4966a8..be72853c0636d4 100644 --- a/scripts/generate_mkdocs.py +++ b/scripts/generate_mkdocs.py @@ -110,7 +110,8 @@ def generate_rule_metadata(rule_doc: Path) -> None: For example: ```yaml --- - description: Checks for abstract classes without abstract methods. + description: Checks for abstract classes without abstract methods or abstract class + variables. tags: - B024 ---