diff --git a/python/ql/src/semmle/python/objects/Callables.qll b/python/ql/src/semmle/python/objects/Callables.qll index b915e4bd5c24..4021f04c5100 100644 --- a/python/ql/src/semmle/python/objects/Callables.qll +++ b/python/ql/src/semmle/python/objects/Callables.qll @@ -27,8 +27,10 @@ abstract class CallableObjectInternal extends ObjectInternal { none() } + /** Gets the `n`th parameter node of this callable. */ abstract NameNode getParameter(int n); + /** Gets the `name`d parameter node of this callable. */ abstract NameNode getParameterByName(string name); abstract predicate neverReturns(); @@ -438,16 +440,30 @@ class BoundMethodObjectInternal extends CallableObjectInternal, TBoundMethod { PointsTo::pointsTo(result.getFunction(), ctx, this, _) } - override NameNode getParameter(int n) { result = this.getFunction().getParameter(n + 1) } + /** Gets the parameter node that will be used for `self`. */ + NameNode getSelfParameter() { result = this.getFunction().getParameter(0) } + override NameNode getParameter(int n) { + result = this.getFunction().getParameter(n + 1) and + // don't return the parameter for `self` at `n = -1` + n >= 0 + } + + /** + * Gets the `name`d parameter node of this callable. + * Will not return the parameter node for `self`, instead use `getSelfParameter`. + */ override NameNode getParameterByName(string name) { - result = this.getFunction().getParameterByName(name) + result = this.getFunction().getParameterByName(name) and + not result = this.getSelfParameter() } override predicate neverReturns() { this.getFunction().neverReturns() } override predicate functionAndOffset(CallableObjectInternal function, int offset) { function = this.getFunction() and offset = 1 + or + function = this and offset = 0 } override predicate useOriginAsLegacyObject() { any() } diff --git a/python/ql/src/semmle/python/objects/ObjectAPI.qll b/python/ql/src/semmle/python/objects/ObjectAPI.qll index 65b3326e6026..320135814e15 100644 --- a/python/ql/src/semmle/python/objects/ObjectAPI.qll +++ b/python/ql/src/semmle/python/objects/ObjectAPI.qll @@ -352,7 +352,29 @@ class CallableValue extends Value { result = this.(CallableObjectInternal).getParameterByName(name) } - /** Gets the argument corresponding to the `n'th parameter node of this callable. */ + /** + * Gets the argument in `call` corresponding to the `n`'th positional parameter of this callable. + * + * Use this method instead of `call.getArg(n)` to handle the fact that this function might be used as + * a bound-method, such that argument `n` of the call corresponds to the `n+1` parameter of the callable. + * + * This method also gives results when the argument is passed as a keyword argument in `call`, as long + * as `this` is not a builtin function or a builtin method. + * + * Examples: + * + * - if `this` represents the `PythonFunctionValue` for `def func(a, b):`, and `call` represents + * `func(10, 20)`, then `getArgumentForCall(call, 0)` will give the `ControlFlowNode` for `10`. + * + * - with `call` representing `func(b=20, a=10)`, `getArgumentForCall(call, 0)` will give + * the `ControlFlowNode` for `10`. + * + * - if `this` represents the `PythonFunctionValue` for `def func(self, a, b):`, and `call` + * represents `foo.func(10, 20)`, then `getArgumentForCall(call, 1)` will give the + * `ControlFlowNode` for `10`. + * Note: There will also exist a `BoundMethodValue bm` where `bm.getArgumentForCall(call, 0)` + * will give the `ControlFlowNode` for `10` (notice the shift in index used). + */ cached ControlFlowNode getArgumentForCall(CallNode call, int n) { exists(ObjectInternal called, int offset | @@ -363,7 +385,7 @@ class CallableValue extends Value { or exists(string name | call.getArgByName(name) = result and - this.(PythonFunctionObjectInternal).getScope().getArg(n + offset).getName() = name + this.getParameter(n).getId() = name ) or called instanceof BoundMethodObjectInternal and @@ -373,21 +395,37 @@ class CallableValue extends Value { ) } - /** Gets the argument corresponding to the `name`d parameter node of this callable. */ + /** + * Gets the argument in `call` corresponding to the `name`d keyword parameter of this callable. + * + * This method also gives results when the argument is passed as a positional argument in `call`, as long + * as `this` is not a builtin function or a builtin method. + * + * Examples: + * + * - if `this` represents the `PythonFunctionValue` for `def func(a, b):`, and `call` represents + * `func(10, 20)`, then `getNamedArgumentForCall(call, "a")` will give the `ControlFlowNode` for `10`. + * + * - with `call` representing `func(b=20, a=10)`, `getNamedArgumentForCall(call, "a")` will give + * the `ControlFlowNode` for `10`. + * + * - if `this` represents the `PythonFunctionValue` for `def func(self, a, b):`, and `call` + * represents `foo.func(10, 20)`, then `getNamedArgumentForCall(call, "a")` will give the + * `ControlFlowNode` for `10`. + */ cached ControlFlowNode getNamedArgumentForCall(CallNode call, string name) { exists(CallableObjectInternal called, int offset | PointsToInternal::pointsTo(call.getFunction(), _, called, _) and called.functionAndOffset(this, offset) | + call.getArgByName(name) = result + or exists(int n | call.getArg(n) = result and - this.(PythonFunctionObjectInternal).getScope().getArg(n + offset).getName() = name + this.getParameter(n + offset).getId() = name ) or - call.getArgByName(name) = result and - exists(this.(PythonFunctionObjectInternal).getScope().getArgByName(name)) - or called instanceof BoundMethodObjectInternal and offset = 1 and name = "self" and @@ -396,6 +434,29 @@ class CallableValue extends Value { } } +/** + * Class representing bound-methods, such as `o.func`, where `o` is an instance + * of a class that has a callable attribute `func`. + */ +class BoundMethodValue extends CallableValue { + BoundMethodValue() { this instanceof BoundMethodObjectInternal } + + /** + * Gets the callable that will be used when `this` is called. + * The actual callable for `func` in `o.func`. + */ + CallableValue getFunction() { result = this.(BoundMethodObjectInternal).getFunction() } + + /** + * Gets the value that will be used for the `self` parameter when `this` is called. + * The value for `o` in `o.func`. + */ + Value getSelf() { result = this.(BoundMethodObjectInternal).getSelf() } + + /** Gets the parameter node that will be used for `self`. */ + NameNode getSelfParameter() { result = this.(BoundMethodObjectInternal).getSelfParameter() } +} + /** * Class representing classes in the Python program, both Python and built-in. */ @@ -663,11 +724,13 @@ class PythonFunctionValue extends FunctionValue { ControlFlowNode getAReturnedNode() { result = this.getScope().getAReturnValueFlowNode() } override ClassValue getARaisedType() { scope_raises(result, this.getScope()) } - + override ClassValue getAnInferredReturnType() { - /* We have to do a special version of this because builtin functions have no + /* + * We have to do a special version of this because builtin functions have no * explicit return nodes that we can query and get the class of. */ + result = this.getAReturnedNode().pointsTo().getClass() } } @@ -690,9 +753,11 @@ class BuiltinFunctionValue extends FunctionValue { } override ClassValue getAnInferredReturnType() { - /* We have to do a special version of this because builtin functions have no + /* + * We have to do a special version of this because builtin functions have no * explicit return nodes that we can query and get the class of. */ + result = TBuiltinClassObject(this.(BuiltinFunctionObjectInternal).getReturnType()) } } @@ -719,7 +784,7 @@ class BuiltinMethodValue extends FunctionValue { /* Information is unavailable for C code in general */ none() } - + override ClassValue getAnInferredReturnType() { result = TBuiltinClassObject(this.(BuiltinMethodObjectInternal).getReturnType()) } diff --git a/python/ql/test/library-tests/PointsTo/calls/Argument.expected b/python/ql/test/library-tests/PointsTo/calls/Argument.expected deleted file mode 100644 index dbc7e586f472..000000000000 --- a/python/ql/test/library-tests/PointsTo/calls/Argument.expected +++ /dev/null @@ -1,15 +0,0 @@ -| 19 | 0 | ControlFlowNode for w | Function f | -| 19 | 1 | ControlFlowNode for x | Function f | -| 19 | 2 | ControlFlowNode for y | Function f | -| 21 | 0 | ControlFlowNode for y | Function f | -| 21 | 1 | ControlFlowNode for w | Function f | -| 21 | 2 | ControlFlowNode for z | Function f | -| 23 | 0 | ControlFlowNode for c | Function f | -| 23 | 1 | ControlFlowNode for w | Function f | -| 23 | 2 | ControlFlowNode for z | Function f | -| 24 | 0 | ControlFlowNode for c | Function n | -| 24 | 1 | ControlFlowNode for x | Function n | -| 25 | 0 | ControlFlowNode for y | Function n | -| 25 | 1 | ControlFlowNode for z | Function n | -| 33 | 0 | ControlFlowNode for IntegerLiteral | Function foo | -| 34 | 0 | ControlFlowNode for IntegerLiteral | Function foo | diff --git a/python/ql/test/library-tests/PointsTo/calls/Argument.ql b/python/ql/test/library-tests/PointsTo/calls/Argument.ql deleted file mode 100644 index 1678c02c182b..000000000000 --- a/python/ql/test/library-tests/PointsTo/calls/Argument.ql +++ /dev/null @@ -1,5 +0,0 @@ -import python - -from ControlFlowNode arg, FunctionObject func, int i -where arg = func.getArgumentForCall(_, i) -select arg.getLocation().getStartLine(), i, arg.toString(), func.toString() diff --git a/python/ql/test/library-tests/PointsTo/calls/Call.expected b/python/ql/test/library-tests/PointsTo/calls/Call.expected deleted file mode 100644 index 9e9c5646d893..000000000000 --- a/python/ql/test/library-tests/PointsTo/calls/Call.expected +++ /dev/null @@ -1,7 +0,0 @@ -| 19 | ControlFlowNode for f() | Function f | -| 21 | ControlFlowNode for f() | Function f | -| 23 | ControlFlowNode for Attribute() | Function f | -| 24 | ControlFlowNode for Attribute() | Function n | -| 25 | ControlFlowNode for Attribute() | Function n | -| 33 | ControlFlowNode for Attribute() | Function foo | -| 34 | ControlFlowNode for Attribute() | Function foo | diff --git a/python/ql/test/library-tests/PointsTo/calls/CallPointsTo.expected b/python/ql/test/library-tests/PointsTo/calls/CallPointsTo.expected new file mode 100644 index 000000000000..05c4d41406e1 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/calls/CallPointsTo.expected @@ -0,0 +1,19 @@ +| 19 | ControlFlowNode for f() | Function f | +| 21 | ControlFlowNode for f() | Function f | +| 22 | ControlFlowNode for C() | class C | +| 23 | ControlFlowNode for Attribute() | Method(Function f, C()) | +| 24 | ControlFlowNode for Attribute() | Method(Function C.n, C()) | +| 25 | ControlFlowNode for Attribute() | Function C.n | +| 29 | ControlFlowNode for staticmethod() | builtin-class staticmethod | +| 33 | ControlFlowNode for Attribute() | Function D.foo | +| 34 | ControlFlowNode for Attribute() | Function D.foo | +| 34 | ControlFlowNode for D() | class D | +| 37 | ControlFlowNode for Attribute() | Method(builtin method append, List) | +| 38 | ControlFlowNode for len() | Builtin-function len | +| 40 | ControlFlowNode for f() | Function f | +| 41 | ControlFlowNode for C() | class C | +| 42 | ControlFlowNode for Attribute() | Method(Function C.n, C()) | +| 45 | ControlFlowNode for open() | Builtin-function open | +| 46 | ControlFlowNode for open() | Builtin-function open | +| 51 | ControlFlowNode for foo() | Function foo | +| 55 | ControlFlowNode for bar() | Function bar | diff --git a/python/ql/test/library-tests/PointsTo/calls/CallPointsTo.ql b/python/ql/test/library-tests/PointsTo/calls/CallPointsTo.ql new file mode 100644 index 000000000000..10247a98f941 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/calls/CallPointsTo.ql @@ -0,0 +1,5 @@ +import python + +from CallNode call, Value func +where call.getFunction().pointsTo(func) +select call.getLocation().getStartLine(), call.toString(), func.toString() diff --git a/python/ql/test/library-tests/PointsTo/calls/GetACall.expected b/python/ql/test/library-tests/PointsTo/calls/GetACall.expected new file mode 100644 index 000000000000..c9b7822b2784 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/calls/GetACall.expected @@ -0,0 +1,23 @@ +| 19 | ControlFlowNode for f() | Function f | +| 21 | ControlFlowNode for f() | Function f | +| 22 | ControlFlowNode for C() | class C | +| 23 | ControlFlowNode for Attribute() | Function f | +| 23 | ControlFlowNode for Attribute() | Method(Function f, C()) | +| 24 | ControlFlowNode for Attribute() | Function C.n | +| 24 | ControlFlowNode for Attribute() | Method(Function C.n, C()) | +| 25 | ControlFlowNode for Attribute() | Function C.n | +| 29 | ControlFlowNode for staticmethod() | builtin-class staticmethod | +| 33 | ControlFlowNode for Attribute() | Function D.foo | +| 34 | ControlFlowNode for Attribute() | Function D.foo | +| 34 | ControlFlowNode for D() | class D | +| 37 | ControlFlowNode for Attribute() | Method(builtin method append, List) | +| 37 | ControlFlowNode for Attribute() | builtin method append | +| 38 | ControlFlowNode for len() | Builtin-function len | +| 40 | ControlFlowNode for f() | Function f | +| 41 | ControlFlowNode for C() | class C | +| 42 | ControlFlowNode for Attribute() | Function C.n | +| 42 | ControlFlowNode for Attribute() | Method(Function C.n, C()) | +| 45 | ControlFlowNode for open() | Builtin-function open | +| 46 | ControlFlowNode for open() | Builtin-function open | +| 51 | ControlFlowNode for foo() | Function foo | +| 55 | ControlFlowNode for bar() | Function bar | diff --git a/python/ql/test/library-tests/PointsTo/calls/Call.ql b/python/ql/test/library-tests/PointsTo/calls/GetACall.ql similarity index 71% rename from python/ql/test/library-tests/PointsTo/calls/Call.ql rename to python/ql/test/library-tests/PointsTo/calls/GetACall.ql index 94c4212cc64a..84f2ab4fb4ae 100644 --- a/python/ql/test/library-tests/PointsTo/calls/Call.ql +++ b/python/ql/test/library-tests/PointsTo/calls/GetACall.ql @@ -1,5 +1,5 @@ import python -from ControlFlowNode call, FunctionObject func +from ControlFlowNode call, Value func where call = func.getACall() select call.getLocation().getStartLine(), call.toString(), func.toString() diff --git a/python/ql/test/library-tests/PointsTo/calls/getArgumentForCall.expected b/python/ql/test/library-tests/PointsTo/calls/getArgumentForCall.expected new file mode 100644 index 000000000000..c834c72049fd --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/calls/getArgumentForCall.expected @@ -0,0 +1,34 @@ +| 19 | ControlFlowNode for f() | Function f | 0 | ControlFlowNode for w | +| 19 | ControlFlowNode for f() | Function f | 1 | ControlFlowNode for x | +| 19 | ControlFlowNode for f() | Function f | 2 | ControlFlowNode for y | +| 21 | ControlFlowNode for f() | Function f | 0 | ControlFlowNode for y | +| 21 | ControlFlowNode for f() | Function f | 1 | ControlFlowNode for w | +| 21 | ControlFlowNode for f() | Function f | 2 | ControlFlowNode for z | +| 23 | ControlFlowNode for Attribute() | Function f | 0 | ControlFlowNode for c | +| 23 | ControlFlowNode for Attribute() | Function f | 1 | ControlFlowNode for w | +| 23 | ControlFlowNode for Attribute() | Function f | 2 | ControlFlowNode for z | +| 23 | ControlFlowNode for Attribute() | Method(Function f, C()) | 0 | ControlFlowNode for w | +| 23 | ControlFlowNode for Attribute() | Method(Function f, C()) | 1 | ControlFlowNode for z | +| 24 | ControlFlowNode for Attribute() | Function C.n | 0 | ControlFlowNode for c | +| 24 | ControlFlowNode for Attribute() | Function C.n | 1 | ControlFlowNode for x | +| 24 | ControlFlowNode for Attribute() | Method(Function C.n, C()) | 0 | ControlFlowNode for x | +| 25 | ControlFlowNode for Attribute() | Function C.n | 0 | ControlFlowNode for y | +| 25 | ControlFlowNode for Attribute() | Function C.n | 1 | ControlFlowNode for z | +| 33 | ControlFlowNode for Attribute() | Function D.foo | 0 | ControlFlowNode for IntegerLiteral | +| 34 | ControlFlowNode for Attribute() | Function D.foo | 0 | ControlFlowNode for IntegerLiteral | +| 37 | ControlFlowNode for Attribute() | Method(builtin method append, List) | 0 | ControlFlowNode for IntegerLiteral | +| 37 | ControlFlowNode for Attribute() | builtin method append | 0 | ControlFlowNode for l | +| 37 | ControlFlowNode for Attribute() | builtin method append | 1 | ControlFlowNode for IntegerLiteral | +| 38 | ControlFlowNode for len() | Builtin-function len | 0 | ControlFlowNode for l | +| 40 | ControlFlowNode for f() | Function f | 0 | ControlFlowNode for IntegerLiteral | +| 40 | ControlFlowNode for f() | Function f | 1 | ControlFlowNode for IntegerLiteral | +| 40 | ControlFlowNode for f() | Function f | 2 | ControlFlowNode for IntegerLiteral | +| 42 | ControlFlowNode for Attribute() | Function C.n | 0 | ControlFlowNode for c | +| 42 | ControlFlowNode for Attribute() | Function C.n | 1 | ControlFlowNode for IntegerLiteral | +| 42 | ControlFlowNode for Attribute() | Method(Function C.n, C()) | 0 | ControlFlowNode for IntegerLiteral | +| 45 | ControlFlowNode for open() | Builtin-function open | 0 | ControlFlowNode for Str | +| 45 | ControlFlowNode for open() | Builtin-function open | 1 | ControlFlowNode for Str | +| 51 | ControlFlowNode for foo() | Function foo | 0 | ControlFlowNode for IntegerLiteral | +| 51 | ControlFlowNode for foo() | Function foo | 1 | ControlFlowNode for IntegerLiteral | +| 51 | ControlFlowNode for foo() | Function foo | 2 | ControlFlowNode for IntegerLiteral | +| 55 | ControlFlowNode for bar() | Function bar | 0 | ControlFlowNode for IntegerLiteral | diff --git a/python/ql/test/library-tests/PointsTo/calls/getArgumentForCall.ql b/python/ql/test/library-tests/PointsTo/calls/getArgumentForCall.ql new file mode 100644 index 000000000000..de13f0504e85 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/calls/getArgumentForCall.ql @@ -0,0 +1,5 @@ +import python + +from CallNode call, CallableValue callable, int i +select call.getLocation().getStartLine(), call.toString(), callable.toString(), i, + callable.getArgumentForCall(call, i).toString() diff --git a/python/ql/test/library-tests/PointsTo/calls/getNamedArgumentForCall.expected b/python/ql/test/library-tests/PointsTo/calls/getNamedArgumentForCall.expected new file mode 100644 index 000000000000..3df8b4336bf0 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/calls/getNamedArgumentForCall.expected @@ -0,0 +1,31 @@ +| 19 | ControlFlowNode for f() | Function f | arg0 | ControlFlowNode for w | +| 19 | ControlFlowNode for f() | Function f | arg1 | ControlFlowNode for x | +| 19 | ControlFlowNode for f() | Function f | arg2 | ControlFlowNode for y | +| 21 | ControlFlowNode for f() | Function f | arg0 | ControlFlowNode for y | +| 21 | ControlFlowNode for f() | Function f | arg1 | ControlFlowNode for w | +| 21 | ControlFlowNode for f() | Function f | arg2 | ControlFlowNode for z | +| 23 | ControlFlowNode for Attribute() | Function f | arg1 | ControlFlowNode for w | +| 23 | ControlFlowNode for Attribute() | Function f | arg2 | ControlFlowNode for z | +| 23 | ControlFlowNode for Attribute() | Function f | self | ControlFlowNode for c | +| 23 | ControlFlowNode for Attribute() | Method(Function f, C()) | arg1 | ControlFlowNode for w | +| 23 | ControlFlowNode for Attribute() | Method(Function f, C()) | arg2 | ControlFlowNode for z | +| 24 | ControlFlowNode for Attribute() | Function C.n | arg1 | ControlFlowNode for x | +| 24 | ControlFlowNode for Attribute() | Function C.n | self | ControlFlowNode for c | +| 24 | ControlFlowNode for Attribute() | Method(Function C.n, C()) | arg1 | ControlFlowNode for x | +| 25 | ControlFlowNode for Attribute() | Function C.n | arg1 | ControlFlowNode for z | +| 25 | ControlFlowNode for Attribute() | Function C.n | self | ControlFlowNode for y | +| 33 | ControlFlowNode for Attribute() | Function D.foo | arg | ControlFlowNode for IntegerLiteral | +| 34 | ControlFlowNode for Attribute() | Function D.foo | arg | ControlFlowNode for IntegerLiteral | +| 37 | ControlFlowNode for Attribute() | builtin method append | self | ControlFlowNode for l | +| 40 | ControlFlowNode for f() | Function f | arg0 | ControlFlowNode for IntegerLiteral | +| 40 | ControlFlowNode for f() | Function f | arg1 | ControlFlowNode for IntegerLiteral | +| 40 | ControlFlowNode for f() | Function f | arg2 | ControlFlowNode for IntegerLiteral | +| 42 | ControlFlowNode for Attribute() | Function C.n | arg1 | ControlFlowNode for IntegerLiteral | +| 42 | ControlFlowNode for Attribute() | Function C.n | self | ControlFlowNode for c | +| 42 | ControlFlowNode for Attribute() | Method(Function C.n, C()) | arg1 | ControlFlowNode for IntegerLiteral | +| 46 | ControlFlowNode for open() | Builtin-function open | file | ControlFlowNode for Str | +| 46 | ControlFlowNode for open() | Builtin-function open | mode | ControlFlowNode for Str | +| 51 | ControlFlowNode for foo() | Function foo | a | ControlFlowNode for IntegerLiteral | +| 55 | ControlFlowNode for bar() | Function bar | a | ControlFlowNode for IntegerLiteral | +| 55 | ControlFlowNode for bar() | Function bar | b | ControlFlowNode for IntegerLiteral | +| 55 | ControlFlowNode for bar() | Function bar | c | ControlFlowNode for IntegerLiteral | diff --git a/python/ql/test/library-tests/PointsTo/calls/getNamedArgumentForCall.ql b/python/ql/test/library-tests/PointsTo/calls/getNamedArgumentForCall.ql new file mode 100644 index 000000000000..c531a9ab57a2 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/calls/getNamedArgumentForCall.ql @@ -0,0 +1,5 @@ +import python + +from CallNode call, CallableValue callable, string name +select call.getLocation().getStartLine(), call.toString(), callable.toString(), name, + callable.getNamedArgumentForCall(call, name).toString() diff --git a/python/ql/test/library-tests/PointsTo/calls/getParameter.expected b/python/ql/test/library-tests/PointsTo/calls/getParameter.expected new file mode 100644 index 000000000000..3f384d387861 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/calls/getParameter.expected @@ -0,0 +1,12 @@ +| Function C.n | 0 | ControlFlowNode for self | +| Function C.n | 1 | ControlFlowNode for arg1 | +| Function D.foo | 0 | ControlFlowNode for arg | +| Function bar | 0 | ControlFlowNode for a | +| Function f | 0 | ControlFlowNode for arg0 | +| Function f | 1 | ControlFlowNode for arg1 | +| Function f | 2 | ControlFlowNode for arg2 | +| Function foo | 0 | ControlFlowNode for a | +| Method(Function C.n, C()) | 0 | ControlFlowNode for arg1 | +| Method(Function C.n, class C) | 0 | ControlFlowNode for arg1 | +| Method(Function f, C()) | 0 | ControlFlowNode for arg1 | +| Method(Function f, C()) | 1 | ControlFlowNode for arg2 | diff --git a/python/ql/test/library-tests/PointsTo/calls/getParameter.ql b/python/ql/test/library-tests/PointsTo/calls/getParameter.ql new file mode 100644 index 000000000000..07f12cce36fa --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/calls/getParameter.ql @@ -0,0 +1,4 @@ +import python + +from CallableValue callable, int i +select callable.toString(), i, callable.getParameter(i).toString() diff --git a/python/ql/test/library-tests/PointsTo/calls/getParameterByName.expected b/python/ql/test/library-tests/PointsTo/calls/getParameterByName.expected new file mode 100644 index 000000000000..da61f6296a78 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/calls/getParameterByName.expected @@ -0,0 +1,12 @@ +| Function C.n | arg1 | ControlFlowNode for arg1 | +| Function C.n | self | ControlFlowNode for self | +| Function D.foo | arg | ControlFlowNode for arg | +| Function bar | a | ControlFlowNode for a | +| Function f | arg0 | ControlFlowNode for arg0 | +| Function f | arg1 | ControlFlowNode for arg1 | +| Function f | arg2 | ControlFlowNode for arg2 | +| Function foo | a | ControlFlowNode for a | +| Method(Function C.n, C()) | arg1 | ControlFlowNode for arg1 | +| Method(Function C.n, class C) | arg1 | ControlFlowNode for arg1 | +| Method(Function f, C()) | arg1 | ControlFlowNode for arg1 | +| Method(Function f, C()) | arg2 | ControlFlowNode for arg2 | diff --git a/python/ql/test/library-tests/PointsTo/calls/getParameterByName.ql b/python/ql/test/library-tests/PointsTo/calls/getParameterByName.ql new file mode 100644 index 000000000000..d4766b680f73 --- /dev/null +++ b/python/ql/test/library-tests/PointsTo/calls/getParameterByName.ql @@ -0,0 +1,4 @@ +import python + +from CallableValue callable, string name +select callable.toString(), name, callable.getParameterByName(name).toString() diff --git a/python/ql/test/library-tests/PointsTo/calls/test.py b/python/ql/test/library-tests/PointsTo/calls/test.py index 38667a4a6e13..449f7fe49fc5 100644 --- a/python/ql/test/library-tests/PointsTo/calls/test.py +++ b/python/ql/test/library-tests/PointsTo/calls/test.py @@ -32,3 +32,24 @@ def foo(arg): D.foo(1) D().foo(2) + +l = [1,2,3] +l.append(4) +len(l) + +f(arg0=0, arg1=1, arg2=2) +c = C() +c.n(arg1=1) + +# positional/keyword arguments for a builtin function +open("foo.txt", "rb") # TODO: Not handled by getNamedArgumentForCall +open(file="foo.txt", mode="rb") + +# Testing how arguments to *args and **kwargs are handled +def foo(a, *args): + pass +foo(1, 2, 3) + +def bar(a, **kwargs): + pass +bar(a=1, b=2, c=3)