-
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
a8e50a7
commit 7c6b67d
Showing
8 changed files
with
206 additions
and
0 deletions.
There are no files selected for viewing
23 changes: 23 additions & 0 deletions
23
crates/ruff_linter/resources/test/fixtures/pylint/singledispatchmethod_function.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,23 @@ | ||
from functools import singledispatchmethod | ||
|
||
|
||
@singledispatchmethod # [singledispatchmethod-function] | ||
def convert_position(position): | ||
pass | ||
|
||
|
||
class Board: | ||
|
||
@singledispatchmethod # Ok | ||
@classmethod | ||
def convert_position(cls, position): | ||
pass | ||
|
||
@singledispatchmethod # Ok | ||
def move(self, position): | ||
pass | ||
|
||
@singledispatchmethod # [singledispatchmethod-function] | ||
@staticmethod | ||
def do(position): | ||
pass |
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
119 changes: 119 additions & 0 deletions
119
crates/ruff_linter/src/rules/pylint/rules/singledispatchmethod_function.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,119 @@ | ||
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 `@singledispatchmethod` decorators on functions or static methods. | ||
/// | ||
/// ## Why is this bad? | ||
/// The `@singledispatchmethod` decorator is intended for use with class and instance methods, not functions. | ||
/// | ||
/// Instead, use the `@singledispatch` decorator. | ||
/// | ||
/// ## Example | ||
/// ```python | ||
/// from functools import singledispatchmethod | ||
/// | ||
/// | ||
/// @singledispatchmethod | ||
/// def foo(arg): | ||
/// ... | ||
/// ``` | ||
/// | ||
/// Use instead: | ||
/// ```python | ||
/// from functools import singledispatchmethod | ||
/// | ||
/// | ||
/// @singledispatch | ||
/// def foo(arg): | ||
/// ... | ||
/// ``` | ||
/// | ||
/// ## Fix safety | ||
/// This rule's fix is marked as unsafe, as migrating from `@singledispatchmethod` to | ||
/// `@singledispatch` may change the behavior of the code. | ||
#[violation] | ||
pub struct SingledispatchmethodFunction; | ||
|
||
impl Violation for SingledispatchmethodFunction { | ||
const FIX_AVAILABILITY: FixAvailability = FixAvailability::Sometimes; | ||
|
||
#[derive_message_formats] | ||
fn message(&self) -> String { | ||
format!("`@singledispatchmethod` decorator should not be used on functions") | ||
} | ||
|
||
fn fix_title(&self) -> Option<String> { | ||
Some("Replace with `@singledispatch`".to_string()) | ||
} | ||
} | ||
|
||
/// E1520 | ||
pub(crate) fn singledispatchmethod_function( | ||
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::Function | function_type::FunctionType::StaticMethod | ||
) { | ||
return; | ||
} | ||
|
||
for decorator in decorator_list { | ||
if checker | ||
.semantic() | ||
.resolve_qualified_name(&decorator.expression) | ||
.is_some_and(|qualified_name| { | ||
matches!( | ||
qualified_name.segments(), | ||
["functools", "singledispatchmethod"] | ||
) | ||
}) | ||
{ | ||
let mut diagnostic = Diagnostic::new(SingledispatchmethodFunction, decorator.range()); | ||
diagnostic.try_set_fix(|| { | ||
let (import_edit, binding) = checker.importer().get_or_import_symbol( | ||
&ImportRequest::import("functools", "singledispatch"), | ||
decorator.start(), | ||
checker.semantic(), | ||
)?; | ||
Ok(Fix::unsafe_edits( | ||
Edit::range_replacement(binding, decorator.expression.range()), | ||
[import_edit], | ||
)) | ||
}); | ||
diagnostics.push(diagnostic); | ||
} | ||
} | ||
} |
50 changes: 50 additions & 0 deletions
50
...hots/ruff_linter__rules__pylint__tests__PLE1520_singledispatchmethod_function.py.snap.new
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,50 @@ | ||
--- | ||
source: crates/ruff_linter/src/rules/pylint/mod.rs | ||
assertion_line: 200 | ||
--- | ||
singledispatchmethod_function.py:4:1: PLE1520 [*] `@singledispatchmethod` decorator should not be used on functions | ||
| | ||
4 | @singledispatchmethod # [singledispatchmethod-function] | ||
| ^^^^^^^^^^^^^^^^^^^^^ PLE1520 | ||
5 | def convert_position(position): | ||
6 | pass | ||
| | ||
= help: Replace with `@singledispatch` | ||
|
||
ℹ Unsafe fix | ||
1 |-from functools import singledispatchmethod | ||
1 |+from functools import singledispatchmethod, singledispatch | ||
2 2 | | ||
3 3 | | ||
4 |-@singledispatchmethod # [singledispatchmethod-function] | ||
4 |+@singledispatch # [singledispatchmethod-function] | ||
5 5 | def convert_position(position): | ||
6 6 | pass | ||
7 7 | | ||
|
||
singledispatchmethod_function.py:20:5: PLE1520 [*] `@singledispatchmethod` decorator should not be used on functions | ||
| | ||
18 | pass | ||
19 | | ||
20 | @singledispatchmethod # [singledispatchmethod-function] | ||
| ^^^^^^^^^^^^^^^^^^^^^ PLE1520 | ||
21 | @staticmethod | ||
22 | def do(position): | ||
| | ||
= help: Replace with `@singledispatch` | ||
|
||
ℹ Unsafe fix | ||
1 |-from functools import singledispatchmethod | ||
1 |+from functools import singledispatchmethod, singledispatch | ||
2 2 | | ||
3 3 | | ||
4 4 | @singledispatchmethod # [singledispatchmethod-function] | ||
-------------------------------------------------------------------------------- | ||
17 17 | def move(self, position): | ||
18 18 | pass | ||
19 19 | | ||
20 |- @singledispatchmethod # [singledispatchmethod-function] | ||
20 |+ @singledispatch # [singledispatchmethod-function] | ||
21 21 | @staticmethod | ||
22 22 | def do(position): | ||
23 23 | pass |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.