Skip to content

Commit e8a438f

Browse files
committed
Address more review comments
1 parent 7b3fe52 commit e8a438f

2 files changed

Lines changed: 45 additions & 114 deletions

File tree

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

Lines changed: 32 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -1057,8 +1057,6 @@ module CfgImpl {
10571057
additional predicate step(PreControlFlowNode n1, PreControlFlowNode n2) {
10581058
rangeStmtStep(n1, n2) or
10591059
selectStmtStep(n1, n2) or
1060-
deferStmtStep(n1, n2) or
1061-
goStmtStep(n1, n2) or
10621060
assignmentStep(n1, n2) or
10631061
incDecStep(n1, n2) or
10641062
returnStep(n1, n2) or
@@ -1129,7 +1127,7 @@ module CfgImpl {
11291127
|
11301128
epilogueStep(assgn, n1, n2)
11311129
or
1132-
// Last epilogue after the assignment
1130+
// Last epilogue -> after the assignment
11331131
n1.isAdditional(assgn, getLastEpilogueTag(assgn)) and
11341132
n2.isAfter(assgn)
11351133
)
@@ -1204,13 +1202,7 @@ module CfgImpl {
12041202
}
12051203

12061204
private string getRankedEpilogueTag(Ast::AstNode node, int rnk) {
1207-
result =
1208-
rank[rnk](string tag, int ord |
1209-
tag = getEpilogueTag(node, ord) and
1210-
exists(tag)
1211-
|
1212-
tag order by ord
1213-
)
1205+
result = rank[rnk](string tag, int ord | tag = getEpilogueTag(node, ord) | tag order by ord)
12141206
}
12151207

12161208
private string getFirstEpilogueTag(Ast::AstNode node) { result = getRankedEpilogueTag(node, 1) }
@@ -1230,7 +1222,7 @@ module CfgImpl {
12301222
}
12311223

12321224
/**
1233-
* Increment/decrement: operand compound-rhs After(stmt).
1225+
* Increment/decrement: operand -> compound-rhs -> After(stmt).
12341226
*
12351227
* `x++` is modelled just like the compound assignment `x += 1`: a single
12361228
* `compound-rhs` node computes the updated value and writes it back to the
@@ -1239,13 +1231,10 @@ module CfgImpl {
12391231
*/
12401232
private predicate incDecStep(PreControlFlowNode n1, PreControlFlowNode n2) {
12411233
exists(Go::IncDecStmt s |
1242-
// Before → Before operand
12431234
n1.isBefore(s) and n2.isBefore(s.getOperand())
12441235
or
1245-
// After operand → compound-rhs (the update value + write)
12461236
n1.isAfter(s.getOperand()) and n2.isAdditional(s, "compound-rhs")
12471237
or
1248-
// compound-rhs → After(stmt)
12491238
n1.isAdditional(s, "compound-rhs") and n2.isAfter(s)
12501239
)
12511240
}
@@ -1258,7 +1247,7 @@ module CfgImpl {
12581247
exists(Go::ReturnStmt ret |
12591248
epilogueStep(ret, n1, n2)
12601249
or
1261-
// Last return epilogue return node
1250+
// Last return epilogue -> return node
12621251
n1.isAdditional(ret, getLastEpilogueTag(ret)) and
12631252
n2.isIn(ret)
12641253
)
@@ -1267,8 +1256,8 @@ module CfgImpl {
12671256
/**
12681257
* Call with spread arguments, e.g. `f(g())` where the inner call `g`
12691258
* returns multiple results that are passed as the arguments of the outer
1270-
* call `f`: evaluate the children (callee and inner call), extract each
1271-
* tuple element of the inner call's result, then invoke the outer call.
1259+
* call `f`: evaluate the function expression and argument call, extract
1260+
* each tuple element of the argument's result, then invoke the outer call.
12721261
*
12731262
* The tuple-extraction nodes are additional nodes (see
12741263
* `extractNodeCondition`); without wiring them into the control flow they
@@ -1277,26 +1266,25 @@ module CfgImpl {
12771266
private predicate callExprStep(PreControlFlowNode n1, PreControlFlowNode n2) {
12781267
exists(Go::CallExpr call |
12791268
// Restrict to ordinary invoked calls; the calls of `defer`/`go`
1280-
// statements are evaluated in place but invoked later / concurrently,
1281-
// and are handled by `deferStmtStep`/`goStmtStep` instead.
1269+
// statements do not use this tuple-extraction override.
12821270
not call = any(Go::DeferStmt s).getCall() and
12831271
not call = any(Go::GoStmt s).getCall() and
12841272
extractNodeCondition(call, _)
12851273
|
12861274
epilogueStep(call, n1, n2)
12871275
or
1288-
// Last tuple-extraction node the call's invocation node
1276+
// Last tuple-extraction node -> the call's invocation node
12891277
n1.isAdditional(call, getLastEpilogueTag(call)) and n2.isIn(call)
12901278
or
1291-
// Invocation node after the call (unless the call never returns normally)
1279+
// Invocation node -> after the call (unless the call never returns normally)
12921280
n1.isIn(call) and
12931281
n2.isAfter(call) and
12941282
not beginAbruptCompletion(call, n1, _, true)
12951283
)
12961284
}
12971285

12981286
/**
1299-
* Index expression: base implicit-deref? index In(indexExpr)
1287+
* Index expression: base -> implicit-deref? -> index -> In(indexExpr)
13001288
*/
13011289
private predicate indexExprStep(PreControlFlowNode n1, PreControlFlowNode n2) {
13021290
exists(Go::IndexExpr ie |
@@ -1347,7 +1335,7 @@ module CfgImpl {
13471335
}
13481336

13491337
/**
1350-
* Slice expression: base implicit-deref? low? high? max? In(sliceExpr).
1338+
* Slice expression: base -> implicit-deref? -> low? -> high? -> max? -> In(sliceExpr).
13511339
*
13521340
* Missing (implicit) bounds have no control-flow node of their own; the
13531341
* implicit lower bound of `0` is modelled as a constant on the
@@ -1357,7 +1345,7 @@ module CfgImpl {
13571345
exists(Go::SliceExpr se |
13581346
n1.isBefore(se) and n2.isBefore(se.getBase())
13591347
or
1360-
// After base implicit deref, or (if none) the first present bound / slice eval
1348+
// After base -> implicit deref, or (if none) the first present bound / slice eval
13611349
n1.isAfter(se.getBase()) and
13621350
(
13631351
if implicitDerefCondition(se.getBase())
@@ -1367,7 +1355,7 @@ module CfgImpl {
13671355
or
13681356
n1.isAdditional(se.getBase(), "implicit-deref") and sliceNext(se, -1, n2)
13691357
or
1370-
// After a present bound the next present bound / slice eval
1358+
// After a present bound -> the next present bound / slice eval
13711359
n1.isAfter(se.getLow()) and sliceNext(se, 0, n2)
13721360
or
13731361
n1.isAfter(se.getHigh()) and sliceNext(se, 1, n2)
@@ -1379,8 +1367,8 @@ module CfgImpl {
13791367
}
13801368

13811369
/**
1382-
* Selector expression with value base: base implicit-deref?
1383-
* implicit-field-selections In(selector)
1370+
* Selector expression with value base: base -> implicit-deref? ->
1371+
* implicit-field-selections -> In(selector)
13841372
*/
13851373
private predicate selectorExprStep(PreControlFlowNode n1, PreControlFlowNode n2) {
13861374
exists(Go::SelectorExpr sel |
@@ -1393,7 +1381,7 @@ module CfgImpl {
13931381
(
13941382
n1.isBefore(sel) and n2.isBefore(sel.getBase())
13951383
or
1396-
// After base (no implicit-deref) first implicit-field or In(sel)
1384+
// After base (no implicit-deref) -> first implicit-field or In(sel)
13971385
n1.isAfter(sel.getBase()) and
13981386
not implicitDerefCondition(sel.getBase()) and
13991387
(
@@ -1407,12 +1395,12 @@ module CfgImpl {
14071395
not implicitFieldSelection(sel, _, _) and n2.isIn(sel)
14081396
)
14091397
or
1410-
// After base (has implicit-deref) implicit-deref node
1398+
// After base (has implicit-deref) -> implicit-deref node
14111399
n1.isAfter(sel.getBase()) and
14121400
implicitDerefCondition(sel.getBase()) and
14131401
n2.isAdditional(sel.getBase(), "implicit-deref")
14141402
or
1415-
// After implicit-deref first implicit-field or In(sel)
1403+
// After implicit-deref -> first implicit-field or In(sel)
14161404
n1.isAdditional(sel.getBase(), "implicit-deref") and
14171405
(
14181406
exists(int maxIdx |
@@ -1432,7 +1420,7 @@ module CfgImpl {
14321420
n2.isAdditional(sel, "implicit-field:" + (i - 1).toString())
14331421
)
14341422
or
1435-
// Last implicit field read (index 1) In(sel)
1423+
// Last implicit field read (index 1) -> In(sel)
14361424
implicitFieldSelection(sel, 1, _) and
14371425
n1.isAdditional(sel, "implicit-field:1") and
14381426
n2.isIn(sel)
@@ -1443,24 +1431,24 @@ module CfgImpl {
14431431
}
14441432

14451433
/**
1446-
* Composite literal: In(lit) element-init chain After(lit)
1434+
* Composite literal: In(lit) -> element-init chain -> After(lit)
14471435
* CompositeLit evaluates the literal (allocation) first (pre-order),
14481436
* then initializes elements.
14491437
*/
14501438
private predicate compositeLitStep(PreControlFlowNode n1, PreControlFlowNode n2) {
14511439
exists(Go::CompositeLit lit |
1452-
// Before In (the literal allocation)
1440+
// Before -> In (the literal allocation)
14531441
n1.isBefore(lit) and n2.isIn(lit)
14541442
or
1455-
// In first element, or After if no elements
1443+
// In -> first element, or After if no elements
14561444
n1.isIn(lit) and
14571445
(
14581446
n2.isBefore(lit.getElement(0))
14591447
or
14601448
not exists(lit.getElement(_)) and n2.isAfter(lit)
14611449
)
14621450
or
1463-
// After element lit-init next element or After.
1451+
// After element -> lit-init -> next element or After.
14641452
// Positional array/slice elements have an implicit index that is
14651453
// modelled on the `lit-init` instruction itself (see
14661454
// `IR::InitLiteralElementInstruction`) rather than as a separate node.
@@ -1479,7 +1467,7 @@ module CfgImpl {
14791467
}
14801468

14811469
/**
1482-
* Send statement (outside select): channel value In(send)
1470+
* Send statement (outside select): channel -> value -> In(send)
14831471
*/
14841472
private predicate sendStmtStep(PreControlFlowNode n1, PreControlFlowNode n2) {
14851473
exists(Go::SendStmt s | not s = any(Go::CommClause cc).getComm() |
@@ -1656,33 +1644,13 @@ module CfgImpl {
16561644
)
16571645
}
16581646

1659-
private predicate deferStmtStep(PreControlFlowNode n1, PreControlFlowNode n2) {
1660-
exists(Go::DeferStmt s |
1661-
n1.isBefore(s) and n2.isBefore(s.getCall())
1662-
or
1663-
n1.isAfter(s.getCall()) and n2.isIn(s)
1664-
or
1665-
n1.isIn(s) and n2.isAfter(s)
1666-
)
1667-
}
1668-
1669-
private predicate goStmtStep(PreControlFlowNode n1, PreControlFlowNode n2) {
1670-
exists(Go::GoStmt s |
1671-
n1.isBefore(s) and n2.isBefore(s.getCall())
1672-
or
1673-
n1.isAfter(s.getCall()) and n2.isIn(s)
1674-
or
1675-
n1.isIn(s) and n2.isAfter(s)
1676-
)
1677-
}
1678-
16791647
/**
16801648
* Function definition prologue and epilogue:
16811649
* - Prologue: parameters are modelled as native CFG nodes by the shared
1682-
* library (Entry param ... Before(body)). The remaining
1650+
* library (Entry -> param -> ... -> Before(body)). The remaining
16831651
* prologue on Before(body) zero-initializes any named result
1684-
* variables: zero-init:0 zero-init:1 ... first statement.
1685-
* - Epilogue: return result-read:0 result-read:1 ... result-read:last
1652+
* variables: zero-init:0 -> zero-init:1 -> ... -> first statement.
1653+
* - Epilogue: return -> result-read:0 -> result-read:1 -> ... -> result-read:last
16861654
*
16871655
* The last result-read node goes to `After(body)`, from which the shared
16881656
* callable CFG continues to the normal exit.
@@ -1698,16 +1666,16 @@ module CfgImpl {
16981666

16991667
private predicate funcDefStep(PreControlFlowNode n1, PreControlFlowNode n2) {
17001668
exists(Go::FuncDef fd | exists(fd.getBody()) |
1701-
// Before(body) first result-var zero-init node. Parameters are
1669+
// Before(body) -> first result-var zero-init node. Parameters are
17021670
// modelled as native CFG nodes by the shared library and route
1703-
// Entry param ... Before(body) ahead of this point; the
1704-
// no-result-variable case (Before(body) first statement) is handled
1671+
// Entry -> param -> ... -> Before(body) ahead of this point; the
1672+
// no-result-variable case (Before(body) -> first statement) is handled
17051673
// by the shared library's default control flow.
17061674
n1.isBefore(fd.getBody()) and
17071675
exists(fd.getResultVar(0)) and
17081676
n2.isAdditional(fd.getBody(), "zero-init:0")
17091677
or
1710-
// zero-init:j next: zero-init:(j+1), or Before(body).
1678+
// zero-init:j -> next: zero-init:(j+1), or Before(body).
17111679
// The zero-init node also writes the result variable (see
17121680
// `IR::EvalImplicitInitInstruction`), so there is no separate result-init node.
17131681
exists(int j | exists(fd.getResultVar(j)) |
@@ -1723,7 +1691,7 @@ module CfgImpl {
17231691
)
17241692
)
17251693
or
1726-
// result-read:j result-read:(j+1)
1694+
// result-read:j -> result-read:(j+1)
17271695
exists(int j | exists(fd.getResultVar(j + 1)) |
17281696
n1.isAdditional(fd.getBody(), "result-read:" + j.toString()) and
17291697
n2.isAdditional(fd.getBody(), "result-read:" + (j + 1).toString())

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

Lines changed: 13 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -18,32 +18,6 @@ private import codeql.controlflow.SuccessorType
1818

1919
/** Provides predicates and classes for working with IR constructs. */
2020
module IR {
21-
/**
22-
* Holds if `e` is in a boolean conditional context, meaning its evaluation
23-
* is split across branch successors rather than producing a single value.
24-
*
25-
* This mirrors the conditions under which the shared CFG library creates
26-
* `TAfterValueNode`s instead of a single `TAfterNode` (see
27-
* `inConditionalContext` in `shared/controlflow/.../ControlFlowGraph.qll`),
28-
* restricted to the Go-relevant cases that affect `NotExpr` and
29-
* `LogicalBinaryExpr`.
30-
*/
31-
private predicate isInBooleanCondContext(Expr e) {
32-
e = any(IfStmt s).getCondition()
33-
or
34-
e = any(ForStmt s).getCond()
35-
or
36-
exists(ExpressionSwitchStmt ess |
37-
not exists(ess.getExpr()) and e = ess.getACase().(CaseClause).getExpr(_)
38-
)
39-
or
40-
e = any(LogicalBinaryExpr be | isInBooleanCondContext(be)).getAnOperand()
41-
or
42-
e = any(NotExpr ne | isInBooleanCondContext(ne)).getOperand()
43-
or
44-
e = any(ParenExpr pe | isInBooleanCondContext(pe)).getExpr()
45-
}
46-
4721
/**
4822
* Holds if `n` is the control-flow node representing a successful match of
4923
* the type-switch case clause `cc` that implicitly declares a variable.
@@ -59,19 +33,13 @@ module IR {
5933
}
6034

6135
/**
62-
* Holds if `n` is a genuine boolean condition-guard node: an "after" node
63-
* that records exactly one of the true/false outcomes of a boolean
64-
* condition.
65-
*
66-
* The shared CFG library's `isAfterTrue`/`isAfterFalse` are deliberately
67-
* permissive when used for step-endpoint matching: a plain "after" node (or
68-
* a merged leaf node) satisfies both of them. A real guard node is
69-
* distinguished by satisfying exactly one of them.
36+
* Holds if `n` records a boolean outcome, or the matching outcome of an
37+
* expressionless switch case condition.
7038
*/
7139
private predicate isConditionGuardNode(ControlFlow::Node n) {
72-
n.isAfterTrue(_) and not n.isAfterFalse(_)
40+
n.isAfterTrue(_)
7341
or
74-
n.isAfterFalse(_) and not n.isAfterTrue(_)
42+
n.isAfterFalse(_)
7543
or
7644
exists(Expr condition, MatchingSuccessor successor |
7745
condition =
@@ -97,16 +65,13 @@ module IR {
9765
typeSwitchCaseMatch(this, _)
9866
or
9967
// `NotExpr` and `LogicalBinaryExpr` are not in `postOrInOrder`, so they
100-
// have no `isIn` node. When such an expression is not in a conditional
101-
// context (so it has a single combined after-node rather than per-branch
102-
// value-after-nodes), use that after-node as the value-producing
103-
// instruction. In conditional contexts the value is already split
104-
// across branches and the `ConditionGuardInstruction` for each branch
105-
// captures the outcome, so no separate value instruction is needed.
68+
// have no `isIn` node. Use their combined after-node as the value-producing
69+
// instruction, but not a value-specific after-node, which is already a
70+
// `ConditionGuardInstruction`.
10671
exists(Expr e |
10772
(e instanceof NotExpr or e instanceof LogicalBinaryExpr) and
108-
not isInBooleanCondContext(e) and
109-
this.isAfter(e)
73+
this.isAfter(e) and
74+
not this.isAfterValue(e, _)
11075
)
11176
or
11277
// A named parameter is represented by a single CFG node (the merged
@@ -247,14 +212,12 @@ module IR {
247212
// `postOrInOrder`, so they don't have an `isIn` node; their value is
248213
// produced by the after-node. (Constant ones are folded and get a leaf
249214
// `isIn` node via `constRoot`, handled by the first disjunct above, so
250-
// they are excluded here to avoid a duplicate value node.) Only use the
251-
// after-node when the expression is not in a conditional context;
252-
// otherwise the value is split across `TAfterValueNode`s per branch and
253-
// should not be exposed as a single value-producing instruction.
215+
// they are excluded here to avoid a duplicate value node.) Value-specific
216+
// after-nodes are condition guards rather than expression evaluations.
254217
(e instanceof NotExpr or e instanceof LogicalBinaryExpr) and
255218
not e.isConst() and
256-
not isInBooleanCondContext(e) and
257-
this.isAfter(e)
219+
this.isAfter(e) and
220+
not this.isAfterValue(_, _)
258221
}
259222

260223
/** Gets the expression underlying this instruction. */

0 commit comments

Comments
 (0)