@@ -17,6 +17,32 @@ private import ControlFlowGraphShared
1717
1818/** Provides predicates and classes for working with IR constructs. */
1919module IR {
20+ /**
21+ * Holds if `e` is in a boolean conditional context, meaning its evaluation
22+ * is split across branch successors rather than producing a single value.
23+ *
24+ * This mirrors the conditions under which the shared CFG library creates
25+ * `TAfterValueNode`s instead of a single `TAfterNode` (see
26+ * `inConditionalContext` in `shared/controlflow/.../ControlFlowGraph.qll`),
27+ * restricted to the Go-relevant cases that affect `NotExpr` and
28+ * `LogicalBinaryExpr`.
29+ */
30+ private predicate isInBooleanCondContext ( Expr e ) {
31+ e = any ( IfStmt s ) .getCond ( )
32+ or
33+ e = any ( ForStmt s ) .getCond ( )
34+ or
35+ exists ( ExpressionSwitchStmt ess |
36+ not exists ( ess .getExpr ( ) ) and e = ess .getACase ( ) .( CaseClause ) .getExpr ( _)
37+ )
38+ or
39+ e = any ( LogicalBinaryExpr be | isInBooleanCondContext ( be ) ) .getAnOperand ( )
40+ or
41+ e = any ( NotExpr ne | isInBooleanCondContext ( ne ) ) .getOperand ( )
42+ or
43+ e = any ( ParenExpr pe | isInBooleanCondContext ( pe ) ) .getExpr ( )
44+ }
45+
2046 /**
2147 * An IR instruction.
2248 */
@@ -29,6 +55,19 @@ module IR {
2955 this .isAfterTrue ( _)
3056 or
3157 this .isAfterFalse ( _)
58+ or
59+ // `NotExpr` and `LogicalBinaryExpr` are not in `postOrInOrder`, so they
60+ // have no `isIn` node. When such an expression is not in a conditional
61+ // context (so it has a single combined after-node rather than per-branch
62+ // value-after-nodes), use that after-node as the value-producing
63+ // instruction. In conditional contexts the value is already split
64+ // across branches and the `ConditionGuardInstruction` for each branch
65+ // captures the outcome, so no separate value instruction is needed.
66+ exists ( Expr e |
67+ ( e instanceof NotExpr or e instanceof LogicalBinaryExpr ) and
68+ not isInBooleanCondContext ( e ) and
69+ this .isAfter ( e )
70+ )
3271 }
3372
3473 /** Holds if this instruction reads the value of variable or constant `v`. */
@@ -176,7 +215,18 @@ module IR {
176215 class EvalInstruction extends Instruction {
177216 Expr e ;
178217
179- EvalInstruction ( ) { this .isIn ( e ) }
218+ EvalInstruction ( ) {
219+ this .isIn ( e )
220+ or
221+ // `NotExpr` and `LogicalBinaryExpr` are not in `postOrInOrder`, so they
222+ // don't have an `isIn` node. Only use the after-node when the
223+ // expression is not in a conditional context; otherwise the value is
224+ // split across `TAfterValueNode`s per branch and should not be exposed
225+ // as a single value-producing instruction.
226+ ( e instanceof NotExpr or e instanceof LogicalBinaryExpr ) and
227+ not isInBooleanCondContext ( e ) and
228+ this .isAfter ( e )
229+ }
180230
181231 /** Gets the expression underlying this instruction. */
182232 Expr getExpr ( ) { result = e }
0 commit comments