Skip to content

Commit

Permalink
[pylint] Add duplicate-bases rule (#4411)
Browse files Browse the repository at this point in the history
  • Loading branch information
alonme authored May 13, 2023
1 parent 2f53781 commit 0a68636
Show file tree
Hide file tree
Showing 9 changed files with 89 additions and 0 deletions.
24 changes: 24 additions & 0 deletions crates/ruff/resources/test/fixtures/pylint/duplicate_bases.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
###
# Errors.
###
class A:
...


class B(A, A):
...


###
# Non-errors.
###
class C:
...


class D(C):
...


class E(A, C):
...
4 changes: 4 additions & 0 deletions crates/ruff/src/checkers/ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -761,6 +761,10 @@ where
if self.settings.rules.enabled(Rule::BuiltinVariableShadowing) {
flake8_builtins::rules::builtin_variable_shadowing(self, name, stmt);
}

if self.settings.rules.enabled(Rule::DuplicateBases) {
pylint::rules::duplicate_bases(self, name, bases);
}
}
StmtKind::Import(ast::StmtImport { names }) => {
if self.settings.rules.enabled(Rule::MultipleImportsOnOneLine) {
Expand Down
1 change: 1 addition & 0 deletions crates/ruff/src/codes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,7 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<Rule> {
(Pylint, "W2901") => Rule::RedefinedLoopName,
(Pylint, "E0302") => Rule::UnexpectedSpecialMethodSignature,
(Pylint, "W3301") => Rule::NestedMinMax,
(Pylint, "E0241") => Rule::DuplicateBases,

// flake8-builtins
(Flake8Builtins, "001") => Rule::BuiltinVariableShadowing,
Expand Down
1 change: 1 addition & 0 deletions crates/ruff/src/registry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,7 @@ ruff_macros::register_rules!(
rules::pylint::rules::LoggingTooManyArgs,
rules::pylint::rules::UnexpectedSpecialMethodSignature,
rules::pylint::rules::NestedMinMax,
rules::pylint::rules::DuplicateBases,
// flake8-builtins
rules::flake8_builtins::rules::BuiltinVariableShadowing,
rules::flake8_builtins::rules::BuiltinArgumentShadowing,
Expand Down
1 change: 1 addition & 0 deletions crates/ruff/src/rules/pylint/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ mod tests {
#[test_case(Rule::ImportSelf, Path::new("import_self/module.py"); "PLW0406")]
#[test_case(Rule::InvalidAllFormat, Path::new("invalid_all_format.py"); "PLE0605")]
#[test_case(Rule::InvalidAllObject, Path::new("invalid_all_object.py"); "PLE0604")]
#[test_case(Rule::DuplicateBases, Path::new("duplicate_bases.py"); "PLE0241")]
#[test_case(Rule::InvalidCharacterBackspace, Path::new("invalid_characters.py"); "PLE2510")]
#[test_case(Rule::InvalidCharacterEsc, Path::new("invalid_characters.py"); "PLE2513")]
#[test_case(Rule::InvalidCharacterNul, Path::new("invalid_characters.py"); "PLE2514")]
Expand Down
42 changes: 42 additions & 0 deletions crates/ruff/src/rules/pylint/rules/duplicate_bases.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
use std::hash::BuildHasherDefault;

use rustc_hash::FxHashSet;
use rustpython_parser::ast::{self, Expr, ExprKind, Identifier};

use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation};

use crate::checkers::ast::Checker;

#[violation]
pub struct DuplicateBases {
base: String,
class: String,
}

impl Violation for DuplicateBases {
#[derive_message_formats]
fn message(&self) -> String {
let DuplicateBases { base, class } = self;
format!("Duplicate base `{base}` for class `{class}`")
}
}

/// PLE0241
pub(crate) fn duplicate_bases(checker: &mut Checker, name: &str, bases: &[Expr]) {
let mut seen: FxHashSet<&Identifier> =
FxHashSet::with_capacity_and_hasher(bases.len(), BuildHasherDefault::default());
for base in bases {
if let ExprKind::Name(ast::ExprName { id, .. }) = &base.node {
if !seen.insert(id) {
checker.diagnostics.push(Diagnostic::new(
DuplicateBases {
base: id.to_string(),
class: name.to_string(),
},
base.range(),
));
}
}
}
}
2 changes: 2 additions & 0 deletions crates/ruff/src/rules/pylint/rules/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ pub(crate) use collapsible_else_if::{collapsible_else_if, CollapsibleElseIf};
pub(crate) use compare_to_empty_string::{compare_to_empty_string, CompareToEmptyString};
pub(crate) use comparison_of_constant::{comparison_of_constant, ComparisonOfConstant};
pub(crate) use continue_in_finally::{continue_in_finally, ContinueInFinally};
pub(crate) use duplicate_bases::{duplicate_bases, DuplicateBases};
pub(crate) use global_statement::{global_statement, GlobalStatement};
pub(crate) use global_variable_not_assigned::GlobalVariableNotAssigned;
pub(crate) use import_self::{import_from_self, import_self, ImportSelf};
Expand Down Expand Up @@ -57,6 +58,7 @@ mod collapsible_else_if;
mod compare_to_empty_string;
mod comparison_of_constant;
mod continue_in_finally;
mod duplicate_bases;
mod global_statement;
mod global_variable_not_assigned;
mod import_self;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
source: crates/ruff/src/rules/pylint/mod.rs
---
duplicate_bases.py:8:12: PLE0241 Duplicate base `A` for class `B`
|
8 | class B(A, A):
| ^ PLE0241
9 | ...
|


3 changes: 3 additions & 0 deletions ruff.schema.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 0a68636

Please sign in to comment.