Skip to content

Commit 376c783

Browse files
committed
Fix ConditionGuardNode
1 parent e781af9 commit 376c783

1 file changed

Lines changed: 51 additions & 1 deletion

File tree

  • go/ql/lib/semmle/go/controlflow

go/ql/lib/semmle/go/controlflow/IR.qll

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,32 @@ private import ControlFlowGraphShared
1717

1818
/** Provides predicates and classes for working with IR constructs. */
1919
module 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

Comments
 (0)