mirror of
https://github.com/github/codeql.git
synced 2026-05-01 03:35:13 +02:00
Python: Support unpacking of keyword arguments.
This commit is contained in:
@@ -294,6 +294,12 @@ module ArgumentPassing {
|
||||
n = -2 and
|
||||
result = TKwOverflowNode(call, callable)
|
||||
)
|
||||
or
|
||||
// argument unpacked from dict
|
||||
exists(string name |
|
||||
call_unpacks(call, callable, name, n) and
|
||||
result = TKwUnpacked(call, callable, name)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -320,6 +326,22 @@ module ArgumentPassing {
|
||||
result = call.getArgByName(key)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `call` unpacks a dictionary argument in order to pass it via `name`.
|
||||
* It will then be passed to the `n`th parameter of `callable`.
|
||||
*/
|
||||
predicate call_unpacks(CallNode call, CallableValue callable, string name, int n) {
|
||||
exists(Function f |
|
||||
f = callable.getScope() and
|
||||
not exists(call.getArg(n)) and // no positional arguement available
|
||||
name = f.getArgName(n) and
|
||||
not exists(call.getArgByName(name)) and // no keyword argument available
|
||||
n >= 0 and
|
||||
n < f.getPositionalParameterCount() + f.getKeywordOnlyParameterCount() and
|
||||
exists(call.getNode().getKwargs()) // dict argument available
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
import ArgumentPassing
|
||||
@@ -760,6 +782,8 @@ predicate readStep(Node nodeFrom, Content c, Node nodeTo) {
|
||||
comprehensionReadStep(nodeFrom, c, nodeTo)
|
||||
or
|
||||
attributeReadStep(nodeFrom, c, nodeTo)
|
||||
or
|
||||
kwUnpackReadStep(nodeFrom, c, nodeTo)
|
||||
}
|
||||
|
||||
/** Data flows from a sequence to a subscript of the sequence. */
|
||||
@@ -864,6 +888,19 @@ predicate attributeReadStep(CfgNode nodeFrom, AttributeContent c, CfgNode nodeTo
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `nodeFrom` is a dictionary argument being unpacked and `nodeTo` is the
|
||||
* synthezised unpacked argument with the name indicated by `c`.
|
||||
*/
|
||||
predicate kwUnpackReadStep(CfgNode nodeFrom, DictionaryElementContent c, Node nodeTo) {
|
||||
exists(CallNode call, CallableValue callable, string name, int n |
|
||||
call_unpacks(call, callable, name, n) and
|
||||
nodeFrom.asCfgNode() = call.getNode().getKwargs().getAFlowNode() and
|
||||
nodeTo = TKwUnpacked(call, callable, name) and
|
||||
name = c.getKey()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if values stored inside content `c` are cleared at node `n`. For example,
|
||||
* any value stored inside `f` is cleared at the pre-update node associated with `x`
|
||||
|
||||
@@ -36,6 +36,10 @@ newtype TNode =
|
||||
/** A node representing the overflow keyword arguments to a call. */
|
||||
TKwOverflowNode(CallNode call, CallableValue callable) {
|
||||
exists(getKeywordOverflowArg(call, callable, _))
|
||||
} or
|
||||
/** A node representing an unpacked element of a dictionary argument. */
|
||||
TKwUnpacked(CallNode call, CallableValue callable, string name) {
|
||||
call_unpacks(call, callable, name, _)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -378,7 +378,7 @@ def test_call_unpack_iterable():
|
||||
|
||||
|
||||
def test_call_unpack_mapping():
|
||||
SINK(second(NONSOURCE, **{"b": SOURCE})) # Flow missing
|
||||
SINK(second(NONSOURCE, **{"b": SOURCE}))
|
||||
|
||||
|
||||
def f_extra_pos(a, *b):
|
||||
@@ -474,7 +474,7 @@ def test_lambda_unpack_mapping():
|
||||
def second(a, b):
|
||||
return b
|
||||
|
||||
SINK(second(NONSOURCE, **{"b": SOURCE})) # Flow missing
|
||||
SINK(second(NONSOURCE, **{"b": SOURCE}))
|
||||
|
||||
|
||||
def test_lambda_extra_pos():
|
||||
|
||||
Reference in New Issue
Block a user