Skip to content

Commit

Permalink
Implement pylint E1519 singledispatch-method #970
Browse files Browse the repository at this point in the history
  • Loading branch information
Glyphack committed Feb 28, 2024
1 parent 8044c24 commit 3111e95
Show file tree
Hide file tree
Showing 8 changed files with 123 additions and 0 deletions.
20 changes: 20 additions & 0 deletions crates/ruff_linter/resources/test/fixtures/pylint/E1519.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
from functools import singledispatch, singledispatchmethod


@singledispatch
def convert_position(position):
pass

class Board:
@singledispatch # [singledispatch-method]
@classmethod
def convert_position(cls, position):
pass

@singledispatch # [singledispatch-method]
def move(self, position):
pass

@singledispatchmethod
def place(self, position):
pass
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ pub(crate) fn deferred_scopes(checker: &mut Checker) {
Rule::UnusedPrivateTypedDict,
Rule::UnusedStaticMethodArgument,
Rule::UnusedVariable,
Rule::SingleDispatchMethod,
]) {
return;
}
Expand Down Expand Up @@ -389,6 +390,10 @@ pub(crate) fn deferred_scopes(checker: &mut Checker) {
if checker.enabled(Rule::TooManyLocals) {
pylint::rules::too_many_locals(checker, scope, &mut diagnostics);
}

if checker.enabled(Rule::SingleDispatchMethod) {
pylint::rules::single_dispatch_method(checker, scope, &mut diagnostics);
}
}
}
checker.diagnostics.extend(diagnostics);
Expand Down
1 change: 1 addition & 0 deletions crates/ruff_linter/src/codes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,7 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
(Pylint, "E1307") => (RuleGroup::Stable, rules::pylint::rules::BadStringFormatType),
(Pylint, "E1310") => (RuleGroup::Stable, rules::pylint::rules::BadStrStripCall),
(Pylint, "E1507") => (RuleGroup::Stable, rules::pylint::rules::InvalidEnvvarValue),
(Pylint, "E1519") => (RuleGroup::Preview, rules::pylint::rules::SingleDispatchMethod),
(Pylint, "E1700") => (RuleGroup::Stable, rules::pylint::rules::YieldFromInAsyncFunction),
(Pylint, "E2502") => (RuleGroup::Stable, rules::pylint::rules::BidirectionalUnicode),
(Pylint, "E2510") => (RuleGroup::Stable, rules::pylint::rules::InvalidCharacterBackspace),
Expand Down
1 change: 1 addition & 0 deletions crates/ruff_linter/src/rules/pylint/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ mod tests {
use crate::settings::LinterSettings;
use crate::test::test_path;

#[test_case(Rule::SingleDispatchMethod, Path::new("E1519.py"))]
#[test_case(Rule::AssertOnStringLiteral, Path::new("assert_on_string_literal.py"))]
#[test_case(Rule::AwaitOutsideAsync, Path::new("await_outside_async.py"))]
#[test_case(Rule::BadOpenMode, Path::new("bad_open_mode.py"))]
Expand Down
2 changes: 2 additions & 0 deletions crates/ruff_linter/src/rules/pylint/rules/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ pub(crate) use repeated_isinstance_calls::*;
pub(crate) use repeated_keyword_argument::*;
pub(crate) use return_in_init::*;
pub(crate) use self_assigning_variable::*;
pub(crate) use single_dispatch_method::*;
pub(crate) use single_string_slots::*;
pub(crate) use subprocess_popen_preexec_fn::*;
pub(crate) use subprocess_run_without_check::*;
Expand Down Expand Up @@ -139,6 +140,7 @@ mod repeated_isinstance_calls;
mod repeated_keyword_argument;
mod return_in_init;
mod self_assigning_variable;
mod single_dispatch_method;
mod single_string_slots;
mod subprocess_popen_preexec_fn;
mod subprocess_run_without_check;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
use ruff_diagnostics::{Diagnostic, FixAvailability, Violation};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::{self as ast};
use ruff_python_semantic::{analyze::function_type, Scope};
use ruff_text_size::Ranged;

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

/// ## What it does
/// Checks for singledispatch decorators on class methods.
///
/// ## Why is this bad?
/// Single dispatch must happen on the type of first non self argument
#[violation]
pub struct SingleDispatchMethod;

impl Violation for SingleDispatchMethod {
const FIX_AVAILABILITY: FixAvailability = FixAvailability::None;

#[derive_message_formats]
fn message(&self) -> String {
format!("singledispatch decorator should not be used with methods, use singledispatchmethod instead.")
}
}

/// E1519
pub(crate) fn single_dispatch_method(
checker: &Checker,
scope: &Scope,
diagnostics: &mut Vec<Diagnostic>,
) {
let Some(func) = scope.kind.as_function() else {
return;
};

let ast::StmtFunctionDef {
name,
decorator_list,
..
} = func;

let Some(parent) = &checker.semantic().first_non_type_parent_scope(scope) else {
return;
};

if !matches!(
function_type::classify(
name,
decorator_list,
parent,
checker.semantic(),
&checker.settings.pep8_naming.classmethod_decorators,
&checker.settings.pep8_naming.staticmethod_decorators,
),
function_type::FunctionType::Method | function_type::FunctionType::ClassMethod
) {
return;
}

for decorator in decorator_list {
if checker
.semantic()
.resolve_call_path(&decorator.expression)
.is_some_and(|call_path| {
matches!(call_path.as_slice(), ["functools", "singledispatch"])
})
{
diagnostics.push(Diagnostic::new(SingleDispatchMethod {}, decorator.range()));
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
---
source: crates/ruff_linter/src/rules/pylint/mod.rs
---
E1519.py:9:5: PLE1519 singledispatch decorator should not be used with methods, use singledispatchmethod instead.
|
8 | class Board:
9 | @singledispatch # [singledispatch-method]
| ^^^^^^^^^^^^^^^ PLE1519
10 | @classmethod
11 | def convert_position(cls, position):
|

E1519.py:14:5: PLE1519 singledispatch decorator should not be used with methods, use singledispatchmethod instead.
|
12 | pass
13 |
14 | @singledispatch # [singledispatch-method]
| ^^^^^^^^^^^^^^^ PLE1519
15 | def move(self, position):
16 | pass
|
2 changes: 2 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 3111e95

Please sign in to comment.