Fix switch-case sanitizer edge for shared CFG and accept CFG expected

This commit is contained in:
copilot-swe-agent[bot]
2026-06-23 19:57:40 +00:00
committed by Owen Mansel-Chan
parent aa7bdb575f
commit 73bf0613d9
5 changed files with 141 additions and 66 deletions

View File

@@ -361,9 +361,16 @@ module ControlFlow {
}
/**
* Holds if `pred` is the node for the case `testExpr` in an expression
* switch statement which is switching on `switchExpr`, and `succ` is the
* node to be executed next if the case test succeeds.
* Holds if `pred` is the node reached when a case of the expression switch
* statement switching on `switchExpr` matches, `testExpr` is one of that
* case's test expressions, and `succ` is the node to be executed next when
* the case matches.
*
* In the control-flow graph the individual case test expressions of a case
* clause all funnel into a single "matched" node for the clause, from which
* control transfers to the case body. Hence `pred` is that shared matched
* node, and the same `(pred, succ)` pair is reported once per test
* expression `testExpr` of the clause.
*/
predicate isSwitchCaseTestPassingEdge(
ControlFlow::Node pred, ControlFlow::Node succ, Expr switchExpr, Expr testExpr
@@ -372,7 +379,7 @@ module ControlFlow {
ess.getExpr() = switchExpr and
cc = ess.getACase() and
testExpr = cc.getExpr(i) and
pred.isAfter(testExpr) and
pred.isAfter(cc) and
succ.isFirstNodeOf(cc.getStmt(0))
)
}

View File

