@@ -448,15 +448,14 @@ private module Cached {
448448 TSynthHashSplatParameterNode ( DataFlowCallable c ) {
449449 isParameterNode ( _, c , any ( ParameterPosition p | p .isKeyword ( _) ) )
450450 } or
451- TSynthSplatParameterNode ( DataFlowCallable c , int n ) {
451+ TSynthSplatParameterNode ( DataFlowCallable c ) {
452452 exists ( c .asCallable ( ) ) and // exclude library callables
453- isParameterNode ( _, c , any ( ParameterPosition p | p .isPositional ( _) ) ) and
453+ isParameterNode ( _, c , any ( ParameterPosition p | p .isPositional ( _) ) )
454454 // `n` is the position of the splat argument that is matched to this node
455- exists ( ArgumentPosition pos | pos .isSplat ( n ) )
456455 } or
457456 TSynthSplatArgParameterNode ( DataFlowCallable c ) {
458- exists ( c .asCallable ( ) ) and // exclude library callables
459- isParameterNode ( _, c , any ( ParameterPosition p | p .isSplat ( _) ) )
457+ exists ( c .asCallable ( ) ) // exclude library callables
458+ // and isParameterNode(_, c, any(ParameterPosition p | p.isSplat(_)))
460459 } or
461460 TSynthSplatParameterElementNode ( DataFlowCallable c , int n ) {
462461 exists ( c .asCallable ( ) ) and // exclude library callables
@@ -478,15 +477,17 @@ private module Cached {
478477 or
479478 c .getAnArgument ( ) instanceof CfgNodes:: ExprNodes:: PairCfgNode
480479 } or
481- TSynthSplatArgumentNode ( CfgNodes:: ExprNodes:: CallCfgNode c ) {
482- exists ( Argument arg , ArgumentPosition pos | pos .isPositional ( _) | arg .isArgumentOf ( c , pos ) ) and
483- not exists ( Argument arg , ArgumentPosition pos | pos .isSplat ( _) | arg .isArgumentOf ( c , pos ) )
480+ TSynthSplatArgumentNode ( CfgNodes:: ExprNodes:: CallCfgNode c ) or
481+ TSynthSplatArgumentElementNode ( CfgNodes:: ExprNodes:: CallCfgNode c , int n ) {
482+ n in [ 0 .. 10 ] and
483+ exists ( Argument arg , ArgumentPosition pos | pos .isSplat ( _) and arg .isArgumentOf ( c , pos ) )
484484 } or
485485 TCaptureNode ( VariableCapture:: Flow:: SynthesizedCaptureNode cn )
486486
487487 class TSourceParameterNode =
488488 TNormalParameterNode or TBlockParameterNode or TSelfParameterNode or
489- TSynthHashSplatParameterNode or TSynthSplatParameterNode or TSynthSplatArgParameterNode ;
489+ TSynthHashSplatParameterNode or TSynthSplatParameterNode or TSynthSplatArgParameterNode or
490+ TSynthSplatArgumentElementNode ;
490491
491492 cached
492493 Location getLocation ( NodeImpl n ) { result = n .getLocationImpl ( ) }
@@ -1037,7 +1038,7 @@ private module ParameterNodes {
10371038 *
10381039 * This node stores the index of the splat argument it is matched to, which allows us to shift
10391040 * indices correctly when adding read steps. Without this, in the example above we would erroneously
1040- * get a read step to `x` and index 0 and `y` and index 1 etc.
1041+ * get a read step to `x` at index 0 and `y` at index 1 etc.
10411042 *
10421043 * We don't yet correctly handle cases where a positional argument follows the splat argument, e.g. in
10431044 * ```rb
@@ -1046,10 +1047,8 @@ private module ParameterNodes {
10461047 */
10471048 class SynthSplatParameterNode extends ParameterNodeImpl , TSynthSplatParameterNode {
10481049 private DataFlowCallable callable ;
1049- // The position of the splat argument that is matched to this node
1050- private int n ;
10511050
1052- SynthSplatParameterNode ( ) { this = TSynthSplatParameterNode ( callable , n ) }
1051+ SynthSplatParameterNode ( ) { this = TSynthSplatParameterNode ( callable ) }
10531052
10541053 /**
10551054 * Gets a parameter which will contain the value given by `c`.
@@ -1064,10 +1063,10 @@ private module ParameterNodes {
10641063 * then `getAParameter(element 0) = y`.
10651064 */
10661065 ParameterNode getAParameter ( ContentSet c ) {
1067- exists ( int m |
1068- isParameterNode ( result , callable , ( any ( ParameterPosition p | p .isPositional ( m ) ) ) ) and
1066+ exists ( int n |
1067+ isParameterNode ( result , callable , ( any ( ParameterPosition p | p .isPositional ( n ) ) ) ) and
10691068 (
1070- c = getPositionalContent ( m - n )
1069+ c = getPositionalContent ( n )
10711070 or
10721071 c .isSingleton ( TUnknownElementContent ( ) )
10731072 )
@@ -1077,7 +1076,7 @@ private module ParameterNodes {
10771076 final override Parameter getParameter ( ) { none ( ) }
10781077
10791078 final override predicate isParameterOf ( DataFlowCallable c , ParameterPosition pos ) {
1080- c = callable and pos .isSynthSplat ( n )
1079+ c = callable and pos .isSynthSplat ( )
10811080 }
10821081
10831082 final override CfgScope getCfgScope ( ) { result = callable .asCallable ( ) }
@@ -1132,12 +1131,7 @@ private module ParameterNodes {
11321131
11331132 int getStorePosition ( ) { result = pos }
11341133
1135- int getReadPosition ( ) {
1136- exists ( int splatPos |
1137- exists ( this .getSplatParameterNode ( splatPos ) ) and
1138- result = pos + splatPos
1139- )
1140- }
1134+ int getReadPosition ( ) { result = pos }
11411135
11421136 final override CfgScope getCfgScope ( ) { result = callable .asCallable ( ) }
11431137
@@ -1322,6 +1316,23 @@ module ArgumentNodes {
13221316
13231317 override string toStringImpl ( ) { result = "*" }
13241318 }
1319+
1320+ class SynthSplatArgumentElementNode extends NodeImpl , TSynthSplatArgumentElementNode {
1321+ CfgNodes:: ExprNodes:: CallCfgNode c ;
1322+ int n ;
1323+
1324+ SynthSplatArgumentElementNode ( ) { this = TSynthSplatArgumentElementNode ( c , n ) }
1325+
1326+ CfgNodes:: ExprNodes:: CallCfgNode getCall ( ) { result = c }
1327+
1328+ int getPosition ( ) { result = n }
1329+
1330+ override CfgScope getCfgScope ( ) { result = c .getExpr ( ) .getCfgScope ( ) }
1331+
1332+ override Location getLocationImpl ( ) { result = c .getLocation ( ) }
1333+
1334+ override string toStringImpl ( ) { result = "*[" + n + "]" }
1335+ }
13251336}
13261337
13271338import ArgumentNodes
@@ -1592,11 +1603,19 @@ predicate storeStep(Node node1, ContentSet c, Node node2) {
15921603 FlowSummaryImpl:: Private:: Steps:: summaryStoreStep ( node1 .( FlowSummaryNode ) .getSummaryNode ( ) , c ,
15931604 node2 .( FlowSummaryNode ) .getSummaryNode ( ) )
15941605 or
1595- node1 =
1596- any ( SynthSplatParameterElementNode elemNode |
1597- node2 = elemNode .getSplatParameterNode ( _) and
1598- c = getPositionalContent ( elemNode .getStorePosition ( ) )
1599- )
1606+ exists ( SynthSplatParameterElementNode elemNode , int splatPos |
1607+ node1 = elemNode and
1608+ node2 = elemNode .getSplatParameterNode ( splatPos ) and
1609+ c = getPositionalContent ( elemNode .getStorePosition ( ) - splatPos )
1610+ )
1611+ or
1612+ // Store from TSynthSplatArgumentElementNode(n)
1613+ // into TSynthSplatArgumentNode[n]
1614+ exists ( CfgNodes:: ExprNodes:: CallCfgNode call , int n |
1615+ node1 = TSynthSplatArgumentElementNode ( call , n ) and
1616+ node2 = TSynthSplatArgumentNode ( call ) and
1617+ c = getPositionalContent ( n )
1618+ )
16001619 or
16011620 storeStepCommon ( node1 , c , node2 )
16021621 or
@@ -1611,6 +1630,38 @@ predicate readStepCommon(Node node1, ContentSet c, Node node2) {
16111630 node2 = node1 .( SynthHashSplatParameterNode ) .getAKeywordParameter ( c )
16121631 or
16131632 node2 = node1 .( SynthSplatParameterNode ) .getAParameter ( c )
1633+ or
1634+ // TODO: convert into the above form
1635+ synthSplatArgumentElementReadStep ( node1 , c , node2 )
1636+ or
1637+ // read from SynthSplatArgParameterNode[n] to nth positional parameter
1638+ exists ( SynthSplatArgParameterNode argParamNode , NormalParameterNode posNode , int n |
1639+ argParamNode = node1 and
1640+ posNode = node2 and
1641+ posNode
1642+ .isParameterOf ( argParamNode .getEnclosingCallable ( ) ,
1643+ any ( ParameterPosition p | p .isPositional ( n ) ) ) and
1644+ c = getPositionalContent ( n )
1645+ )
1646+ }
1647+
1648+ // read from splat arg to synth splat arg element
1649+ predicate synthSplatArgumentElementReadStep (
1650+ Node node1 , ContentSet c , SynthSplatArgumentElementNode node2
1651+ ) {
1652+ exists ( int n , int splatPos , CfgNodes:: ExprNodes:: CallCfgNode call |
1653+ // Don't propagate taint on the `self` element of the splat
1654+ // since that won't (probably) won't reach the parameters of the callable.
1655+ // This saves a node per call.
1656+ n >= 0 and
1657+ node1 .asExpr ( ) .( Argument ) .isArgumentOf ( call , any ( ArgumentPosition p | p .isSplat ( splatPos ) ) ) and
1658+ node2 .getCall ( ) = call and
1659+ node2 .getPosition ( ) = n + splatPos and
1660+ (
1661+ c = getPositionalContent ( n ) or
1662+ c .isSingleton ( TUnknownElementContent ( ) )
1663+ )
1664+ )
16141665}
16151666
16161667/**
0 commit comments