@@ -261,7 +261,7 @@ private Node update(Node node) {
261261 * When a call contains an iterable unpacking argument, such as `func(*args)`, it is expanded into positional arguments.
262262 *
263263 * CURRENTLY NOT SUPPORTED:
264- * When a call contains an iterable unpacking argument, such as `func(*args)`, and the callee contains a starred argument, any extra
264+ * If a call contains an iterable unpacking argument, such as `func(*args)`, and the callee contains a starred argument, any extra
265265 * positional arguments are passed to the starred argument.
266266 *
267267 * When a call contains keyword arguments that do not correspond to keyword parameters, these
@@ -313,7 +313,7 @@ private Node update(Node node) {
313313 * `y`. There is a dataflow step from `**{"y": 1, "a": 3}` to `[**d]` to transfer the content and
314314 * a clearing of content at key `y` for node `[**d]`, since that value has been unpacked.
315315 */
316- module ArgumentPassing {
316+ private module ArgumentPassing {
317317 /**
318318 * Holds if `call` represents a `DataFlowCall` to a `DataFlowCallable` represented by `callable`.
319319 *
@@ -354,23 +354,57 @@ module ArgumentPassing {
354354 )
355355 }
356356
357+ /**
358+ * A type representing a mapping from argument indices to parameter indices.
359+ * We currently use two mappings: NoShift, the identity, used for ordinary
360+ * function calls, and ShiftOne which is used for calls where an extra argument
361+ * is inserted. These include method calls, constructor calls and class calls.
362+ * In these calls, the argument at index `n` is mapped to the parameter at position `n+1`.
363+ */
364+ newtype TArgParamMapping =
365+ TNoShift ( ) or
366+ TShiftOne ( )
367+
368+ /** A mapping used for parameter passing. */
369+ abstract class ArgParamMapping extends TArgParamMapping {
370+ bindingset [ result ]
371+ abstract int getArgN ( int paramN ) ;
372+
373+ string toString ( ) { none ( ) }
374+ }
375+
376+ /** A mapping that passes argument `n` to parameter `n`. */
377+ class NoShift extends ArgParamMapping , TNoShift {
378+ NoShift ( ) { this = TNoShift ( ) }
379+
380+ bindingset [ result ]
381+ override int getArgN ( int paramN ) { result = paramN }
382+ }
383+
384+ /** A mapping that passes argument `n` to parameter `n+1`. */
385+ class ShiftOne extends ArgParamMapping , TShiftOne {
386+ ShiftOne ( ) { this = TShiftOne ( ) }
387+
388+ bindingset [ result ]
389+ override int getArgN ( int paramN ) { result = paramN - 1 }
390+ }
391+
357392 /**
358393 * Gets the node representing the argument to `call` that is passed to the parameter at
359394 * (zero-based) index `paramN` in `callable`. If this is a positional argument, it must appear
360- * at index `argN ` in `call`.
395+ * at index `mapping.getArgN(paramN) ` in `call`.
361396 *
362- * `argN ` will differ from `paramN` for method- or constructor calls, where the first parameter
397+ * `mapping.getArgN(paramN) ` will differ from `paramN` for method- or constructor calls, where the first parameter
363398 * is `self` and the first positional argument is passed to the second positional parameter.
364399 * Similarly for classmethod calls, where the first parameter is `cls`.
365400 *
366401 * NOT SUPPORTED: Keyword-only parameters.
367402 */
368- Node getArg ( CallNode call , int argN , CallableValue callable , int paramN ) {
403+ Node getArg ( CallNode call , ArgParamMapping mapping , CallableValue callable , int paramN ) {
369404 connects ( call , callable ) and
370- paramN - argN in [ 0 , 1 ] and // constrain for now to limit the size of the predicate; we only use it to insert one argument (self).
371405 (
372406 // positional argument
373- result = TCfgNode ( call .getArg ( argN ) )
407+ result = TCfgNode ( call .getArg ( mapping . getArgN ( paramN ) ) )
374408 or
375409 // keyword argument
376410 // TODO: Since `getArgName` have no results for keyword-only parameters,
@@ -393,7 +427,7 @@ module ArgumentPassing {
393427 or
394428 // argument unpacked from dict
395429 exists ( string name |
396- call_unpacks ( call , argN , callable , name , paramN ) and
430+ call_unpacks ( call , mapping , callable , name , paramN ) and
397431 result = TKwUnpacked ( call , callable , name )
398432 )
399433 )
@@ -425,20 +459,21 @@ module ArgumentPassing {
425459
426460 /**
427461 * Holds if `call` unpacks a dictionary argument in order to pass it via `name`.
428- * It will then be passed to the `n`th parameter of `callable`.
462+ * It will then be passed to the parameter of `callable` at index `paramN `.
429463 */
430- predicate call_unpacks ( CallNode call , int argNr , CallableValue callable , string name , int n ) {
464+ predicate call_unpacks (
465+ CallNode call , ArgParamMapping mapping , CallableValue callable , string name , int paramN
466+ ) {
431467 connects ( call , callable ) and
432- n - argNr in [ 0 , 1 ] and
433468 exists ( Function f |
434469 f = callable .getScope ( ) and
435- not exists ( call .getArg ( argNr ) ) and // no positional arguement available
436- name = f .getArgName ( n ) and
470+ not exists ( call .getArg ( mapping . getArgN ( paramN ) ) ) and // no positional argument available
471+ name = f .getArgName ( paramN ) and
437472 // not exists(call.getArgByName(name)) and // only matches keyword arguments not preceded by **
438473 // TODO: make the below logic respect control flow splitting (by not going to the AST).
439474 not call .getNode ( ) .getANamedArg ( ) .( Keyword ) .getArg ( ) = name and // no keyword argument available
440- n >= 0 and
441- n < f .getPositionalParameterCount ( ) + f .getKeywordOnlyParameterCount ( ) and
475+ paramN >= 0 and
476+ paramN < f .getPositionalParameterCount ( ) + f .getKeywordOnlyParameterCount ( ) and
442477 exists ( call .getNode ( ) .getKwargs ( ) ) // dict argument available
443478 )
444479 }
@@ -581,7 +616,7 @@ class FunctionCall extends DataFlowCall, TFunctionCall {
581616
582617 override string toString ( ) { result = call .toString ( ) }
583618
584- override Node getArg ( int n ) { result = getArg ( call , n , callable .getCallableValue ( ) , n ) }
619+ override Node getArg ( int n ) { result = getArg ( call , TNoShift ( ) , callable .getCallableValue ( ) , n ) }
585620
586621 override ControlFlowNode getNode ( ) { result = call }
587622
@@ -608,7 +643,7 @@ class MethodCall extends DataFlowCall, TMethodCall {
608643 override string toString ( ) { result = call .toString ( ) }
609644
610645 override Node getArg ( int n ) {
611- n > 0 and result = getArg ( call , n - 1 , this .getCallableValue ( ) , n )
646+ n > 0 and result = getArg ( call , TShiftOne ( ) , this .getCallableValue ( ) , n )
612647 or
613648 n = 0 and result = TCfgNode ( call .getFunction ( ) .( AttrNode ) .getObject ( ) )
614649 }
@@ -640,7 +675,7 @@ class ClassCall extends DataFlowCall, TClassCall {
640675 override string toString ( ) { result = call .toString ( ) }
641676
642677 override Node getArg ( int n ) {
643- n > 0 and result = getArg ( call , n - 1 , this .getCallableValue ( ) , n )
678+ n > 0 and result = getArg ( call , TShiftOne ( ) , this .getCallableValue ( ) , n )
644679 or
645680 n = 0 and result = TSyntheticPreUpdateNode ( TCfgNode ( call ) )
646681 }
0 commit comments