@@ -43,6 +43,32 @@ private class ExprNodeImpl extends ExprNode, NodeImpl {
4343 override string toStringImpl ( ) { result = this .getExprNode ( ) .toString ( ) }
4444}
4545
46+ /**
47+ * Gets a node that may execute last in `n`, and which, when it executes last,
48+ * will be the value of `n`.
49+ */
50+ private CfgNodes:: ExprCfgNode getALastEvalNode ( CfgNodes:: ExprCfgNode n ) {
51+ result = n .( CfgNodes:: ExprNodes:: StmtSequenceCfgNode ) .getLastStmt ( )
52+ or
53+ result = n .( CfgNodes:: ExprNodes:: ConditionalExprCfgNode ) .getBranch ( _)
54+ or
55+ exists ( CfgNodes:: AstCfgNode branch |
56+ branch = n .( CfgNodes:: ExprNodes:: CaseExprCfgNode ) .getBranch ( _)
57+ |
58+ result = branch .( CfgNodes:: ExprNodes:: InClauseCfgNode ) .getBody ( )
59+ or
60+ result = branch .( CfgNodes:: ExprNodes:: WhenClauseCfgNode ) .getBody ( )
61+ or
62+ result = branch
63+ )
64+ }
65+
66+ /** Gets a node for which to construct a post-update node for argument `arg`. */
67+ CfgNodes:: ExprCfgNode getAPostUpdateNodeForArg ( Argument arg ) {
68+ result = getALastEvalNode * ( arg ) and
69+ not exists ( getALastEvalNode ( result ) )
70+ }
71+
4672/** Provides predicates related to local data flow. */
4773module LocalFlow {
4874 private import codeql.ruby.dataflow.internal.SsaImpl
@@ -135,19 +161,7 @@ module LocalFlow {
135161 or
136162 nodeFrom .asExpr ( ) = nodeTo .asExpr ( ) .( CfgNodes:: ExprNodes:: BlockArgumentCfgNode ) .getValue ( )
137163 or
138- nodeFrom .asExpr ( ) = nodeTo .asExpr ( ) .( CfgNodes:: ExprNodes:: StmtSequenceCfgNode ) .getLastStmt ( )
139- or
140- nodeFrom .asExpr ( ) = nodeTo .asExpr ( ) .( CfgNodes:: ExprNodes:: ConditionalExprCfgNode ) .getBranch ( _)
141- or
142- exists ( CfgNodes:: AstCfgNode branch |
143- branch = nodeTo .asExpr ( ) .( CfgNodes:: ExprNodes:: CaseExprCfgNode ) .getBranch ( _)
144- |
145- nodeFrom .asExpr ( ) = branch .( CfgNodes:: ExprNodes:: InClauseCfgNode ) .getBody ( )
146- or
147- nodeFrom .asExpr ( ) = branch .( CfgNodes:: ExprNodes:: WhenClauseCfgNode ) .getBody ( )
148- or
149- nodeFrom .asExpr ( ) = branch
150- )
164+ nodeFrom .asExpr ( ) = getALastEvalNode ( nodeTo .asExpr ( ) )
151165 or
152166 exists ( CfgNodes:: ExprCfgNode exprTo , ReturningStatementNode n |
153167 nodeFrom = n and
@@ -241,7 +255,8 @@ private module Cached {
241255 // filter out nodes that clearly don't need post-update nodes
242256 isNonConstantExpr ( n ) and
243257 (
244- n instanceof Argument or
258+ n = getAPostUpdateNodeForArg ( _)
259+ or
245260 n = any ( CfgNodes:: ExprNodes:: InstanceVariableAccessCfgNode v ) .getReceiver ( )
246261 )
247262 } or
@@ -1127,7 +1142,18 @@ private module PostUpdateNodes {
11271142
11281143 ExprPostUpdateNode ( ) { this = TExprPostUpdateNode ( e ) }
11291144
1130- override ExprNode getPreUpdateNode ( ) { e = result .getExprNode ( ) }
1145+ override ExprNode getPreUpdateNode ( ) {
1146+ // For compund arguments, such as `m(if b then x else y)`, we want the leaf nodes
1147+ // `[post] x` and `[post] y` to have two pre-update nodes: (1) the compund argument,
1148+ // `if b then x else y`; and the (2) the underlying expressions; `x` and `y`,
1149+ // respectively.
1150+ //
1151+ // This ensures that we get flow out of the call into both leafs (1), while still
1152+ // maintaining the invariant that the underlying expression is a pre-update node (2).
1153+ e = getAPostUpdateNodeForArg ( result .getExprNode ( ) )
1154+ or
1155+ e = result .getExprNode ( )
1156+ }
11311157
11321158 override CfgScope getCfgScope ( ) { result = e .getExpr ( ) .getCfgScope ( ) }
11331159
0 commit comments