@@ -43,6 +43,32 @@ private class ExprNodeImpl extends ExprNode, NodeImpl {
43
43
override string toStringImpl ( ) { result = this .getExprNode ( ) .toString ( ) }
44
44
}
45
45
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
+
46
72
/** Provides predicates related to local data flow. */
47
73
module LocalFlow {
48
74
private import codeql.ruby.dataflow.internal.SsaImpl
@@ -135,19 +161,7 @@ module LocalFlow {
135
161
or
136
162
nodeFrom .asExpr ( ) = nodeTo .asExpr ( ) .( CfgNodes:: ExprNodes:: BlockArgumentCfgNode ) .getValue ( )
137
163
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 ( ) )
151
165
or
152
166
exists ( CfgNodes:: ExprCfgNode exprTo , ReturningStatementNode n |
153
167
nodeFrom = n and
@@ -241,7 +255,8 @@ private module Cached {
241
255
// filter out nodes that clearly don't need post-update nodes
242
256
isNonConstantExpr ( n ) and
243
257
(
244
- n instanceof Argument or
258
+ n = getAPostUpdateNodeForArg ( _)
259
+ or
245
260
n = any ( CfgNodes:: ExprNodes:: InstanceVariableAccessCfgNode v ) .getReceiver ( )
246
261
)
247
262
} or
@@ -1127,7 +1142,18 @@ private module PostUpdateNodes {
1127
1142
1128
1143
ExprPostUpdateNode ( ) { this = TExprPostUpdateNode ( e ) }
1129
1144
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
+ }
1131
1157
1132
1158
override CfgScope getCfgScope ( ) { result = e .getExpr ( ) .getCfgScope ( ) }
1133
1159
0 commit comments