Skip to content

Commit 1d6b6a4

Browse files
authored
Merge pull request github#2924 from BekaValentine/python-objectapi-to-valueapi-wrongnumberargumentsincall
Python: ObjectAPI to ValueAPI: WrongNumberArgumentsInCall
2 parents 36b2838 + 89752f4 commit 1d6b6a4

File tree

12 files changed

+219
-51
lines changed

12 files changed

+219
-51
lines changed

python/ql/src/Expressions/CallArgs.qll

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -154,14 +154,17 @@ predicate too_few_args(Call call, Value callable, int limit) {
154154
not exists(call.getKwargs()) and
155155
arg_count(call) < limit and
156156
exists(FunctionValue func | func = get_function_or_initializer(callable) |
157-
call = func.getACall().getNode() and
157+
call = func.getAFunctionCall().getNode() and
158158
limit = func.minParameters() and
159-
// The combination of misuse of `mox.Mox().StubOutWithMock()`
160-
// and a bug in mox's implementation of methods results in having to
161-
// pass 1 too few arguments to the mocked function.
159+
/*
160+
* The combination of misuse of `mox.Mox().StubOutWithMock()`
161+
* and a bug in mox's implementation of methods results in having to
162+
* pass 1 too few arguments to the mocked function.
163+
*/
164+
162165
not (useOfMoxInModule(call.getEnclosingModule()) and func.isNormalMethod())
163166
or
164-
call = func.getACall().getNode() and limit = func.minParameters() - 1
167+
call = func.getAMethodCall().getNode() and limit = func.minParameters() - 1
165168
or
166169
callable instanceof ClassValue and
167170
call.getAFlowNode() = get_a_call(callable) and
@@ -198,9 +201,9 @@ predicate too_many_args(Call call, Value callable, int limit) {
198201
not func.getScope().hasVarArg() and
199202
limit >= 0
200203
|
201-
call = func.getACall().getNode() and limit = func.maxParameters()
204+
call = func.getAFunctionCall().getNode() and limit = func.maxParameters()
202205
or
203-
call = func.getACall().getNode() and limit = func.maxParameters() - 1
206+
call = func.getAMethodCall().getNode() and limit = func.maxParameters() - 1
204207
or
205208
callable instanceof ClassValue and
206209
call.getAFlowNode() = get_a_call(callable) and
@@ -254,3 +257,8 @@ predicate overridden_call(FunctionValue func, FunctionValue overriding, Call cal
254257
overriding.overrides(func) and
255258
overriding.getACall().getNode() = call
256259
}
260+
261+
/** Holds if `func` will raise a `NotImplemented` error. */
262+
predicate isAbstract(FunctionValue func) {
263+
func.getARaisedType() = ClassValue::notImplementedError()
264+
}

python/ql/src/Expressions/WrongNumberArgumentsInCall.ql

Lines changed: 13 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -14,22 +14,17 @@
1414
import python
1515
import CallArgs
1616

17-
from Call call, FunctionObject func, string too, string should, int limit
17+
from Call call, FunctionValue func, string too, string should, int limit
1818
where
19-
(
20-
too_many_args_objectapi(call, func, limit) and
21-
too = "too many arguments" and
22-
should = "no more than "
23-
or
24-
too_few_args_objectapi(call, func, limit) and
25-
too = "too few arguments" and
26-
should = "no fewer than "
27-
) and
28-
not func.isAbstract() and
29-
not exists(FunctionObject overridden |
30-
func.overrides(overridden) and correct_args_if_called_as_method_objectapi(call, overridden)
31-
) and
32-
/* The semantics of `__new__` can be a bit subtle, so we simply exclude `__new__` methods */
33-
not func.getName() = "__new__"
34-
select call, "Call to $@ with " + too + "; should be " + should + limit.toString() + ".", func,
35-
func.descriptiveString()
19+
(
20+
too_many_args(call, func, limit) and too = "too many arguments" and should = "no more than "
21+
or
22+
too_few_args(call, func, limit) and too = "too few arguments" and should = "no fewer than "
23+
) and
24+
not isAbstract(func) and
25+
not exists(FunctionValue overridden | func.overrides(overridden) and correct_args_if_called_as_method(call, overridden))
26+
/* The semantics of `__new__` can be a bit subtle, so we simply exclude `__new__` methods */
27+
and not func.getName() = "__new__"
28+
29+
select call, "Call to $@ with " + too + "; should be " + should + limit.toString() + ".", func, func.descriptiveString()
30+

python/ql/src/semmle/python/objects/ObjectAPI.qll

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -608,6 +608,12 @@ abstract class FunctionValue extends CallableValue {
608608
)
609609
}
610610

611+
/** Gets a class that may be raised by this function */
612+
abstract ClassValue getARaisedType();
613+
614+
/** Gets a call-site from where this function is called as a function */
615+
CallNode getAFunctionCall() { result.getFunction().pointsTo() = this }
616+
611617
/** Gets a call-site from where this function is called as a method */
612618
CallNode getAMethodCall() {
613619
exists(BoundMethodObjectInternal bm |
@@ -656,6 +662,8 @@ class PythonFunctionValue extends FunctionValue {
656662
/** Gets a control flow node corresponding to a return statement in this function */
657663
ControlFlowNode getAReturnedNode() { result = this.getScope().getAReturnValueFlowNode() }
658664

665+
override ClassValue getARaisedType() { scope_raises(result, this.getScope()) }
666+
659667
override ClassValue getAnInferredReturnType() {
660668
/* We have to do a special version of this because builtin functions have no
661669
* explicit return nodes that we can query and get the class of.
@@ -676,6 +684,11 @@ class BuiltinFunctionValue extends FunctionValue {
676684

677685
override int maxParameters() { none() }
678686

687+
override ClassValue getARaisedType() {
688+
/* Information is unavailable for C code in general */
689+
none()
690+
}
691+
679692
override ClassValue getAnInferredReturnType() {
680693
/* We have to do a special version of this because builtin functions have no
681694
* explicit return nodes that we can query and get the class of.
@@ -702,6 +715,11 @@ class BuiltinMethodValue extends FunctionValue {
702715

703716
override int maxParameters() { none() }
704717

718+
override ClassValue getARaisedType() {
719+
/* Information is unavailable for C code in general */
720+
none()
721+
}
722+
705723
override ClassValue getAnInferredReturnType() {
706724
result = TBuiltinClassObject(this.(BuiltinMethodObjectInternal).getReturnType())
707725
}
@@ -929,6 +947,9 @@ module ClassValue {
929947
/** Get the `ClassValue` for the `LookupError` class. */
930948
ClassValue lookupError() { result = TBuiltinClassObject(Builtin::builtin("LookupError")) }
931949

950+
/** Get the `ClassValue` for the `IndexError` class. */
951+
ClassValue indexError() { result = TBuiltinClassObject(Builtin::builtin("IndexError")) }
952+
932953
/** Get the `ClassValue` for the `IOError` class. */
933954
ClassValue ioError() { result = TBuiltinClassObject(Builtin::builtin("IOError")) }
934955

@@ -949,4 +970,7 @@ module ClassValue {
949970
ClassValue unicodeDecodeError() {
950971
result = TBuiltinClassObject(Builtin::builtin("UnicodeDecodeError"))
951972
}
973+
974+
/** Get the `ClassValue` for the `SystemExit` class. */
975+
ClassValue systemExit() { result = TBuiltinClassObject(Builtin::builtin("SystemExit")) }
952976
}

0 commit comments

Comments
 (0)