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

Skip to content

Commit 30d048f

Browse files
committed
Python: Support unpacking of keyword arguments.
1 parent e02cfbf commit 30d048f

3 files changed

Lines changed: 43 additions & 2 deletions

File tree

python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,12 @@ module ArgumentPassing {
294294
n = -2 and
295295
result = TKwOverflowNode(call, callable)
296296
)
297+
or
298+
// argument unpacked from dict
299+
exists(string name |
300+
call_unpacks(call, callable, name, n) and
301+
result = TKwUnpacked(call, callable, name)
302+
)
297303
)
298304
}
299305

@@ -320,6 +326,22 @@ module ArgumentPassing {
320326
result = call.getArgByName(key)
321327
)
322328
}
329+
330+
/**
331+
* Holds if `call` unpacks a dictionary argument in order to pass it via `name`.
332+
* It will then be passed to the `n`th parameter of `callable`.
333+
*/
334+
predicate call_unpacks(CallNode call, CallableValue callable, string name, int n) {
335+
exists(Function f |
336+
f = callable.getScope() and
337+
not exists(call.getArg(n)) and // no positional arguement available
338+
name = f.getArgName(n) and
339+
not exists(call.getArgByName(name)) and // no keyword argument available
340+
n >= 0 and
341+
n < f.getPositionalParameterCount() + f.getKeywordOnlyParameterCount() and
342+
exists(call.getNode().getKwargs()) // dict argument available
343+
)
344+
}
323345
}
324346

325347
import ArgumentPassing
@@ -760,6 +782,8 @@ predicate readStep(Node nodeFrom, Content c, Node nodeTo) {
760782
comprehensionReadStep(nodeFrom, c, nodeTo)
761783
or
762784
attributeReadStep(nodeFrom, c, nodeTo)
785+
or
786+
kwUnpackReadStep(nodeFrom, c, nodeTo)
763787
}
764788

765789
/** Data flows from a sequence to a subscript of the sequence. */
@@ -864,6 +888,19 @@ predicate attributeReadStep(CfgNode nodeFrom, AttributeContent c, CfgNode nodeTo
864888
)
865889
}
866890

891+
/**
892+
* Holds if `nodeFrom` is a dictionary argument being unpacked and `nodeTo` is the
893+
* synthezised unpacked argument with the name indicated by `c`.
894+
*/
895+
predicate kwUnpackReadStep(CfgNode nodeFrom, DictionaryElementContent c, Node nodeTo) {
896+
exists(CallNode call, CallableValue callable, string name, int n |
897+
call_unpacks(call, callable, name, n) and
898+
nodeFrom.asCfgNode() = call.getNode().getKwargs().getAFlowNode() and
899+
nodeTo = TKwUnpacked(call, callable, name) and
900+
name = c.getKey()
901+
)
902+
}
903+
867904
/**
868905
* Holds if values stored inside content `c` are cleared at node `n`. For example,
869906
* any value stored inside `f` is cleared at the pre-update node associated with `x`

python/ql/src/experimental/dataflow/internal/DataFlowPublic.qll

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,10 @@ newtype TNode =
3636
/** A node representing the overflow keyword arguments to a call. */
3737
TKwOverflowNode(CallNode call, CallableValue callable) {
3838
exists(getKeywordOverflowArg(call, callable, _))
39+
} or
40+
/** A node representing an unpacked element of a dictionary argument. */
41+
TKwUnpacked(CallNode call, CallableValue callable, string name) {
42+
call_unpacks(call, callable, name, _)
3943
}
4044

4145
/**

python/ql/test/experimental/dataflow/coverage/test.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -378,7 +378,7 @@ def test_call_unpack_iterable():
378378

379379

380380
def test_call_unpack_mapping():
381-
SINK(second(NONSOURCE, **{"b": SOURCE})) # Flow missing
381+
SINK(second(NONSOURCE, **{"b": SOURCE}))
382382

383383

384384
def f_extra_pos(a, *b):
@@ -474,7 +474,7 @@ def test_lambda_unpack_mapping():
474474
def second(a, b):
475475
return b
476476

477-
SINK(second(NONSOURCE, **{"b": SOURCE})) # Flow missing
477+
SINK(second(NONSOURCE, **{"b": SOURCE}))
478478

479479

480480
def test_lambda_extra_pos():

0 commit comments

Comments
 (0)