@@ -77,10 +77,20 @@ module GoCfg {
}
AstNode getChild(AstNode n, int index) {
not n instanceof Go::FuncDef and
not skipCfg(n) and
not skipCfg(result) and
result = n.getChild(index)
(
not n instanceof Go::FuncDef and
not skipCfg(n) and
result = n.getChild(index)
or
// The body block of an expression switch is transparent (see `skipCfg`),
// so it is not itself a child and contributes no children. Expose the
// case clauses directly as children of the switch instead, so that the
// AST child chain stays connected for abrupt-completion propagation
// (e.g. a panicking call in a case body reaching the enclosing
// function's exceptional exit).
result = n.(Go::ExpressionSwitchStmt).getBody().getChild(index)
) and
not skipCfg(result)
}
class Callable extends AstNode {

View File

@@ -341,10 +341,22 @@ private ControlFlow::Node getANonTestPassingPredecessor(
) {
isPossibleInputNode(inputNode, succ.getRoot()) and
result = succ.getAPredecessor() and
not exists(Expr testExpr, DataFlow::Node switchExprNode |
not exists(DataFlow::Node switchExprNode |
flowsToSwitchExpression(inputNode, switchExprNode) and
ControlFlow::isSwitchCaseTestPassingEdge(result, succ, switchExprNode.asExpr(), testExpr) and
testExpr.isConst()
// The case body is reachable only by matching a constant: at least one of
// the case's test expressions is constant, and none of them is
// non-constant. (All test expressions of a case share the same matched
// edge `result -> succ`, so a case mixing constant and non-constant tests
// must not be treated as a constant-only match.)
exists(Expr testExpr |
ControlFlow::isSwitchCaseTestPassingEdge(result, succ, switchExprNode.asExpr(), testExpr) and
testExpr.isConst()
) and
not exists(Expr nonConstTestExpr |
ControlFlow::isSwitchCaseTestPassingEdge(result, succ, switchExprNode.asExpr(),
nonConstTestExpr) and
not nonConstTestExpr.isConst()
)
)
}

View File

@@ -14,14 +14,16 @@
| DuplicateSwitchCase.go:3:29:12:1 | param-init:0 block statement | DuplicateSwitchCase.go:4:2:11:2 | expression-switch statement |
| DuplicateSwitchCase.go:4:2:11:2 | After expression-switch statement | DuplicateSwitchCase.go:3:29:12:1 | After block statement |
| DuplicateSwitchCase.go:4:2:11:2 | expression-switch statement | DuplicateSwitchCase.go:5:2:6:9 | case clause |
| DuplicateSwitchCase.go:5:2:6:9 | After case clause [match] | DuplicateSwitchCase.go:6:3:6:9 | expression statement |
| DuplicateSwitchCase.go:5:2:6:9 | After case clause [no-match] | DuplicateSwitchCase.go:7:2:8:8 | case clause |
| DuplicateSwitchCase.go:5:2:6:9 | case clause | DuplicateSwitchCase.go:5:7:5:20 | Before ...==... |
| DuplicateSwitchCase.go:5:7:5:9 | After msg | DuplicateSwitchCase.go:5:14:5:20 | Before "start" |
| DuplicateSwitchCase.go:5:7:5:9 | Before msg | DuplicateSwitchCase.go:5:7:5:9 | msg |
| DuplicateSwitchCase.go:5:7:5:9 | msg | DuplicateSwitchCase.go:5:7:5:9 | After msg |
| DuplicateSwitchCase.go:5:7:5:20 | ...==... | DuplicateSwitchCase.go:5:7:5:20 | After ...==... [false] |
| DuplicateSwitchCase.go:5:7:5:20 | ...==... | DuplicateSwitchCase.go:5:7:5:20 | After ...==... [true] |
| DuplicateSwitchCase.go:5:7:5:20 | After ...==... [false] | DuplicateSwitchCase.go:7:2:8:8 | case clause |
| DuplicateSwitchCase.go:5:7:5:20 | After ...==... [true] | DuplicateSwitchCase.go:6:3:6:9 | expression statement |
| DuplicateSwitchCase.go:5:7:5:20 | ...==... | DuplicateSwitchCase.go:5:7:5:20 | After ...==... [match] |
| DuplicateSwitchCase.go:5:7:5:20 | ...==... | DuplicateSwitchCase.go:5:7:5:20 | After ...==... [no-match] |
| DuplicateSwitchCase.go:5:7:5:20 | After ...==... [match] | DuplicateSwitchCase.go:5:2:6:9 | After case clause [match] |
| DuplicateSwitchCase.go:5:7:5:20 | After ...==... [no-match] | DuplicateSwitchCase.go:5:2:6:9 | After case clause [no-match] |
| DuplicateSwitchCase.go:5:7:5:20 | Before ...==... | DuplicateSwitchCase.go:5:7:5:9 | Before msg |
| DuplicateSwitchCase.go:5:14:5:20 | "start" | DuplicateSwitchCase.go:5:14:5:20 | After "start" |
| DuplicateSwitchCase.go:5:14:5:20 | After "start" | DuplicateSwitchCase.go:5:7:5:20 | ...==... |
@@ -35,14 +37,16 @@
| DuplicateSwitchCase.go:6:3:6:9 | call to start | DuplicateSwitchCase.go:3:1:12:1 | Exceptional Exit |
| DuplicateSwitchCase.go:6:3:6:9 | call to start | DuplicateSwitchCase.go:6:3:6:9 | After call to start |
| DuplicateSwitchCase.go:6:3:6:9 | expression statement | DuplicateSwitchCase.go:6:3:6:9 | Before call to start |
| DuplicateSwitchCase.go:7:2:8:8 | After case clause [match] | DuplicateSwitchCase.go:8:3:8:8 | expression statement |
| DuplicateSwitchCase.go:7:2:8:8 | After case clause [no-match] | DuplicateSwitchCase.go:9:2:10:34 | case clause |
| DuplicateSwitchCase.go:7:2:8:8 | case clause | DuplicateSwitchCase.go:7:7:7:20 | Before ...==... |
| DuplicateSwitchCase.go:7:7:7:9 | After msg | DuplicateSwitchCase.go:7:14:7:20 | Before "start" |
| DuplicateSwitchCase.go:7:7:7:9 | Before msg | DuplicateSwitchCase.go:7:7:7:9 | msg |
| DuplicateSwitchCase.go:7:7:7:9 | msg | DuplicateSwitchCase.go:7:7:7:9 | After msg |
| DuplicateSwitchCase.go:7:7:7:20 | ...==... | DuplicateSwitchCase.go:7:7:7:20 | After ...==... [false] |
| DuplicateSwitchCase.go:7:7:7:20 | ...==... | DuplicateSwitchCase.go:7:7:7:20 | After ...==... [true] |
| DuplicateSwitchCase.go:7:7:7:20 | After ...==... [false] | DuplicateSwitchCase.go:9:2:10:34 | case clause |
| DuplicateSwitchCase.go:7:7:7:20 | After ...==... [true] | DuplicateSwitchCase.go:8:3:8:8 | expression statement |
| DuplicateSwitchCase.go:7:7:7:20 | ...==... | DuplicateSwitchCase.go:7:7:7:20 | After ...==... [match] |
| DuplicateSwitchCase.go:7:7:7:20 | ...==... | DuplicateSwitchCase.go:7:7:7:20 | After ...==... [no-match] |
| DuplicateSwitchCase.go:7:7:7:20 | After ...==... [match] | DuplicateSwitchCase.go:7:2:8:8 | After case clause [match] |
| DuplicateSwitchCase.go:7:7:7:20 | After ...==... [no-match] | DuplicateSwitchCase.go:7:2:8:8 | After case clause [no-match] |
| DuplicateSwitchCase.go:7:7:7:20 | Before ...==... | DuplicateSwitchCase.go:7:7:7:9 | Before msg |
| DuplicateSwitchCase.go:7:14:7:20 | "start" | DuplicateSwitchCase.go:7:14:7:20 | After "start" |
| DuplicateSwitchCase.go:7:14:7:20 | After "start" | DuplicateSwitchCase.go:7:7:7:20 | ...==... |
@@ -56,7 +60,8 @@
| DuplicateSwitchCase.go:8:3:8:8 | call to stop | DuplicateSwitchCase.go:3:1:12:1 | Exceptional Exit |
| DuplicateSwitchCase.go:8:3:8:8 | call to stop | DuplicateSwitchCase.go:8:3:8:8 | After call to stop |
| DuplicateSwitchCase.go:8:3:8:8 | expression statement | DuplicateSwitchCase.go:8:3:8:8 | Before call to stop |
| DuplicateSwitchCase.go:9:2:10:34 | case clause | DuplicateSwitchCase.go:10:3:10:34 | expression statement |
| DuplicateSwitchCase.go:9:2:10:34 | After case clause [match] | DuplicateSwitchCase.go:10:3:10:34 | expression statement |
| DuplicateSwitchCase.go:9:2:10:34 | case clause | DuplicateSwitchCase.go:9:2:10:34 | After case clause [match] |
| DuplicateSwitchCase.go:10:3:10:7 | After panic | DuplicateSwitchCase.go:10:9:10:33 | Before "Message not understood." |
| DuplicateSwitchCase.go:10:3:10:7 | Before panic | DuplicateSwitchCase.go:10:3:10:7 | panic |
| DuplicateSwitchCase.go:10:3:10:7 | panic | DuplicateSwitchCase.go:10:3:10:7 | After panic |
@@ -3618,7 +3623,8 @@
| stmts.go:79:21:79:22 | 19 | stmts.go:79:21:79:22 | After 19 |
| stmts.go:79:21:79:22 | After 19 | stmts.go:79:17:79:22 | ...-... |
| stmts.go:79:21:79:22 | Before 19 | stmts.go:79:21:79:22 | 19 |
| stmts.go:80:2:81:14 | case clause | stmts.go:81:3:81:14 | expression statement |
| stmts.go:80:2:81:14 | After case clause [match] | stmts.go:81:3:81:14 | expression statement |
| stmts.go:80:2:81:14 | case clause | stmts.go:80:2:81:14 | After case clause [match] |
| stmts.go:81:3:81:7 | After test5 | stmts.go:81:9:81:13 | Before false |
| stmts.go:81:3:81:7 | Before test5 | stmts.go:81:3:81:7 | test5 |
| stmts.go:81:3:81:7 | test5 | stmts.go:81:3:81:7 | After test5 |
@@ -3636,18 +3642,26 @@
| stmts.go:84:9:84:9 | After x | stmts.go:85:2:85:8 | case clause |
| stmts.go:84:9:84:9 | Before x | stmts.go:84:9:84:9 | x |
| stmts.go:84:9:84:9 | x | stmts.go:84:9:84:9 | After x |
| stmts.go:85:2:85:8 | After case clause [match] | stmts.go:84:2:88:2 | After expression-switch statement |
| stmts.go:85:2:85:8 | After case clause [no-match] | stmts.go:86:2:87:13 | case clause |
| stmts.go:85:2:85:8 | case clause | stmts.go:85:7:85:7 | Before 1 |
| stmts.go:85:7:85:7 | 1 | stmts.go:85:7:85:7 | After 1 |
| stmts.go:85:7:85:7 | After 1 | stmts.go:84:2:88:2 | After expression-switch statement |
| stmts.go:85:7:85:7 | After 1 | stmts.go:86:2:87:13 | case clause |
| stmts.go:85:7:85:7 | 1 | stmts.go:85:7:85:7 | After 1 [match] |
| stmts.go:85:7:85:7 | 1 | stmts.go:85:7:85:7 | After 1 [no-match] |
| stmts.go:85:7:85:7 | After 1 [match] | stmts.go:85:2:85:8 | After case clause [match] |
| stmts.go:85:7:85:7 | After 1 [no-match] | stmts.go:85:2:85:8 | After case clause [no-match] |
| stmts.go:85:7:85:7 | Before 1 | stmts.go:85:7:85:7 | 1 |
| stmts.go:86:2:87:13 | After case clause [match] | stmts.go:87:3:87:13 | expression statement |
| stmts.go:86:2:87:13 | After case clause [no-match] | stmts.go:84:2:88:2 | After expression-switch statement |
| stmts.go:86:2:87:13 | case clause | stmts.go:86:7:86:7 | Before 2 |
| stmts.go:86:7:86:7 | 2 | stmts.go:86:7:86:7 | After 2 |
| stmts.go:86:7:86:7 | After 2 | stmts.go:86:10:86:10 | Before 3 |
| stmts.go:86:7:86:7 | 2 | stmts.go:86:7:86:7 | After 2 [match] |
| stmts.go:86:7:86:7 | 2 | stmts.go:86:7:86:7 | After 2 [no-match] |
| stmts.go:86:7:86:7 | After 2 [match] | stmts.go:86:2:87:13 | After case clause [match] |
| stmts.go:86:7:86:7 | After 2 [no-match] | stmts.go:86:10:86:10 | Before 3 |
| stmts.go:86:7:86:7 | Before 2 | stmts.go:86:7:86:7 | 2 |
| stmts.go:86:10:86:10 | 3 | stmts.go:86:10:86:10 | After 3 |
| stmts.go:86:10:86:10 | After 3 | stmts.go:84:2:88:2 | After expression-switch statement |
| stmts.go:86:10:86:10 | After 3 | stmts.go:87:3:87:13 | expression statement |
| stmts.go:86:10:86:10 | 3 | stmts.go:86:10:86:10 | After 3 [match] |
| stmts.go:86:10:86:10 | 3 | stmts.go:86:10:86:10 | After 3 [no-match] |
| stmts.go:86:10:86:10 | After 3 [match] | stmts.go:86:2:87:13 | After case clause [match] |
| stmts.go:86:10:86:10 | After 3 [no-match] | stmts.go:86:2:87:13 | After case clause [no-match] |
| stmts.go:86:10:86:10 | Before 3 | stmts.go:86:10:86:10 | 3 |
| stmts.go:87:3:87:7 | After test5 | stmts.go:87:9:87:12 | Before true |
| stmts.go:87:3:87:7 | Before test5 | stmts.go:87:3:87:7 | test5 |
@@ -3666,10 +3680,13 @@
| stmts.go:90:9:90:9 | After x | stmts.go:91:2:93:13 | case clause |
| stmts.go:90:9:90:9 | Before x | stmts.go:90:9:90:9 | x |
| stmts.go:90:9:90:9 | x | stmts.go:90:9:90:9 | After x |
| stmts.go:91:2:93:13 | After case clause [match] | stmts.go:92:3:92:14 | expression statement |
| stmts.go:91:2:93:13 | After case clause [no-match] | stmts.go:94:2:95:13 | case clause |
| stmts.go:91:2:93:13 | case clause | stmts.go:91:7:91:7 | Before 1 |
| stmts.go:91:7:91:7 | 1 | stmts.go:91:7:91:7 | After 1 |
| stmts.go:91:7:91:7 | After 1 | stmts.go:92:3:92:14 | expression statement |
| stmts.go:91:7:91:7 | After 1 | stmts.go:94:2:95:13 | case clause |
| stmts.go:91:7:91:7 | 1 | stmts.go:91:7:91:7 | After 1 [match] |
| stmts.go:91:7:91:7 | 1 | stmts.go:91:7:91:7 | After 1 [no-match] |
| stmts.go:91:7:91:7 | After 1 [match] | stmts.go:91:2:93:13 | After case clause [match] |
| stmts.go:91:7:91:7 | After 1 [no-match] | stmts.go:91:2:93:13 | After case clause [no-match] |
| stmts.go:91:7:91:7 | Before 1 | stmts.go:91:7:91:7 | 1 |
| stmts.go:92:3:92:7 | After test5 | stmts.go:92:9:92:13 | Before false |
| stmts.go:92:3:92:7 | Before test5 | stmts.go:92:3:92:7 | test5 |
@@ -3683,14 +3700,17 @@
| stmts.go:92:9:92:13 | After false | stmts.go:92:3:92:14 | call to test5 |
| stmts.go:92:9:92:13 | Before false | stmts.go:92:9:92:13 | false |
| stmts.go:92:9:92:13 | false | stmts.go:92:9:92:13 | After false |
| stmts.go:93:3:93:13 | fallthrough statement | stmts.go:90:2:96:2 | After expression-switch statement |
| stmts.go:93:3:93:13 | fallthrough statement | stmts.go:95:3:95:13 | expression statement |
| stmts.go:94:2:95:13 | After case clause [match] | stmts.go:95:3:95:13 | expression statement |
| stmts.go:94:2:95:13 | After case clause [no-match] | stmts.go:90:2:96:2 | After expression-switch statement |
| stmts.go:94:2:95:13 | case clause | stmts.go:94:7:94:11 | Before ...-... |
| stmts.go:94:7:94:7 | 2 | stmts.go:94:7:94:7 | After 2 |
| stmts.go:94:7:94:7 | After 2 | stmts.go:94:11:94:11 | Before 5 |
| stmts.go:94:7:94:7 | Before 2 | stmts.go:94:7:94:7 | 2 |
| stmts.go:94:7:94:11 | ...-... | stmts.go:94:7:94:11 | After ...-... |
| stmts.go:94:7:94:11 | After ...-... | stmts.go:90:2:96:2 | After expression-switch statement |
| stmts.go:94:7:94:11 | After ...-... | stmts.go:95:3:95:13 | expression statement |
| stmts.go:94:7:94:11 | ...-... | stmts.go:94:7:94:11 | After ...-... [match] |
| stmts.go:94:7:94:11 | ...-... | stmts.go:94:7:94:11 | After ...-... [no-match] |
| stmts.go:94:7:94:11 | After ...-... [match] | stmts.go:94:2:95:13 | After case clause [match] |
| stmts.go:94:7:94:11 | After ...-... [no-match] | stmts.go:94:2:95:13 | After case clause [no-match] |
| stmts.go:94:7:94:11 | Before ...-... | stmts.go:94:7:94:7 | Before 2 |
| stmts.go:94:11:94:11 | 5 | stmts.go:94:11:94:11 | After 5 |
| stmts.go:94:11:94:11 | After 5 | stmts.go:94:7:94:11 | ...-... |
@@ -3712,12 +3732,15 @@
| stmts.go:98:9:98:9 | After x | stmts.go:100:2:101:13 | case clause |
| stmts.go:98:9:98:9 | Before x | stmts.go:98:9:98:9 | x |
| stmts.go:98:9:98:9 | x | stmts.go:98:9:98:9 | After x |
| stmts.go:99:2:99:9 | case clause | stmts.go:98:2:102:2 | After expression-switch statement |
| stmts.go:99:2:99:9 | case clause | stmts.go:100:2:101:13 | case clause |
| stmts.go:99:2:99:9 | After case clause [match] | stmts.go:98:2:102:2 | After expression-switch statement |
| stmts.go:99:2:99:9 | case clause | stmts.go:99:2:99:9 | After case clause [match] |
| stmts.go:100:2:101:13 | After case clause [match] | stmts.go:101:3:101:13 | expression statement |
| stmts.go:100:2:101:13 | After case clause [no-match] | stmts.go:99:2:99:9 | case clause |
| stmts.go:100:2:101:13 | case clause | stmts.go:100:7:100:7 | Before 2 |
| stmts.go:100:7:100:7 | 2 | stmts.go:100:7:100:7 | After 2 |
| stmts.go:100:7:100:7 | After 2 | stmts.go:99:2:99:9 | case clause |
| stmts.go:100:7:100:7 | After 2 | stmts.go:101:3:101:13 | expression statement |
| stmts.go:100:7:100:7 | 2 | stmts.go:100:7:100:7 | After 2 [match] |
| stmts.go:100:7:100:7 | 2 | stmts.go:100:7:100:7 | After 2 [no-match] |
| stmts.go:100:7:100:7 | After 2 [match] | stmts.go:100:2:101:13 | After case clause [match] |
| stmts.go:100:7:100:7 | After 2 [no-match] | stmts.go:100:2:101:13 | After case clause [no-match] |
| stmts.go:100:7:100:7 | Before 2 | stmts.go:100:7:100:7 | 2 |
| stmts.go:101:3:101:7 | After test5 | stmts.go:101:9:101:12 | Before true |
| stmts.go:101:3:101:7 | Before test5 | stmts.go:101:3:101:7 | test5 |
@@ -3733,10 +3756,18 @@
| stmts.go:101:9:101:12 | true | stmts.go:101:9:101:12 | After true |
| stmts.go:104:2:108:2 | After expression-switch statement | stmts.go:75:19:109:1 | After block statement |
| stmts.go:104:2:108:2 | expression-switch statement | stmts.go:107:2:107:11 | case clause |
| stmts.go:105:2:106:7 | After case clause [match] | stmts.go:106:3:106:7 | Before break statement |
| stmts.go:105:2:106:7 | case clause | stmts.go:105:2:106:7 | After case clause [match] |
| stmts.go:106:3:106:7 | Before break statement | stmts.go:106:3:106:7 | break statement |
| stmts.go:106:3:106:7 | break statement | stmts.go:104:2:108:2 | After expression-switch statement |
| stmts.go:107:2:107:11 | After case clause [match] | stmts.go:104:2:108:2 | After expression-switch statement |
| stmts.go:107:2:107:11 | After case clause [no-match] | stmts.go:105:2:106:7 | case clause |
| stmts.go:107:2:107:11 | case clause | stmts.go:107:7:107:10 | Before true |
| stmts.go:107:7:107:10 | After true [true] | stmts.go:104:2:108:2 | After expression-switch statement |
| stmts.go:107:7:107:10 | After true [match] | stmts.go:107:2:107:11 | After case clause [match] |
| stmts.go:107:7:107:10 | After true [no-match] | stmts.go:107:2:107:11 | After case clause [no-match] |
| stmts.go:107:7:107:10 | Before true | stmts.go:107:7:107:10 | true |
| stmts.go:107:7:107:10 | true | stmts.go:107:7:107:10 | After true [true] |
| stmts.go:107:7:107:10 | true | stmts.go:107:7:107:10 | After true [match] |
| stmts.go:107:7:107:10 | true | stmts.go:107:7:107:10 | After true [no-match] |
| stmts.go:112:1:137:1 | After function declaration | stmts.go:140:1:142:1 | Before function declaration |
| stmts.go:112:1:137:1 | Before function declaration | stmts.go:112:1:137:1 | function declaration |
| stmts.go:112:1:137:1 | Entry | stmts.go:112:27:137:1 | block statement |
@@ -4027,38 +4058,44 @@
| tst.go:3:19:12:1 | param-init:0 block statement | tst.go:4:2:11:2 | expression-switch statement |
| tst.go:4:2:11:2 | After expression-switch statement | tst.go:3:19:12:1 | After block statement |
| tst.go:4:2:11:2 | expression-switch statement | tst.go:5:2:5:13 | case clause |
| tst.go:5:2:5:13 | After case clause [match] | tst.go:4:2:11:2 | After expression-switch statement |
| tst.go:5:2:5:13 | After case clause [no-match] | tst.go:7:2:7:13 | case clause |
| tst.go:5:2:5:13 | case clause | tst.go:5:7:5:12 | Before ...<... |
| tst.go:5:7:5:7 | After x | tst.go:5:11:5:12 | Before 23 |
| tst.go:5:7:5:7 | Before x | tst.go:5:7:5:7 | x |
| tst.go:5:7:5:7 | x | tst.go:5:7:5:7 | After x |
| tst.go:5:7:5:12 | ...<... | tst.go:5:7:5:12 | After ...<... [false] |
| tst.go:5:7:5:12 | ...<... | tst.go:5:7:5:12 | After ...<... [true] |
| tst.go:5:7:5:12 | After ...<... [false] | tst.go:7:2:7:13 | case clause |
| tst.go:5:7:5:12 | After ...<... [true] | tst.go:4:2:11:2 | After expression-switch statement |
| tst.go:5:7:5:12 | ...<... | tst.go:5:7:5:12 | After ...<... [match] |
| tst.go:5:7:5:12 | ...<... | tst.go:5:7:5:12 | After ...<... [no-match] |
| tst.go:5:7:5:12 | After ...<... [match] | tst.go:5:2:5:13 | After case clause [match] |
| tst.go:5:7:5:12 | After ...<... [no-match] | tst.go:5:2:5:13 | After case clause [no-match] |
| tst.go:5:7:5:12 | Before ...<... | tst.go:5:7:5:7 | Before x |
| tst.go:5:11:5:12 | 23 | tst.go:5:11:5:12 | After 23 |
| tst.go:5:11:5:12 | After 23 | tst.go:5:7:5:12 | ...<... |
| tst.go:5:11:5:12 | Before 23 | tst.go:5:11:5:12 | 23 |
| tst.go:7:2:7:13 | After case clause [match] | tst.go:4:2:11:2 | After expression-switch statement |
| tst.go:7:2:7:13 | After case clause [no-match] | tst.go:9:2:9:13 | case clause |
| tst.go:7:2:7:13 | case clause | tst.go:7:7:7:12 | Before ...<... |
| tst.go:7:7:7:7 | After x | tst.go:7:11:7:12 | Before 42 |
| tst.go:7:7:7:7 | Before x | tst.go:7:7:7:7 | x |
| tst.go:7:7:7:7 | x | tst.go:7:7:7:7 | After x |
| tst.go:7:7:7:12 | ...<... | tst.go:7:7:7:12 | After ...<... [false] |
| tst.go:7:7:7:12 | ...<... | tst.go:7:7:7:12 | After ...<... [true] |
| tst.go:7:7:7:12 | After ...<... [false] | tst.go:9:2:9:13 | case clause |
| tst.go:7:7:7:12 | After ...<... [true] | tst.go:4:2:11:2 | After expression-switch statement |
| tst.go:7:7:7:12 | ...<... | tst.go:7:7:7:12 | After ...<... [match] |
| tst.go:7:7:7:12 | ...<... | tst.go:7:7:7:12 | After ...<... [no-match] |
| tst.go:7:7:7:12 | After ...<... [match] | tst.go:7:2:7:13 | After case clause [match] |
| tst.go:7:7:7:12 | After ...<... [no-match] | tst.go:7:2:7:13 | After case clause [no-match] |
| tst.go:7:7:7:12 | Before ...<... | tst.go:7:7:7:7 | Before x |
| tst.go:7:11:7:12 | 42 | tst.go:7:11:7:12 | After 42 |
| tst.go:7:11:7:12 | After 42 | tst.go:7:7:7:12 | ...<... |
| tst.go:7:11:7:12 | Before 42 | tst.go:7:11:7:12 | 42 |
| tst.go:9:2:9:13 | After case clause [match] | tst.go:4:2:11:2 | After expression-switch statement |
| tst.go:9:2:9:13 | After case clause [no-match] | tst.go:4:2:11:2 | After expression-switch statement |
| tst.go:9:2:9:13 | case clause | tst.go:9:7:9:12 | Before ...<... |
| tst.go:9:7:9:7 | After x | tst.go:9:11:9:12 | Before 23 |
| tst.go:9:7:9:7 | Before x | tst.go:9:7:9:7 | x |
| tst.go:9:7:9:7 | x | tst.go:9:7:9:7 | After x |
| tst.go:9:7:9:12 | ...<... | tst.go:9:7:9:12 | After ...<... [false] |
| tst.go:9:7:9:12 | ...<... | tst.go:9:7:9:12 | After ...<... [true] |
| tst.go:9:7:9:12 | After ...<... [false] | tst.go:4:2:11:2 | After expression-switch statement |
| tst.go:9:7:9:12 | After ...<... [true] | tst.go:4:2:11:2 | After expression-switch statement |
| tst.go:9:7:9:12 | ...<... | tst.go:9:7:9:12 | After ...<... [match] |
| tst.go:9:7:9:12 | ...<... | tst.go:9:7:9:12 | After ...<... [no-match] |
| tst.go:9:7:9:12 | After ...<... [match] | tst.go:9:2:9:13 | After case clause [match] |
| tst.go:9:7:9:12 | After ...<... [no-match] | tst.go:9:2:9:13 | After case clause [no-match] |
| tst.go:9:7:9:12 | Before ...<... | tst.go:9:7:9:7 | Before x |
| tst.go:9:11:9:12 | 23 | tst.go:9:11:9:12 | After 23 |
| tst.go:9:11:9:12 | After 23 | tst.go:9:7:9:12 | ...<... |
@@ -4074,14 +4111,16 @@
| tst.go:14:26:21:1 | param-init:0 block statement | tst.go:15:2:20:2 | expression-switch statement |
| tst.go:15:2:20:2 | After expression-switch statement | tst.go:14:26:21:1 | After block statement |
| tst.go:15:2:20:2 | expression-switch statement | tst.go:16:2:16:34 | case clause |
| tst.go:16:2:16:34 | After case clause [match] | tst.go:15:2:20:2 | After expression-switch statement |
| tst.go:16:2:16:34 | After case clause [no-match] | tst.go:18:2:18:39 | case clause |
| tst.go:16:2:16:34 | case clause | tst.go:16:7:16:33 | Before ...<... |
| tst.go:16:7:16:11 | After value | tst.go:16:15:16:33 | Before ...*... |
| tst.go:16:7:16:11 | Before value | tst.go:16:7:16:11 | value |
| tst.go:16:7:16:11 | value | tst.go:16:7:16:11 | After value |
| tst.go:16:7:16:33 | ...<... | tst.go:16:7:16:33 | After ...<... [false] |
| tst.go:16:7:16:33 | ...<... | tst.go:16:7:16:33 | After ...<... [true] |
| tst.go:16:7:16:33 | After ...<... [false] | tst.go:18:2:18:39 | case clause |
| tst.go:16:7:16:33 | After ...<... [true] | tst.go:15:2:20:2 | After expression-switch statement |
| tst.go:16:7:16:33 | ...<... | tst.go:16:7:16:33 | After ...<... [match] |
| tst.go:16:7:16:33 | ...<... | tst.go:16:7:16:33 | After ...<... [no-match] |
| tst.go:16:7:16:33 | After ...<... [match] | tst.go:16:2:16:34 | After case clause [match] |
| tst.go:16:7:16:33 | After ...<... [no-match] | tst.go:16:2:16:34 | After case clause [no-match] |
| tst.go:16:7:16:33 | Before ...<... | tst.go:16:7:16:11 | Before value |
| tst.go:16:15:16:18 | 1024 | tst.go:16:15:16:18 | After 1024 |
| tst.go:16:15:16:18 | After 1024 | tst.go:16:20:16:23 | Before 1024 |
@@ -4104,14 +4143,16 @@
| tst.go:16:30:16:33 | 1024 | tst.go:16:30:16:33 | After 1024 |
| tst.go:16:30:16:33 | After 1024 | tst.go:16:15:16:33 | ...*... |
| tst.go:16:30:16:33 | Before 1024 | tst.go:16:30:16:33 | 1024 |
| tst.go:18:2:18:39 | After case clause [match] | tst.go:15:2:20:2 | After expression-switch statement |
| tst.go:18:2:18:39 | After case clause [no-match] | tst.go:15:2:20:2 | After expression-switch statement |
| tst.go:18:2:18:39 | case clause | tst.go:18:7:18:38 | Before ...<... |
| tst.go:18:7:18:11 | After value | tst.go:18:15:18:38 | Before ...*... |
| tst.go:18:7:18:11 | Before value | tst.go:18:7:18:11 | value |
| tst.go:18:7:18:11 | value | tst.go:18:7:18:11 | After value |
| tst.go:18:7:18:38 | ...<... | tst.go:18:7:18:38 | After ...<... [false] |
| tst.go:18:7:18:38 | ...<... | tst.go:18:7:18:38 | After ...<... [true] |
| tst.go:18:7:18:38 | After ...<... [false] | tst.go:15:2:20:2 | After expression-switch statement |
| tst.go:18:7:18:38 | After ...<... [true] | tst.go:15:2:20:2 | After expression-switch statement |
| tst.go:18:7:18:38 | ...<... | tst.go:18:7:18:38 | After ...<... [match] |
| tst.go:18:7:18:38 | ...<... | tst.go:18:7:18:38 | After ...<... [no-match] |
| tst.go:18:7:18:38 | After ...<... [match] | tst.go:18:2:18:39 | After case clause [match] |
| tst.go:18:7:18:38 | After ...<... [no-match] | tst.go:18:2:18:39 | After case clause [no-match] |
| tst.go:18:7:18:38 | Before ...<... | tst.go:18:7:18:11 | Before value |
| tst.go:18:15:18:18 | 1024 | tst.go:18:15:18:18 | After 1024 |
| tst.go:18:15:18:18 | After 1024 | tst.go:18:20:18:23 | Before 1024 |
@@ -4158,5 +4199,5 @@
| tst.go:28:15:32:1 | block statement | tst.go:29:2:31:2 | expression-switch statement |
| tst.go:29:2:31:2 | After expression-switch statement | tst.go:28:15:32:1 | After block statement |
| tst.go:29:2:31:2 | expression-switch statement | tst.go:30:2:30:9 | case clause |
| tst.go:30:2:30:9 | case clause | tst.go:29:2:31:2 | After expression-switch statement |
| tst.go:30:2:30:9 | case clause | tst.go:29:9:31:2 | After block statement |
| tst.go:30:2:30:9 | After case clause [match] | tst.go:29:2:31:2 | After expression-switch statement |
| tst.go:30:2:30:9 | case clause | tst.go:30:2:30:9 | After case clause [match] |

View File

@@ -683,7 +683,12 @@ module Make0<LocationSig Location, AstSig<Location> Ast> {
not exists(getChild(n, _)) and
not postOrInOrder(n) and
not additionalNode(n, _, _) and
not inConditionalContext(n, _)
not inConditionalContext(n, _) and
// A switch is a branching construct with an explicit step from its
// "before" node to its "after" node, so it must keep distinct before and
// after nodes even when it has no children (e.g. an empty `switch {}`).
// Merging them would otherwise turn that step into a spurious self-loop.
not n instanceof Switch
}
private string catchClauseEmptyBodyTag() { result = "[CatchClauseEmptyBody]" }