Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 9fa630f

Browse files
Add comments documenting helper predicates, and add call resolve condition to callMatchesSignature to avoid cartesian product
1 parent 318d1cd commit 9fa630f

1 file changed

Lines changed: 18 additions & 0 deletions

File tree

python/ql/src/Functions/SignatureOverriddenMethod.ql

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,18 +17,21 @@ import semmle.python.dataflow.new.DataFlow
1717
import semmle.python.dataflow.new.internal.DataFlowDispatch
1818
import codeql.util.Option
1919

20+
/** Holds if `base` is overriden by `sub` */
2021
predicate overrides(Function base, Function sub) {
2122
base.getName() = sub.getName() and
2223
base.getScope() = getADirectSuperclass+(sub.getScope())
2324
}
2425

26+
/** Constructs a string to pluralize `str` depending on `num`. */
2527
bindingset[num, str]
2628
string plural(int num, string str) {
2729
num = 1 and result = "1 " + str
2830
or
2931
num != 1 and result = num.toString() + " " + str + "s"
3032
}
3133

34+
/** Describes the minimum number of arguments `func` can accept, using "at least" if it may accept more. */
3235
string describeMin(Function func) {
3336
exists(string descr | descr = plural(func.getMinPositionalArguments(), "positional argument") |
3437
if func.getMinPositionalArguments() = func.getMaxPositionalArguments()
@@ -37,6 +40,7 @@ string describeMin(Function func) {
3740
)
3841
}
3942

43+
/** Described the maximum number of arguments `func` can accept, using "at most" if it may accept fewer, and "arbitrarily many" if it has a vararg. */
4044
string describeMax(Function func) {
4145
if func.hasVarArg()
4246
then result = "arbitrarily many positional arguments"
@@ -48,6 +52,7 @@ string describeMax(Function func) {
4852
)
4953
}
5054

55+
/** Describes the minumum number of arguments `func` can accept, without repeating "positional arguments". */
5156
string describeMinShort(Function func) {
5257
exists(string descr | descr = func.getMinPositionalArguments().toString() |
5358
if func.getMinPositionalArguments() = func.getMaxPositionalArguments()
@@ -56,6 +61,7 @@ string describeMinShort(Function func) {
5661
)
5762
}
5863

64+
/** Describes the maximum number of arguments `func` can accept, without repeating "positional arguments". */
5965
string describeMaxShort(Function func) {
6066
if func.hasVarArg()
6167
then result = "arbitrarily many"
@@ -67,6 +73,7 @@ string describeMaxShort(Function func) {
6773
)
6874
}
6975

76+
/** Describe an upper bound on the number of arguments `func` may accept, without specifying "at most". */
7077
string describeMaxBound(Function func) {
7178
if func.hasVarArg()
7279
then result = "arbitrarily many"
@@ -121,6 +128,7 @@ predicate weakSignatureMismatch(Function base, Function sub, string msg) {
121128
)
122129
}
123130

131+
/** Holds if `f` should be ignored for considering signature mismatches. */
124132
predicate ignore(Function f) {
125133
isClassmethod(f)
126134
or
@@ -133,18 +141,21 @@ predicate ignore(Function f) {
133141
)
134142
}
135143

144+
/** Gets a function that `call` may resolve to. */
136145
Function resolveCall(Call call) {
137146
exists(DataFlowCall dfc | call = dfc.getNode().(CallNode).getNode() |
138147
result = viableCallable(dfc).(DataFlowFunction).getScope()
139148
)
140149
}
141150

151+
/** Holds if `call` may resolve to either `base` or `sub`, and `base` is overiden by `sub`. */
142152
predicate callViableForEitherOverride(Function base, Function sub, Call call) {
143153
overrides(base, sub) and
144154
base = resolveCall(call) and
145155
sub = resolveCall(call)
146156
}
147157

158+
/** Holds if either both `base` and `sub` are static methods, or both are not static methods, and `base` is overriden by `sub`. */
148159
predicate matchingStatic(Function base, Function sub) {
149160
overrides(base, sub) and
150161
(
@@ -158,7 +169,9 @@ predicate matchingStatic(Function base, Function sub) {
158169

159170
int extraSelfArg(Function func) { if isStaticmethod(func) then result = 0 else result = 1 }
160171

172+
/** Holds if the call `call` matches the signature for `func`. */
161173
predicate callMatchesSignature(Function func, Call call) {
174+
func = resolveCall(call) and
162175
(
163176
// Each parameter of the function is accounted for in the call
164177
forall(Parameter param, int i | param = func.getArg(i) |
@@ -189,12 +202,14 @@ predicate callMatchesSignature(Function func, Call call) {
189202
)
190203
}
191204

205+
/** Gets a call which matches the signature of `base`, but not of overriden `sub`. */
192206
Call getASignatureMismatchWitness(Function base, Function sub) {
193207
callViableForEitherOverride(base, sub, result) and
194208
callMatchesSignature(base, result) and
195209
not callMatchesSignature(sub, result)
196210
}
197211

212+
/** Choose a 'witnessing' call that matches the signature of `base` but not of overridden `sub`, and is in the file `file`. */
198213
Call chooseASignatureMismatchWitnessInFile(Function base, Function sub, File file) {
199214
result =
200215
min(Call c |
@@ -205,6 +220,7 @@ Call chooseASignatureMismatchWitnessInFile(Function base, Function sub, File fil
205220
)
206221
}
207222

223+
/** Choose a 'witnessing' call that matches the signature of `base` but not of overridden `sub`. */
208224
Call chooseASignatureMismatchWitness(Function base, Function sub) {
209225
exists(getASignatureMismatchWitness(base, sub)) and
210226
(
@@ -242,6 +258,7 @@ where
242258
not ignore(base) and
243259
matchingStatic(base, sub) and
244260
(
261+
// If we have a witness, alert for a 'weak' mismatch, but prefer the message for a 'strong' mismatch if that holds.
245262
call.asSome() = chooseASignatureMismatchWitness(base, sub) and
246263
extraMsg =
247264
" $@ correctly calls the base method, but does not match the signature of the overriding method." and
@@ -252,6 +269,7 @@ where
252269
weakSignatureMismatch(base, sub, msg)
253270
)
254271
or
272+
// With no witness, only alert for 'strong' mismatches.
255273
not exists(getASignatureMismatchWitness(base, sub)) and
256274
call.isNone() and
257275
strongSignatureMismatch(base, sub, msg) and

0 commit comments

Comments
 (0)