@@ -68,12 +68,19 @@ module GoCfg {
6868 or
6969 e .getParent * ( ) = any ( Go:: ArrayTypeExpr ate ) .getLength ( )
7070 or
71- // The body block of an expression switch is transparent: the shared
72- // switch model wires control flow directly from the switch to its case
73- // clauses (in control-flow order) and between cases, so the enclosing
74- // block must not introduce its own nodes or default left-to-right
75- // sequencing of the case clauses.
76- e = any ( Go:: ExpressionSwitchStmt sw ) .getBody ( )
71+ // The body block of a switch (expression or type) is transparent: the
72+ // shared switch model wires control flow directly from the switch to its
73+ // case clauses (in control-flow order) and between cases, so the
74+ // enclosing block must not introduce its own nodes or default
75+ // left-to-right sequencing of the case clauses.
76+ e = any ( Go:: SwitchStmt sw ) .getBody ( )
77+ or
78+ // The test statement of a type switch (`y := x.(type)` or the bare
79+ // `x.(type)` expression statement) is transparent: the shared switch
80+ // model evaluates the underlying type-assertion expression directly as
81+ // the switch expression (see `Switch.getExpr`), so the wrapping
82+ // statement must not introduce its own assignment or expression nodes.
83+ e = any ( Go:: TypeSwitchStmt ts ) .getTest ( )
7784 }
7885
7986 AstNode getChild ( AstNode n , int index ) {
@@ -82,13 +89,18 @@ module GoCfg {
8289 not skipCfg ( n ) and
8390 result = n .getChild ( index )
8491 or
85- // The body block of an expression switch is transparent (see `skipCfg`),
86- // so it is not itself a child and contributes no children. Expose the
87- // case clauses directly as children of the switch instead, so that the
88- // AST child chain stays connected for abrupt-completion propagation
89- // (e.g. a panicking call in a case body reaching the enclosing
90- // function's exceptional exit).
91- result = n .( Go:: ExpressionSwitchStmt ) .getBody ( ) .getChild ( index )
92+ // The body block of a switch (expression or type) is transparent (see
93+ // `skipCfg`), so it is not itself a child and contributes no children.
94+ // Expose the case clauses directly as children of the switch instead,
95+ // so that the AST child chain stays connected for abrupt-completion
96+ // propagation (e.g. a panicking call in a case body reaching the
97+ // enclosing function's exceptional exit).
98+ result = n .( Go:: SwitchStmt ) .getBody ( ) .getChild ( index )
99+ or
100+ // The type-switch test statement is transparent (see `skipCfg`), so
101+ // expose the underlying type-assertion expression directly as a child
102+ // of the type switch, keeping the AST child chain connected.
103+ result = n .( Go:: TypeSwitchStmt ) .getExpr ( ) and index = 1
92104 ) and
93105 not skipCfg ( result )
94106 }
@@ -141,7 +153,17 @@ module GoCfg {
141153 }
142154 }
143155
144- class ExprStmt = Go:: ExprStmt ;
156+ class ExprStmt extends Stmt instanceof Go:: ExprStmt {
157+ // The `x.(type)` test statement of a type switch is transparent (see
158+ // `skipCfg`): the shared switch model evaluates the underlying
159+ // type-assertion expression directly as the switch expression. It must
160+ // therefore not be treated as an ordinary expression statement, whose
161+ // value would otherwise be propagated from the expression to the
162+ // statement (creating a spurious flow into the transparent wrapper).
163+ ExprStmt ( ) { not this = any ( Go:: TypeSwitchStmt ts ) .getTest ( ) }
164+
165+ Expr getExpr ( ) { result = Go:: ExprStmt .super .getExpr ( ) }
166+ }
145167
146168 class IfStmt extends Stmt {
147169 IfStmt ( ) { this instanceof Go:: IfStmt }
@@ -234,11 +256,19 @@ module GoCfg {
234256 }
235257
236258 class Switch extends AstNode {
237- Switch ( ) { this instanceof Go:: ExpressionSwitchStmt }
259+ Switch ( ) { this instanceof Go:: SwitchStmt }
238260
239- Expr getExpr ( ) { result = this .( Go:: ExpressionSwitchStmt ) .getExpr ( ) }
261+ Expr getExpr ( ) {
262+ result = this .( Go:: ExpressionSwitchStmt ) .getExpr ( )
263+ or
264+ // For a type switch the "switch expression" is the type-assertion
265+ // expression `x.(type)`; evaluating it directly (rather than the
266+ // wrapping `y := x.(type)` statement) lets the shared switch model
267+ // drive the per-case type tests.
268+ result = this .( Go:: TypeSwitchStmt ) .getExpr ( )
269+ }
240270
241- Case getCase ( int index ) { result = this .( Go:: ExpressionSwitchStmt ) .getCase ( index ) }
271+ Case getCase ( int index ) { result = this .( Go:: SwitchStmt ) .getCase ( index ) }
242272
243273 Stmt getStmt ( int index ) {
244274 // Go nests each case clause's body statements under the clause rather
@@ -256,7 +286,7 @@ module GoCfg {
256286 }
257287
258288 class Case extends AstNode {
259- Case ( ) { this = any ( Go:: ExpressionSwitchStmt sw ) .getACase ( ) }
289+ Case ( ) { this = any ( Go:: SwitchStmt sw ) .getACase ( ) }
260290
261291 AstNode getPattern ( int index ) { result = this .( Go:: CaseClause ) .getExpr ( index ) }
262292
@@ -270,7 +300,7 @@ module GoCfg {
270300 }
271301
272302 /** Gets the initializer of `switch` statement `switch`, if any. */
273- AstNode getSwitchInit ( Switch switch ) { result = switch .( Go:: ExpressionSwitchStmt ) .getInit ( ) }
303+ AstNode getSwitchInit ( Switch switch ) { result = switch .( Go:: SwitchStmt ) .getInit ( ) }
274304
275305 /**
276306 * Go has no implicit fall-through between case clauses; a case that runs to
@@ -287,12 +317,10 @@ module GoCfg {
287317
288318 /**
289319 * Holds if `s` is the flattened body element at position (`caseIdx`,
290- * `inner`) of expression switch `sw`: either the `caseIdx`-th case clause
291- * itself (with `inner` = -1) or its `inner`-th body statement.
320+ * `inner`) of switch `sw`: either the `caseIdx`-th case clause itself (with
321+ * `inner` = -1) or its `inner`-th body statement.
292322 */
293- private predicate switchFlatItem (
294- Go:: ExpressionSwitchStmt sw , Go:: Stmt s , int caseIdx , int inner
295- ) {
323+ private predicate switchFlatItem ( Go:: SwitchStmt sw , Go:: Stmt s , int caseIdx , int inner ) {
296324 s = sw .getCase ( caseIdx ) and inner = - 1
297325 or
298326 s = sw .getCase ( caseIdx ) .getStmt ( inner )
@@ -469,6 +497,11 @@ module GoCfg {
469497 or
470498 notBlankIdent ( n .( Go:: RangeStmt ) .getValue ( ) ) and i = 1
471499 ) and
500+ // The `y := x.(type)` test statement of a type switch is transparent
501+ // (see `skipCfg`): the per-case implicit variables are written at the
502+ // case match nodes (see `IR::TypeSwitchImplicitVariableInstruction`),
503+ // so the guard itself emits no assignment write node.
504+ not n = any ( Go:: TypeSwitchStmt ts ) .getTest ( ) and
472505 tag = "assign:" + i .toString ( )
473506 )
474507 or
@@ -559,14 +592,6 @@ module GoCfg {
559592 not exists ( n .( Go:: SliceExpr ) .getMax ( ) ) and
560593 tag = "implicit-max"
561594 or
562- // Type switch implicit variable
563- exists ( Go:: TypeSwitchStmt ts , Go:: DefineStmt ds |
564- ds = ts .getAssign ( ) and
565- n .( Go:: CaseClause ) = ts .getACase ( ) and
566- exists ( n .( Go:: CaseClause ) .getImplicitlyDeclaredVariable ( ) ) and
567- tag = "type-switch-var"
568- )
569- or
570595 // Literal element initialization
571596 n = any ( Go:: CompositeLit lit ) .getAnElement ( ) and
572597 tag = "lit-init"
@@ -943,7 +968,6 @@ module GoCfg {
943968
944969 predicate step ( PreControlFlowNode n1 , PreControlFlowNode n2 ) {
945970 rangeLoop ( n1 , n2 ) or
946- switchStmt ( n1 , n2 ) or
947971 selectStmt ( n1 , n2 ) or
948972 deferStmt ( n1 , n2 ) or
949973 goStmtStep ( n1 , n2 ) or
@@ -1007,7 +1031,12 @@ module GoCfg {
10071031 assgn instanceof Go:: Assignment and not assgn instanceof Go:: RecvStmt
10081032 or
10091033 assgn instanceof Go:: ValueSpec
1010- )
1034+ ) and
1035+ // The `y := x.(type)` test statement of a type switch is transparent
1036+ // (see `skipCfg`); the shared switch model evaluates the underlying
1037+ // type-assertion expression directly, so this statement has no
1038+ // assignment flow of its own.
1039+ not assgn = any ( Go:: TypeSwitchStmt ts ) .getTest ( )
10111040 |
10121041 // Route through children (LHS names, RHS expressions)
10131042 childSequenceStep ( assgn , n1 , n2 )
@@ -1429,119 +1458,6 @@ module GoCfg {
14291458 )
14301459 }
14311460
1432- private predicate switchStmt ( PreControlFlowNode n1 , PreControlFlowNode n2 ) {
1433- typeSwitch ( n1 , n2 ) or typeCaseClause ( n1 , n2 )
1434- }
1435-
1436- private predicate switchCasesStartOrAfter ( Go:: TypeSwitchStmt sw , PreControlFlowNode n ) {
1437- n .isBefore ( sw .getNonDefaultCase ( 0 ) )
1438- or
1439- not exists ( sw .getANonDefaultCase ( ) ) and n .isBefore ( sw .getDefault ( ) )
1440- or
1441- not exists ( sw .getACase ( ) ) and n .isAfter ( sw )
1442- }
1443-
1444- private predicate typeSwitch ( PreControlFlowNode n1 , PreControlFlowNode n2 ) {
1445- exists ( Go:: TypeSwitchStmt sw |
1446- n1 .isBefore ( sw ) and
1447- (
1448- n2 .isBefore ( sw .getInit ( ) )
1449- or
1450- not exists ( sw .getInit ( ) ) and n2 .isBefore ( sw .getTest ( ) )
1451- )
1452- or
1453- n1 .isAfter ( sw .getInit ( ) ) and n2 .isBefore ( sw .getTest ( ) )
1454- or
1455- n1 .isAfter ( sw .getTest ( ) ) and switchCasesStartOrAfter ( sw , n2 )
1456- )
1457- }
1458-
1459- /**
1460- * Holds if `cc` is a case clause of a type switch with an assignment that
1461- * implicitly declares a variable whose type narrows to the case type. In
1462- * this situation the CFG inserts a `type-switch-var` additional node
1463- * between the case test and the case body, on which the IR layer
1464- * materialises the implicit assignment to that variable.
1465- */
1466- private predicate hasTypeSwitchVar ( Go:: CaseClause cc ) {
1467- exists ( Go:: TypeSwitchStmt ts |
1468- ts .getACase ( ) = cc and
1469- exists ( ts .getAssign ( ) ) and
1470- exists ( cc .getImplicitlyDeclaredVariable ( ) )
1471- )
1472- }
1473-
1474- private predicate typeCaseClause ( PreControlFlowNode n1 , PreControlFlowNode n2 ) {
1475- exists ( Go:: TypeSwitchStmt sw , Go:: CaseClause cc , int i | cc = sw .getNonDefaultCase ( i ) |
1476- n1 .isBefore ( cc ) and n2 .isBefore ( cc .getExpr ( 0 ) )
1477- or
1478- // A type switch is not boolean, so each case type test has a single
1479- // "after" node from which control flows both to the case body (on a
1480- // match) and on to the next test (on a mismatch).
1481- exists ( int j | n1 .isAfter ( cc .getExpr ( j ) ) and n2 .isBefore ( cc .getExpr ( j + 1 ) ) )
1482- or
1483- exists ( int last | last = max ( int j | exists ( cc .getExpr ( j ) ) ) |
1484- n1 .isAfter ( cc .getExpr ( last ) ) and
1485- (
1486- hasTypeSwitchVar ( cc ) and n2 .isAdditional ( cc , "type-switch-var" )
1487- or
1488- not hasTypeSwitchVar ( cc ) and
1489- (
1490- n2 .isBefore ( cc .getStmt ( 0 ) )
1491- or
1492- not exists ( cc .getStmt ( 0 ) ) and n2 .isAfter ( sw )
1493- )
1494- )
1495- or
1496- n1 .isAfter ( cc .getExpr ( last ) ) and
1497- (
1498- n2 .isBefore ( sw .getNonDefaultCase ( i + 1 ) )
1499- or
1500- not exists ( sw .getNonDefaultCase ( i + 1 ) ) and n2 .isBefore ( sw .getDefault ( ) )
1501- or
1502- not exists ( sw .getNonDefaultCase ( i + 1 ) ) and
1503- not exists ( sw .getDefault ( ) ) and
1504- n2 .isAfter ( sw )
1505- )
1506- )
1507- )
1508- or
1509- exists ( Go:: TypeSwitchStmt sw , Go:: CaseClause def | def = sw .getDefault ( ) |
1510- n1 .isBefore ( def ) and
1511- (
1512- hasTypeSwitchVar ( def ) and n2 .isAdditional ( def , "type-switch-var" )
1513- or
1514- not hasTypeSwitchVar ( def ) and
1515- (
1516- n2 .isBefore ( def .getStmt ( 0 ) )
1517- or
1518- not exists ( def .getStmt ( 0 ) ) and n2 .isAfter ( sw )
1519- )
1520- )
1521- )
1522- or
1523- exists ( Go:: TypeSwitchStmt sw , Go:: CaseClause cc |
1524- sw .getACase ( ) = cc and
1525- hasTypeSwitchVar ( cc ) and
1526- n1 .isAdditional ( cc , "type-switch-var" ) and
1527- (
1528- n2 .isBefore ( cc .getStmt ( 0 ) )
1529- or
1530- not exists ( cc .getStmt ( 0 ) ) and n2 .isAfter ( sw )
1531- )
1532- )
1533- or
1534- exists ( Go:: TypeSwitchStmt sw , Go:: CaseClause cc | cc = sw .getACase ( ) |
1535- exists ( int j | n1 .isAfter ( cc .getStmt ( j ) ) and n2 .isBefore ( cc .getStmt ( j + 1 ) ) )
1536- or
1537- exists ( int last |
1538- last = max ( int j | exists ( cc .getStmt ( j ) ) ) and
1539- n1 .isAfter ( cc .getStmt ( last ) ) and
1540- n2 .isAfter ( sw )
1541- )
1542- )
1543- }
1544-
15451461 private predicate commClauseBodyStart (
15461462 Go:: SelectStmt sel , Go:: CommClause cc , PreControlFlowNode n
15471463 ) {
0 commit comments