mirror of https://github.com/astral-sh/ruff
Avoid underflow in expected-special-method-signature (#4377)
This commit is contained in:
parent
1ccef5150d
commit
ffcf0618c7
|
|
@ -1,19 +1,16 @@
|
|||
class TestClass:
|
||||
def __bool__(self):
|
||||
...
|
||||
|
||||
|
||||
def __bool__(self, x): # too many mandatory args
|
||||
...
|
||||
|
||||
|
||||
def __bool__(self, x=1): # additional optional args OK
|
||||
...
|
||||
|
||||
def __bool__(self, *args): # varargs OK
|
||||
...
|
||||
|
||||
|
||||
def __bool__(): # ignored; should be caughty by E0211/N805
|
||||
...
|
||||
|
||||
|
||||
@staticmethod
|
||||
def __bool__():
|
||||
...
|
||||
|
|
@ -21,31 +18,58 @@ class TestClass:
|
|||
@staticmethod
|
||||
def __bool__(x): # too many mandatory args
|
||||
...
|
||||
|
||||
|
||||
@staticmethod
|
||||
def __bool__(x=1): # additional optional args OK
|
||||
...
|
||||
|
||||
|
||||
def __eq__(self, other): # multiple args
|
||||
...
|
||||
|
||||
|
||||
def __eq__(self, other=1): # expected arg is optional
|
||||
...
|
||||
|
||||
|
||||
def __eq__(self): # too few mandatory args
|
||||
...
|
||||
|
||||
|
||||
def __eq__(self, other, other_other): # too many mandatory args
|
||||
...
|
||||
|
||||
def __round__(self): # allow zero additional args.
|
||||
|
||||
def __round__(self): # allow zero additional args
|
||||
...
|
||||
|
||||
def __round__(self, x): # allow one additional args.
|
||||
|
||||
def __round__(self, x): # allow one additional args
|
||||
...
|
||||
|
||||
|
||||
def __round__(self, x, y): # disallow 2 args
|
||||
...
|
||||
|
||||
|
||||
def __round__(self, x, y, z=2): # disallow 3 args even when one is optional
|
||||
...
|
||||
...
|
||||
|
||||
def __eq__(self, *args): # ignore *args
|
||||
...
|
||||
|
||||
def __eq__(self, x, *args): # extra *args is ok
|
||||
...
|
||||
|
||||
def __eq__(self, x, y, *args): # too many args with *args
|
||||
...
|
||||
|
||||
def __round__(self, *args): # allow zero additional args
|
||||
...
|
||||
|
||||
def __round__(self, x, *args): # allow one additional args
|
||||
...
|
||||
|
||||
def __round__(self, x, y, *args): # disallow 2 args
|
||||
...
|
||||
|
||||
def __eq__(self, **kwargs): # ignore **kwargs
|
||||
...
|
||||
|
||||
def __eq__(self, /, other=42): # ignore positional-only args
|
||||
...
|
||||
|
||||
def __eq__(self, *, other=42): # ignore positional-only args
|
||||
...
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ use ruff_macros::{derive_message_formats, violation};
|
|||
use ruff_python_ast::helpers::identifier_range;
|
||||
use ruff_python_ast::source_code::Locator;
|
||||
use ruff_python_semantic::analyze::visibility::is_staticmethod;
|
||||
use ruff_python_semantic::scope::ScopeKind;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
|
||||
|
|
@ -109,7 +108,12 @@ pub fn unexpected_special_method_signature(
|
|||
args: &Arguments,
|
||||
locator: &Locator,
|
||||
) {
|
||||
if !matches!(checker.ctx.scope().kind, ScopeKind::Class(_)) {
|
||||
if !checker.ctx.scope().kind.is_class() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Ignore methods with positional-only or keyword-only parameters, or variadic parameters.
|
||||
if !args.posonlyargs.is_empty() || !args.kwonlyargs.is_empty() || args.kwarg.is_some() {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -126,18 +130,22 @@ pub fn unexpected_special_method_signature(
|
|||
return;
|
||||
};
|
||||
|
||||
let emit = match expected_params {
|
||||
ExpectedParams::Range(min, max) => !(min..=max).contains(&actual_params),
|
||||
ExpectedParams::Fixed(expected) => match expected.cmp(&mandatory_params) {
|
||||
Ordering::Less => true,
|
||||
Ordering::Greater => {
|
||||
args.vararg.is_none() && optional_params < (expected - mandatory_params)
|
||||
let valid_signature = match expected_params {
|
||||
ExpectedParams::Range(min, max) => {
|
||||
if mandatory_params >= min {
|
||||
mandatory_params <= max
|
||||
} else {
|
||||
args.vararg.is_some() || actual_params <= max
|
||||
}
|
||||
Ordering::Equal => false,
|
||||
}
|
||||
ExpectedParams::Fixed(expected) => match expected.cmp(&mandatory_params) {
|
||||
Ordering::Less => false,
|
||||
Ordering::Greater => args.vararg.is_some() || actual_params >= expected,
|
||||
Ordering::Equal => true,
|
||||
},
|
||||
};
|
||||
|
||||
if emit {
|
||||
if !valid_signature {
|
||||
checker.diagnostics.push(Diagnostic::new(
|
||||
UnexpectedSpecialMethodSignature {
|
||||
method_name: name.to_owned(),
|
||||
|
|
|
|||
|
|
@ -4,54 +4,72 @@ source: crates/ruff/src/rules/pylint/mod.rs
|
|||
unexpected_special_method_signature.py:5:9: PLE0302 The special method `__bool__` expects 1 parameter, 2 were given
|
||||
|
|
||||
5 | ...
|
||||
6 |
|
||||
6 |
|
||||
7 | def __bool__(self, x): # too many mandatory args
|
||||
| ^^^^^^^^ PLE0302
|
||||
8 | ...
|
||||
|
|
||||
|
||||
unexpected_special_method_signature.py:22:9: PLE0302 The special method `__bool__` expects 0 parameters, 1 was given
|
||||
unexpected_special_method_signature.py:19:9: PLE0302 The special method `__bool__` expects 0 parameters, 1 was given
|
||||
|
|
||||
22 | @staticmethod
|
||||
23 | def __bool__(x): # too many mandatory args
|
||||
19 | @staticmethod
|
||||
20 | def __bool__(x): # too many mandatory args
|
||||
| ^^^^^^^^ PLE0302
|
||||
24 | ...
|
||||
21 | ...
|
||||
|
|
||||
|
||||
unexpected_special_method_signature.py:35:9: PLE0302 The special method `__eq__` expects 2 parameters, 1 was given
|
||||
unexpected_special_method_signature.py:32:9: PLE0302 The special method `__eq__` expects 2 parameters, 1 was given
|
||||
|
|
||||
32 | ...
|
||||
33 |
|
||||
34 | def __eq__(self): # too few mandatory args
|
||||
| ^^^^^^ PLE0302
|
||||
35 | ...
|
||||
|
|
||||
|
||||
unexpected_special_method_signature.py:35:9: PLE0302 The special method `__eq__` expects 2 parameters, 3 were given
|
||||
|
|
||||
35 | ...
|
||||
36 |
|
||||
37 | def __eq__(self): # too few mandatory args
|
||||
36 |
|
||||
37 | def __eq__(self, other, other_other): # too many mandatory args
|
||||
| ^^^^^^ PLE0302
|
||||
38 | ...
|
||||
|
|
||||
|
||||
unexpected_special_method_signature.py:38:9: PLE0302 The special method `__eq__` expects 2 parameters, 3 were given
|
||||
unexpected_special_method_signature.py:44:9: PLE0302 The special method `__round__` expects between 1 and 2 parameters, 3 were given
|
||||
|
|
||||
38 | ...
|
||||
39 |
|
||||
40 | def __eq__(self, other, other_other): # too many mandatory args
|
||||
| ^^^^^^ PLE0302
|
||||
41 | ...
|
||||
44 | ...
|
||||
45 |
|
||||
46 | def __round__(self, x, y): # disallow 2 args
|
||||
| ^^^^^^^^^ PLE0302
|
||||
47 | ...
|
||||
|
|
||||
|
||||
unexpected_special_method_signature.py:47:9: PLE0302 The special method `__round__` expects between 1 and 2 parameters, 3 were given
|
||||
unexpected_special_method_signature.py:47:9: PLE0302 The special method `__round__` expects between 1 and 2 parameters, 4 were given
|
||||
|
|
||||
47 | ...
|
||||
48 |
|
||||
49 | def __round__(self, x, y): # disallow 2 args
|
||||
48 |
|
||||
49 | def __round__(self, x, y, z=2): # disallow 3 args even when one is optional
|
||||
| ^^^^^^^^^ PLE0302
|
||||
50 | ...
|
||||
|
|
||||
|
||||
unexpected_special_method_signature.py:50:9: PLE0302 The special method `__round__` expects between 1 and 2 parameters, 4 were given
|
||||
unexpected_special_method_signature.py:56:9: PLE0302 The special method `__eq__` expects 2 parameters, 3 were given
|
||||
|
|
||||
50 | ...
|
||||
51 |
|
||||
52 | def __round__(self, x, y, z=2): # disallow 3 args even when one is optional
|
||||
56 | ...
|
||||
57 |
|
||||
58 | def __eq__(self, x, y, *args): # too many args with *args
|
||||
| ^^^^^^ PLE0302
|
||||
59 | ...
|
||||
|
|
||||
|
||||
unexpected_special_method_signature.py:65:9: PLE0302 The special method `__round__` expects between 1 and 2 parameters, 3 were given
|
||||
|
|
||||
65 | ...
|
||||
66 |
|
||||
67 | def __round__(self, x, y, *args): # disallow 2 args
|
||||
| ^^^^^^^^^ PLE0302
|
||||
53 | ...
|
||||
68 | ...
|
||||
|
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue