|
2 | 2 |
|
3 | 3 | import python
|
4 | 4 | import semmle.python.ApiGraphs
|
| 5 | +import DataFlow |
5 | 6 |
|
6 | 7 | /** Holds if `f` is a method of the class `c`. */
|
7 | 8 | private predicate methodOfClass(Function f, Class c) { f.getScope() = c }
|
@@ -34,13 +35,29 @@ private predicate isZopeInterface(Class c) {
|
34 | 35 | .asExpr()
|
35 | 36 | }
|
36 | 37 |
|
| 38 | +/** |
| 39 | + * Holds if `f` is used in the initialisation of `c`. |
| 40 | + * This means `f` isn't being used as a normal method. |
| 41 | + * Ideally it should be a `@staticmethod`; however this wasn't possible prior to Python 3.10. |
| 42 | + * We exclude this case from the `not-named-self` query. |
| 43 | + * However there is potential for a new query that specifically covers and alerts for this case. |
| 44 | + */ |
| 45 | +private predicate usedInInit(Function f, Class c) { |
| 46 | + methodOfClass(f, c) and |
| 47 | + exists(Call call | |
| 48 | + call.getScope() = c and |
| 49 | + DataFlow::localFlow(DataFlow::exprNode(f.getDefinition()), DataFlow::exprNode(call.getFunc())) |
| 50 | + ) |
| 51 | +} |
| 52 | + |
37 | 53 | /** Holds if the first parameter of `f` should be named `self`. */
|
38 | 54 | predicate shouldBeSelf(Function f, Class c) {
|
39 | 55 | methodOfClass(f, c) and
|
40 | 56 | not isStaticMethod(f) and
|
41 | 57 | not isClassMethod(f) and
|
42 | 58 | not isMetaclass(c) and
|
43 |
| - not isZopeInterface(c) |
| 59 | + not isZopeInterface(c) and |
| 60 | + not usedInInit(f, c) |
44 | 61 | }
|
45 | 62 |
|
46 | 63 | /** Holds if the first parameter of `f` should be named `cls`. */
|
@@ -73,8 +90,17 @@ predicate firstArgShouldBeNamedSelfAndIsnt(Function f) {
|
73 | 90 | not firstArgNamedSelf(f)
|
74 | 91 | }
|
75 | 92 |
|
| 93 | +/** Holds if `f` is a regular method of a metaclass, and its first argument is named `self`. */ |
| 94 | +private predicate metaclassNamedSelf(Function f, Class c) { |
| 95 | + methodOfClass(f, c) and |
| 96 | + firstArgNamedSelf(f) and |
| 97 | + isMetaclass(c) and |
| 98 | + not isClassMethod(f) |
| 99 | +} |
| 100 | + |
76 | 101 | /** Holds if the first parameter of `f` should be named `cls`, but isn't. */
|
77 | 102 | predicate firstArgShouldBeNamedClsAndIsnt(Function f) {
|
78 | 103 | shouldBeCls(f, _) and
|
79 |
| - not firstArgNamedCls(f) |
| 104 | + not firstArgNamedCls(f) and |
| 105 | + not metaclassNamedSelf(f, _) |
80 | 106 | }
|
0 commit comments