@@ -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
325347import 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`
0 commit comments