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:
|
class TestClass:
|
||||||
def __bool__(self):
|
def __bool__(self):
|
||||||
...
|
...
|
||||||
|
|
||||||
def __bool__(self, x): # too many mandatory args
|
def __bool__(self, x): # too many mandatory args
|
||||||
...
|
...
|
||||||
|
|
||||||
def __bool__(self, x=1): # additional optional args OK
|
def __bool__(self, x=1): # additional optional args OK
|
||||||
...
|
...
|
||||||
|
|
||||||
def __bool__(self, *args): # varargs OK
|
|
||||||
...
|
|
||||||
|
|
||||||
def __bool__(): # ignored; should be caughty by E0211/N805
|
def __bool__(): # ignored; should be caughty by E0211/N805
|
||||||
...
|
...
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def __bool__():
|
def __bool__():
|
||||||
...
|
...
|
||||||
|
|
@ -21,31 +18,58 @@ class TestClass:
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def __bool__(x): # too many mandatory args
|
def __bool__(x): # too many mandatory args
|
||||||
...
|
...
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def __bool__(x=1): # additional optional args OK
|
def __bool__(x=1): # additional optional args OK
|
||||||
...
|
...
|
||||||
|
|
||||||
def __eq__(self, other): # multiple args
|
def __eq__(self, other): # multiple args
|
||||||
...
|
...
|
||||||
|
|
||||||
def __eq__(self, other=1): # expected arg is optional
|
def __eq__(self, other=1): # expected arg is optional
|
||||||
...
|
...
|
||||||
|
|
||||||
def __eq__(self): # too few mandatory args
|
def __eq__(self): # too few mandatory args
|
||||||
...
|
...
|
||||||
|
|
||||||
def __eq__(self, other, other_other): # too many 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): # disallow 2 args
|
||||||
...
|
...
|
||||||
|
|
||||||
def __round__(self, x, y, z=2): # disallow 3 args even when one is optional
|
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::helpers::identifier_range;
|
||||||
use ruff_python_ast::source_code::Locator;
|
use ruff_python_ast::source_code::Locator;
|
||||||
use ruff_python_semantic::analyze::visibility::is_staticmethod;
|
use ruff_python_semantic::analyze::visibility::is_staticmethod;
|
||||||
use ruff_python_semantic::scope::ScopeKind;
|
|
||||||
|
|
||||||
use crate::checkers::ast::Checker;
|
use crate::checkers::ast::Checker;
|
||||||
|
|
||||||
|
|
@ -109,7 +108,12 @@ pub fn unexpected_special_method_signature(
|
||||||
args: &Arguments,
|
args: &Arguments,
|
||||||
locator: &Locator,
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -126,18 +130,22 @@ pub fn unexpected_special_method_signature(
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
let emit = match expected_params {
|
let valid_signature = match expected_params {
|
||||||
ExpectedParams::Range(min, max) => !(min..=max).contains(&actual_params),
|
ExpectedParams::Range(min, max) => {
|
||||||
ExpectedParams::Fixed(expected) => match expected.cmp(&mandatory_params) {
|
if mandatory_params >= min {
|
||||||
Ordering::Less => true,
|
mandatory_params <= max
|
||||||
Ordering::Greater => {
|
} else {
|
||||||
args.vararg.is_none() && optional_params < (expected - mandatory_params)
|
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(
|
checker.diagnostics.push(Diagnostic::new(
|
||||||
UnexpectedSpecialMethodSignature {
|
UnexpectedSpecialMethodSignature {
|
||||||
method_name: name.to_owned(),
|
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
|
unexpected_special_method_signature.py:5:9: PLE0302 The special method `__bool__` expects 1 parameter, 2 were given
|
||||||
|
|
|
|
||||||
5 | ...
|
5 | ...
|
||||||
6 |
|
6 |
|
||||||
7 | def __bool__(self, x): # too many mandatory args
|
7 | def __bool__(self, x): # too many mandatory args
|
||||||
| ^^^^^^^^ PLE0302
|
| ^^^^^^^^ PLE0302
|
||||||
8 | ...
|
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
|
19 | @staticmethod
|
||||||
23 | def __bool__(x): # too many mandatory args
|
20 | def __bool__(x): # too many mandatory args
|
||||||
| ^^^^^^^^ PLE0302
|
| ^^^^^^^^ 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 | ...
|
35 | ...
|
||||||
36 |
|
36 |
|
||||||
37 | def __eq__(self): # too few mandatory args
|
37 | def __eq__(self, other, other_other): # too many mandatory args
|
||||||
| ^^^^^^ PLE0302
|
| ^^^^^^ PLE0302
|
||||||
38 | ...
|
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 | ...
|
44 | ...
|
||||||
39 |
|
45 |
|
||||||
40 | def __eq__(self, other, other_other): # too many mandatory args
|
46 | def __round__(self, x, y): # disallow 2 args
|
||||||
| ^^^^^^ PLE0302
|
| ^^^^^^^^^ PLE0302
|
||||||
41 | ...
|
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 | ...
|
47 | ...
|
||||||
48 |
|
48 |
|
||||||
49 | def __round__(self, x, y): # disallow 2 args
|
49 | def __round__(self, x, y, z=2): # disallow 3 args even when one is optional
|
||||||
| ^^^^^^^^^ PLE0302
|
| ^^^^^^^^^ PLE0302
|
||||||
50 | ...
|
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 | ...
|
56 | ...
|
||||||
51 |
|
57 |
|
||||||
52 | def __round__(self, x, y, z=2): # disallow 3 args even when one is optional
|
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
|
| ^^^^^^^^^ PLE0302
|
||||||
53 | ...
|
68 | ...
|
||||||
|
|
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue