-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
6fb1272
commit 1dbb6ae
Showing
9 changed files
with
206 additions
and
98 deletions.
There are no files selected for viewing
20 changes: 0 additions & 20 deletions
20
crates/ruff_linter/resources/test/fixtures/pylint/E1519.py
This file was deleted.
Oops, something went wrong.
39 changes: 39 additions & 0 deletions
39
crates/ruff_linter/resources/test/fixtures/pylint/singledispatch_method.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
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 | ||
|
||
@singledispatch | ||
@staticmethod | ||
def do(position): | ||
pass | ||
|
||
# False negative (flagged by Pylint). | ||
@convert_position.register | ||
@classmethod | ||
def _(cls, position: str) -> tuple: | ||
position_a, position_b = position.split(",") | ||
return (int(position_a), int(position_b)) | ||
|
||
# False negative (flagged by Pylint). | ||
@convert_position.register | ||
@classmethod | ||
def _(cls, position: tuple) -> str: | ||
return f"{position[0]},{position[1]}" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
71 changes: 0 additions & 71 deletions
71
crates/ruff_linter/src/rules/pylint/rules/single_dispatch_method.rs
This file was deleted.
Oops, something went wrong.
117 changes: 117 additions & 0 deletions
117
crates/ruff_linter/src/rules/pylint/rules/singledispatch_method.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
use ruff_diagnostics::{Diagnostic, Edit, Fix, FixAvailability, Violation}; | ||
use ruff_macros::{derive_message_formats, violation}; | ||
use ruff_python_ast as ast; | ||
use ruff_python_semantic::analyze::function_type; | ||
use ruff_python_semantic::Scope; | ||
use ruff_text_size::Ranged; | ||
|
||
use crate::checkers::ast::Checker; | ||
use crate::importer::ImportRequest; | ||
|
||
/// ## What it does | ||
/// Checks for `@singledispatch` decorators on class and instance methods. | ||
/// | ||
/// ## Why is this bad? | ||
/// The `@singledispatch` decorator is intended for use with functions, not methods. | ||
/// | ||
/// Instead, use the `@singledispatchmethod` decorator, or migrate the method to a | ||
/// standalone function or `@staticmethod`. | ||
/// | ||
/// ## Example | ||
/// ```python | ||
/// from functools import singledispatch | ||
/// | ||
/// class Class: | ||
/// @singledispatch | ||
/// def method(self, arg): | ||
/// ... | ||
/// ``` | ||
/// | ||
/// Use instead: | ||
/// ```python | ||
/// from functools import singledispatchmethod | ||
/// | ||
/// class Class: | ||
/// @singledispatchmethod | ||
/// def method(self, arg): | ||
/// ... | ||
/// ``` | ||
/// | ||
/// ## Fix safety | ||
/// This rule's fix is marked as unsafe, as migrating from `@singledispatch` to | ||
/// `@singledispatchmethod` may change the behavior of the code. | ||
#[violation] | ||
pub struct SingledispatchMethod; | ||
|
||
impl Violation for SingledispatchMethod { | ||
const FIX_AVAILABILITY: FixAvailability = FixAvailability::Sometimes; | ||
|
||
#[derive_message_formats] | ||
fn message(&self) -> String { | ||
format!("`@singledispatch` decorator should not be used on methods") | ||
} | ||
|
||
fn fix_title(&self) -> Option<String> { | ||
Some("Replace with `@singledispatchmethod`".to_string()) | ||
} | ||
} | ||
|
||
/// E1519 | ||
pub(crate) fn singledispatch_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; | ||
}; | ||
|
||
let type_ = function_type::classify( | ||
&name, | ||
&decorator_list, | ||
parent, | ||
checker.semantic(), | ||
&checker.settings.pep8_naming.classmethod_decorators, | ||
&checker.settings.pep8_naming.staticmethod_decorators, | ||
); | ||
if !matches!( | ||
type_, | ||
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"]) | ||
}) | ||
{ | ||
let mut diagnostic = Diagnostic::new(SingledispatchMethod, decorator.range()); | ||
diagnostic.try_set_fix(|| { | ||
let (import_edit, binding) = checker.importer().get_or_import_symbol( | ||
&ImportRequest::import("functools", "singledispatchmethod"), | ||
decorator.start(), | ||
checker.semantic(), | ||
)?; | ||
Ok(Fix::unsafe_edits( | ||
Edit::range_replacement(binding, decorator.expression.range()), | ||
[import_edit], | ||
)) | ||
}); | ||
diagnostics.push(diagnostic); | ||
} | ||
} | ||
} |
43 changes: 43 additions & 0 deletions
43
...pylint/snapshots/ruff_linter__rules__pylint__tests__PLE1519_singledispatch_method.py.snap
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
--- | ||
source: crates/ruff_linter/src/rules/pylint/mod.rs | ||
--- | ||
singledispatch_method.py:10:5: PLE1519 [*] `@singledispatch` decorator should not be used on methods | ||
| | ||
9 | class Board: | ||
10 | @singledispatch # [singledispatch-method] | ||
| ^^^^^^^^^^^^^^^ PLE1519 | ||
11 | @classmethod | ||
12 | def convert_position(cls, position): | ||
| | ||
= help: Replace with `@singledispatchmethod` | ||
|
||
ℹ Unsafe fix | ||
7 7 | | ||
8 8 | | ||
9 9 | class Board: | ||
10 |- @singledispatch # [singledispatch-method] | ||
10 |+ @singledispatchmethod # [singledispatch-method] | ||
11 11 | @classmethod | ||
12 12 | def convert_position(cls, position): | ||
13 13 | pass | ||
|
||
singledispatch_method.py:15:5: PLE1519 [*] `@singledispatch` decorator should not be used on methods | ||
| | ||
13 | pass | ||
14 | | ||
15 | @singledispatch # [singledispatch-method] | ||
| ^^^^^^^^^^^^^^^ PLE1519 | ||
16 | def move(self, position): | ||
17 | pass | ||
| | ||
= help: Replace with `@singledispatchmethod` | ||
|
||
ℹ Unsafe fix | ||
12 12 | def convert_position(cls, position): | ||
13 13 | pass | ||
14 14 | | ||
15 |- @singledispatch # [singledispatch-method] | ||
15 |+ @singledispatchmethod # [singledispatch-method] | ||
16 16 | def move(self, position): | ||
17 17 | pass | ||
18 18 | |