-
Notifications
You must be signed in to change notification settings - Fork 2k
Expand file tree
/
Copy pathSignatureOverriddenMethod.ql
More file actions
82 lines (76 loc) · 2.84 KB
/
SignatureOverriddenMethod.ql
File metadata and controls
82 lines (76 loc) · 2.84 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
/**
* @name Signature mismatch in overriding method
* @description Overriding a method without ensuring that both methods accept the same
* number and type of parameters has the potential to cause an error when there is a mismatch.
* @kind problem
* @problem.severity warning
* @tags quality
* reliability
* correctness
* @sub-severity high
* @precision very-high
* @id py/inheritance/signature-mismatch
*/
import python
import semmle.python.dataflow.new.internal.DataFlowDispatch
predicate overrides(Function base, Function sub) {
base.getName() = sub.getName() and
base.getScope() = getADirectSuperclass*(sub.getScope())
}
/** Holds if no way to call `base` would be valid for `sub`. The `msg` applies to the `sub method. */
predicate strongSignatureMismatch(Function base, Function sub, string msg) {
overrides(base, sub) and
(
sub.getMinPositionalArguments() > base.getMaxPositionalArguments() and
msg = "requires more positional arguments than overridden $@ allows."
or
sub.getMaxPositionalArguments() < base.getMinPositionalArguments() and
msg = "requires fewer positional arguments than overridden $@ allows."
)
}
/** Holds if there may be some ways to call `base` that would not be valid for `sub`. The `msg` applies to the `sub` method. */
predicate weakSignatureMismatch(Function base, Function sub, string msg) {
overrides(base, sub) and
(
sub.getMinPositionalArguments() > base.getMinPositionalArguments() and
msg = "requires more positional arguments than overridden $@ may accept."
or
sub.getMaxPositionalArguments() < base.getMaxPositionalArguments() and
msg = "requires fewer positional arguments than overridden $@ may accept."
or
exists(string arg |
// TODO: positional-only args not considered
// e.g. `def foo(x, y, /, z):` has x,y as positional only args, should not be considered as possible kw args
arg = base.getAnArg().getName() and
not arg = sub.getAnArg().getName() and
not exists(sub.getKwarg()) and
msg = "does not accept keyword argument " + arg + ", which overridden $@ does."
)
or
exists(base.getKwarg()) and
not exists(sub.getKwarg()) and
msg = "does not accept arbitrary keyword arguments, which overridden $@ does."
)
}
predicate ignore(Function f) {
isClassmethod(f)
or
exists(Function g |
g.getScope() = f.getScope() and
g.getName() = f.getName() and
g != f
)
}
from Function base, Function sub, string msg
where
// not exists(base.getACall()) and
// not exists(FunctionValue a_derived |
// a_derived.overrides(base) and
// exists(a_derived.getACall())
// ) and
not sub.isSpecialMethod() and
sub.getName() != "__init__" and
not ignore(sub) and
not ignore(base) and
strongSignatureMismatch(base, sub, msg)
select sub, "This method " + msg, base, base.getQualifiedName()