@@ -5,6 +5,7 @@ import semmle.python.ApiGraphs
5
5
import semmle.python.dataflow.new.internal.DataFlowDispatch
6
6
import codeql.util.Option
7
7
8
+ /** Holds if `meth` is a method named `name` that transitively calls `calledMulti` of the same name via the calls `call1` and `call2`. */
8
9
predicate multipleCallsToSuperclassMethod (
9
10
Function meth , Function calledMulti , DataFlow:: MethodCallNode call1 ,
10
11
DataFlow:: MethodCallNode call2 , string name
@@ -19,6 +20,7 @@ predicate multipleCallsToSuperclassMethod(
19
20
)
20
21
}
21
22
23
+ /** Gets a method transitively called by `meth` named `name` with `call` that it overrides, with `mroBase` as the type determining the MRO to search. */
22
24
Function getASuperCallTargetFromCall (
23
25
Class mroBase , Function meth , DataFlow:: MethodCallNode call , string name
24
26
) {
@@ -29,6 +31,7 @@ Function getASuperCallTargetFromCall(
29
31
)
30
32
}
31
33
34
+ /** Gets the method called by `meth` named `name` with `call`, with `mroBase` as the type determining the MRO to search. */
32
35
Function getDirectSuperCallTargetFromCall (
33
36
Class mroBase , Function meth , DataFlow:: MethodCallNode call , string name
34
37
) {
@@ -51,6 +54,7 @@ Function getDirectSuperCallTargetFromCall(
51
54
)
52
55
}
53
56
57
+ /** Gets a method that is transitively called by a call to `cls.<name>`, with `mroBase` as the type determining the MRO to search. */
54
58
Function getASuperCallTargetFromClass ( Class mroBase , Class cls , string name ) {
55
59
exists ( Function target |
56
60
target = findFunctionAccordingToMroKnownStartingClass ( cls , mroBase , name ) and
@@ -62,6 +66,7 @@ Function getASuperCallTargetFromClass(Class mroBase, Class cls, string name) {
62
66
)
63
67
}
64
68
69
+ /** Holds if `meth` does something besides calling a superclass method. */
65
70
predicate nonTrivial ( Function meth ) {
66
71
exists ( Stmt s | s = meth .getAStmt ( ) |
67
72
not s instanceof Pass and
@@ -74,20 +79,23 @@ predicate nonTrivial(Function meth) {
74
79
exists ( meth .getANormalExit ( ) ) // doesn't always raise an exception
75
80
}
76
81
82
+ /** Holds if `call` is a call to `super().<name>`. No distinction is made btween 0- and 2- arg super calls. */
77
83
predicate superCall ( DataFlow:: MethodCallNode call , string name ) {
78
84
exists ( DataFlow:: Node sup |
79
85
call .calls ( sup , name ) and
80
86
sup = API:: builtin ( "super" ) .getACall ( )
81
87
)
82
88
}
83
89
90
+ /** Holds if `meth` calls `super().<name>` where `name` is the name of the method. */
84
91
predicate callsSuper ( Function meth ) {
85
92
exists ( DataFlow:: MethodCallNode call |
86
93
call .getScope ( ) = meth and
87
94
superCall ( call , meth .getName ( ) )
88
95
)
89
96
}
90
97
98
+ /** Holds if `meth` calls `target.<name>(self, ...)` with the call `call`. */
91
99
predicate callsMethodOnClassWithSelf (
92
100
Function meth , DataFlow:: MethodCallNode call , Class target , string name
93
101
) {
@@ -99,6 +107,7 @@ predicate callsMethodOnClassWithSelf(
99
107
)
100
108
}
101
109
110
+ /** Holds if `meth` calls a method named `name` passing its `self` argument as its first parameter, but the class it refers to is unknown. */
102
111
predicate callsMethodOnUnknownClassWithSelf ( Function meth , string name ) {
103
112
exists ( DataFlow:: MethodCallNode call , DataFlow:: Node callTarget , DataFlow:: ParameterNode self |
104
113
call .calls ( callTarget , name ) and
@@ -108,6 +117,7 @@ predicate callsMethodOnUnknownClassWithSelf(Function meth, string name) {
108
117
)
109
118
}
110
119
120
+ /** Holds if `base` does not call a superclass method `shouldCall` named `name` when it appears it should. */
111
121
predicate missingCallToSuperclassMethod ( Class base , Function shouldCall , string name ) {
112
122
shouldCall .getName ( ) = name and
113
123
shouldCall .getScope ( ) = getADirectSuperclass + ( base ) and
@@ -117,6 +127,9 @@ predicate missingCallToSuperclassMethod(Class base, Function shouldCall, string
117
127
not callsMethodOnUnknownClassWithSelf ( getASuperCallTargetFromClass ( base , base , name ) , name )
118
128
}
119
129
130
+ /** Holds if `base` does not call a superclass method `shouldCall` named `name` when it appears it should.
131
+ * Results are restricted to hold only for the highest `base` class and the lowest `shouldCall` method in the heirarchy for which this applies.
132
+ */
120
133
predicate missingCallToSuperclassMethodRestricted ( Class base , Function shouldCall , string name ) {
121
134
missingCallToSuperclassMethod ( base , shouldCall , name ) and
122
135
not exists ( Class superBase |
@@ -131,6 +144,11 @@ predicate missingCallToSuperclassMethodRestricted(Class base, Function shouldCal
131
144
)
132
145
}
133
146
147
+ /**
148
+ * If `base` contains a `super()` call, gets a method in the inheritence heirarchy of `name` in the MRO of `base`
149
+ * that does not contain a `super()` call, but would call `shouldCall` if it did, which does not otherwise get called
150
+ * during a call to `base.<name>`.
151
+ * */
134
152
Function getPossibleMissingSuper ( Class base , Function shouldCall , string name ) {
135
153
missingCallToSuperclassMethod ( base , shouldCall , name ) and
136
154
exists ( Function baseMethod |
@@ -151,6 +169,7 @@ Function getPossibleMissingSuper(Class base, Function shouldCall, string name) {
151
169
152
170
private module FunctionOption = Option< Function > ;
153
171
172
+ /** An optional `Function`. */
154
173
class FunctionOption extends FunctionOption:: Option {
155
174
/**
156
175
* Holds if this element is at the specified location.
@@ -174,6 +193,7 @@ class FunctionOption extends FunctionOption::Option {
174
193
endcolumn = 0
175
194
}
176
195
196
+ /** Gets the qualified name of this function. */
177
197
string getQualifiedName ( ) {
178
198
result = this .asSome ( ) .getQualifiedName ( )
179
199
or
@@ -182,6 +202,7 @@ class FunctionOption extends FunctionOption::Option {
182
202
}
183
203
}
184
204
205
+ /** Gets the result of `getPossibleMissingSuper`, or None if none exists. */
185
206
bindingset [ name]
186
207
FunctionOption getPossibleMissingSuperOption ( Class base , Function shouldCall , string name ) {
187
208
result .asSome ( ) = getPossibleMissingSuper ( base , shouldCall , name )
0 commit comments