diff --git a/crates/ruff_linter/src/checkers/ast/analyze/definitions.rs b/crates/ruff_linter/src/checkers/ast/analyze/definitions.rs index 6cafa66f49..8b5ba3700d 100644 --- a/crates/ruff_linter/src/checkers/ast/analyze/definitions.rs +++ b/crates/ruff_linter/src/checkers/ast/analyze/definitions.rs @@ -1,13 +1,15 @@ use ruff_python_ast::str::raw_contents_range; use ruff_text_size::{Ranged, TextRange}; -use ruff_python_semantic::{BindingKind, ContextualizedDefinition, Export}; +use ruff_python_semantic::{ + BindingKind, ContextualizedDefinition, Definition, Export, Member, MemberKind, +}; use crate::checkers::ast::Checker; use crate::codes::Rule; use crate::docstrings::Docstring; use crate::fs::relativize_path; -use crate::rules::{flake8_annotations, flake8_pyi, pydocstyle}; +use crate::rules::{flake8_annotations, flake8_pyi, pydocstyle, pylint}; use crate::{docstrings, warn_user}; /// Run lint rules over all [`Definition`] nodes in the [`SemanticModel`]. @@ -31,6 +33,7 @@ pub(crate) fn definitions(checker: &mut Checker) { ]); let enforce_stubs = checker.source_type.is_stub() && checker.enabled(Rule::DocstringInStub); let enforce_stubs_and_runtime = checker.enabled(Rule::IterMethodReturnIterable); + let enforce_dunder_method = checker.enabled(Rule::BadDunderMethodName); let enforce_docstrings = checker.any_enabled(&[ Rule::BlankLineAfterLastSection, Rule::BlankLineAfterSummary, @@ -80,7 +83,12 @@ pub(crate) fn definitions(checker: &mut Checker) { Rule::UndocumentedPublicPackage, ]); - if !enforce_annotations && !enforce_docstrings && !enforce_stubs && !enforce_stubs_and_runtime { + if !enforce_annotations + && !enforce_docstrings + && !enforce_stubs + && !enforce_stubs_and_runtime + && !enforce_dunder_method + { return; } @@ -147,6 +155,19 @@ pub(crate) fn definitions(checker: &mut Checker) { } } + // pylint + if enforce_dunder_method { + if checker.enabled(Rule::BadDunderMethodName) { + if let Definition::Member(Member { + kind: MemberKind::Method(method), + .. + }) = definition + { + pylint::rules::bad_dunder_method_name(checker, method); + } + } + } + // pydocstyle if enforce_docstrings { if pydocstyle::helpers::should_ignore_definition( diff --git a/crates/ruff_linter/src/checkers/ast/analyze/statement.rs b/crates/ruff_linter/src/checkers/ast/analyze/statement.rs index fae6b0b527..3ceac94574 100644 --- a/crates/ruff_linter/src/checkers/ast/analyze/statement.rs +++ b/crates/ruff_linter/src/checkers/ast/analyze/statement.rs @@ -513,9 +513,6 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) { if checker.enabled(Rule::SingleStringSlots) { pylint::rules::single_string_slots(checker, class_def); } - if checker.enabled(Rule::BadDunderMethodName) { - pylint::rules::bad_dunder_method_name(checker, body); - } if checker.enabled(Rule::MetaClassABCMeta) { refurb::rules::metaclass_abcmeta(checker, class_def); } diff --git a/crates/ruff_linter/src/rules/pylint/rules/bad_dunder_method_name.rs b/crates/ruff_linter/src/rules/pylint/rules/bad_dunder_method_name.rs index 828ba64354..054b9a0d20 100644 --- a/crates/ruff_linter/src/rules/pylint/rules/bad_dunder_method_name.rs +++ b/crates/ruff_linter/src/rules/pylint/rules/bad_dunder_method_name.rs @@ -1,7 +1,7 @@ use ruff_diagnostics::{Diagnostic, Violation}; use ruff_macros::{derive_message_formats, violation}; +use ruff_python_ast as ast; use ruff_python_ast::identifier::Identifier; -use ruff_python_ast::Stmt; use ruff_python_semantic::analyze::visibility; use crate::checkers::ast::Checker; @@ -56,32 +56,32 @@ impl Violation for BadDunderMethodName { } /// PLW3201 -pub(crate) fn bad_dunder_method_name(checker: &mut Checker, class_body: &[Stmt]) { - for method in class_body - .iter() - .filter_map(ruff_python_ast::Stmt::as_function_def_stmt) - .filter(|method| { - if is_known_dunder_method(&method.name) - || checker - .settings - .pylint - .allow_dunder_method_names - .contains(method.name.as_str()) - || matches!(method.name.as_str(), "_") - { - return false; - } - method.name.starts_with('_') && method.name.ends_with('_') - }) - { - if visibility::is_override(&method.decorator_list, checker.semantic()) { - continue; - } - checker.diagnostics.push(Diagnostic::new( - BadDunderMethodName { - name: method.name.to_string(), - }, - method.identifier(), - )); +pub(crate) fn bad_dunder_method_name(checker: &mut Checker, method: &ast::StmtFunctionDef) { + // If the name isn't a dunder, skip it. + if !method.name.starts_with('_') || !method.name.ends_with('_') { + return; } + + // If the name is explicitly allowed, skip it. + if is_known_dunder_method(&method.name) + || checker + .settings + .pylint + .allow_dunder_method_names + .contains(method.name.as_str()) + || matches!(method.name.as_str(), "_") + { + return; + } + + if visibility::is_override(&method.decorator_list, checker.semantic()) { + return; + } + + checker.diagnostics.push(Diagnostic::new( + BadDunderMethodName { + name: method.name.to_string(), + }, + method.identifier(), + )); }