@@ -67,6 +67,13 @@ module GoCfg {
6767 e = any ( Go:: ImportSpec is ) .getPathExpr ( )
6868 or
6969 e .getParent * ( ) = any ( Go:: ArrayTypeExpr ate ) .getLength ( )
70+ 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 ( )
7077 }
7178
7279 AstNode getChild ( AstNode n , int index ) {
@@ -217,27 +224,68 @@ module GoCfg {
217224 }
218225
219226 class Switch extends AstNode {
220- Switch ( ) { none ( ) }
221-
222- Expr getExpr ( ) { none ( ) }
223-
224- Case getCase ( int index ) { none ( ) }
225-
226- Stmt getStmt ( int index ) { none ( ) }
227+ Switch ( ) { this instanceof Go:: ExpressionSwitchStmt }
228+
229+ Expr getExpr ( ) { result = this .( Go:: ExpressionSwitchStmt ) .getExpr ( ) }
230+
231+ Case getCase ( int index ) { result = this .( Go:: ExpressionSwitchStmt ) .getCase ( index ) }
232+
233+ Stmt getStmt ( int index ) {
234+ // Go nests each case clause's body statements under the clause rather
235+ // than in a flat list, so we expose a flattened view in which every
236+ // case clause is immediately followed by its own body statements. This
237+ // lets the shared library compute the body of a case as the statements
238+ // between it and the next clause.
239+ result =
240+ rank [ index + 1 ] ( Go:: Stmt s , int caseIdx , int inner |
241+ switchFlatItem ( this , s , caseIdx , inner )
242+ |
243+ s order by caseIdx , inner
244+ )
245+ }
227246 }
228247
229248 class Case extends AstNode {
230- Case ( ) { none ( ) }
249+ Case ( ) { this = any ( Go :: ExpressionSwitchStmt sw ) . getACase ( ) }
231250
232- AstNode getPattern ( int index ) { none ( ) }
251+ AstNode getPattern ( int index ) { result = this . ( Go :: CaseClause ) . getExpr ( index ) }
233252
234253 Expr getGuard ( ) { none ( ) }
235254
236255 AstNode getBody ( ) { none ( ) }
237256 }
238257
239258 class DefaultCase extends Case {
240- DefaultCase ( ) { none ( ) }
259+ DefaultCase ( ) { not exists ( this .( Go:: CaseClause ) .getExpr ( _) ) }
260+ }
261+
262+ /** Gets the initializer of `switch` statement `switch`, if any. */
263+ AstNode getSwitchInit ( Switch switch ) { result = switch .( Go:: ExpressionSwitchStmt ) .getInit ( ) }
264+
265+ /**
266+ * Go has no implicit fall-through between case clauses; a case that runs to
267+ * the end of its body breaks out of the switch. Fall-through only happens
268+ * when the case body ends with an explicit `fallthrough` statement, in
269+ * which case control transfers to the next case clause's body (in source
270+ * order). The shared library models this by chaining the body of such a
271+ * case to the body of the following case.
272+ */
273+ predicate fallsThrough ( Case c ) {
274+ c .( Go:: CaseClause ) .getStmt ( max ( int i | exists ( c .( Go:: CaseClause ) .getStmt ( i ) ) ) ) instanceof
275+ Go:: FallthroughStmt
276+ }
277+
278+ /**
279+ * Holds if `s` is the flattened body element at position (`caseIdx`,
280+ * `inner`) of expression switch `sw`: either the `caseIdx`-th case clause
281+ * itself (with `inner` = -1) or its `inner`-th body statement.
282+ */
283+ private predicate switchFlatItem (
284+ Go:: ExpressionSwitchStmt sw , Go:: Stmt s , int caseIdx , int inner
285+ ) {
286+ s = sw .getCase ( caseIdx ) and inner = - 1
287+ or
288+ s = sw .getCase ( caseIdx ) .getStmt ( inner )
241289 }
242290
243291 class ConditionalExpr extends Expr {
@@ -315,16 +363,10 @@ module GoCfg {
315363
316364 class CallableContext = Void ;
317365
318- private newtype TLabel =
319- TGoLabel ( string l ) { exists ( Go:: LabeledStmt ls | l = ls .getLabel ( ) ) } or
320- TFallthrough ( )
366+ private newtype TLabel = TGoLabel ( string l ) { exists ( Go:: LabeledStmt ls | l = ls .getLabel ( ) ) }
321367
322368 class Label extends TLabel {
323- string toString ( ) {
324- exists ( string l | this = TGoLabel ( l ) and result = l )
325- or
326- this = TFallthrough ( ) and result = "fallthrough"
327- }
369+ string toString ( ) { exists ( string l | this = TGoLabel ( l ) and result = l ) }
328370 }
329371
330372 private Label getLabelOfStmt ( Go:: Stmt s ) {
@@ -339,19 +381,11 @@ module GoCfg {
339381 l = TGoLabel ( n .( Go:: BreakStmt ) .getLabel ( ) )
340382 or
341383 l = TGoLabel ( n .( Go:: ContinueStmt ) .getLabel ( ) )
342- or
343- l = TFallthrough ( ) and n instanceof Go:: FallthroughStmt
344384 }
345385
346386 predicate inConditionalContext ( Ast:: AstNode n , ConditionKind kind ) {
347387 kind .isBoolean ( ) and
348- (
349- n = any ( Go:: ForStmt fs ) .getCond ( )
350- or
351- exists ( Go:: ExpressionSwitchStmt ess |
352- not exists ( ess .getExpr ( ) ) and n = ess .getACase ( ) .( Go:: CaseClause ) .getExpr ( _)
353- )
354- )
388+ n = any ( Go:: ForStmt fs ) .getCond ( )
355389 }
356390
357391 predicate preOrderExpr ( Ast:: Expr e ) {
@@ -515,17 +549,6 @@ module GoCfg {
515549 not exists ( n .( Go:: SliceExpr ) .getMax ( ) ) and
516550 tag = "implicit-max"
517551 or
518- // Implicit true in tagless switch
519- n instanceof Go:: ExpressionSwitchStmt and
520- not exists ( n .( Go:: ExpressionSwitchStmt ) .getExpr ( ) ) and
521- tag = "implicit-true"
522- or
523- // Case check nodes
524- exists ( int i |
525- exists ( n .( Go:: CaseClause ) .getExpr ( i ) ) and
526- tag = "case-check:" + i .toString ( )
527- )
528- or
529552 // Type switch implicit variable
530553 exists ( Go:: TypeSwitchStmt ts , Go:: DefineStmt ds |
531554 ds = ts .getAssign ( ) and
@@ -709,12 +732,6 @@ module GoCfg {
709732 c .asSimpleAbruptCompletion ( ) instanceof ExceptionSuccessor and
710733 always = false
711734 or
712- ast instanceof Go:: FallthroughStmt and
713- n .injects ( ast ) and
714- c .getSuccessorType ( ) instanceof BreakSuccessor and
715- c .hasLabel ( TFallthrough ( ) ) and
716- always = true
717- or
718735 ast instanceof Go:: GotoStmt and
719736 n .injects ( ast ) and
720737 c .getSuccessorType ( ) instanceof GotoSuccessor and
@@ -1403,42 +1420,17 @@ module GoCfg {
14031420 }
14041421
14051422 private predicate switchStmt ( PreControlFlowNode n1 , PreControlFlowNode n2 ) {
1406- exprSwitch ( n1 , n2 ) or typeSwitch ( n1 , n2 ) or caseClause ( n1 , n2 )
1423+ typeSwitch ( n1 , n2 ) or typeCaseClause ( n1 , n2 )
14071424 }
14081425
1409- private predicate switchCasesStartOrAfter ( Go:: SwitchStmt sw , PreControlFlowNode n ) {
1426+ private predicate switchCasesStartOrAfter ( Go:: TypeSwitchStmt sw , PreControlFlowNode n ) {
14101427 n .isBefore ( sw .getNonDefaultCase ( 0 ) )
14111428 or
14121429 not exists ( sw .getANonDefaultCase ( ) ) and n .isBefore ( sw .getDefault ( ) )
14131430 or
14141431 not exists ( sw .getACase ( ) ) and n .isAfter ( sw )
14151432 }
14161433
1417- private predicate exprSwitch ( PreControlFlowNode n1 , PreControlFlowNode n2 ) {
1418- exists ( Go:: ExpressionSwitchStmt sw |
1419- n1 .isBefore ( sw ) and
1420- (
1421- n2 .isBefore ( sw .getInit ( ) )
1422- or
1423- not exists ( sw .getInit ( ) ) and
1424- (
1425- n2 .isBefore ( sw .getExpr ( ) )
1426- or
1427- not exists ( sw .getExpr ( ) ) and switchCasesStartOrAfter ( sw , n2 )
1428- )
1429- )
1430- or
1431- n1 .isAfter ( sw .getInit ( ) ) and
1432- (
1433- n2 .isBefore ( sw .getExpr ( ) )
1434- or
1435- not exists ( sw .getExpr ( ) ) and switchCasesStartOrAfter ( sw , n2 )
1436- )
1437- or
1438- n1 .isAfter ( sw .getExpr ( ) ) and switchCasesStartOrAfter ( sw , n2 )
1439- )
1440- }
1441-
14421434 private predicate typeSwitch ( PreControlFlowNode n1 , PreControlFlowNode n2 ) {
14431435 exists ( Go:: TypeSwitchStmt sw |
14441436 n1 .isBefore ( sw ) and
@@ -1454,46 +1446,6 @@ module GoCfg {
14541446 )
14551447 }
14561448
1457- /**
1458- * Holds if `sw` is a tagless expression switch, in which case the case
1459- * expressions are themselves the boolean conditions being tested and are
1460- * therefore in a boolean conditional context.
1461- */
1462- private predicate isBooleanSwitch ( Go:: SwitchStmt sw ) {
1463- sw instanceof Go:: ExpressionSwitchStmt and
1464- not exists ( sw .( Go:: ExpressionSwitchStmt ) .getExpr ( ) )
1465- }
1466-
1467- /**
1468- * Holds if `n` is the control-flow node immediately after evaluating case
1469- * expression `caseExpr` of switch `sw` on the branch where `caseExpr`
1470- * matches.
1471- */
1472- private predicate afterCaseExprMatch ( Go:: SwitchStmt sw , Go:: Expr caseExpr , PreControlFlowNode n ) {
1473- caseExpr = sw .getACase ( ) .( Go:: CaseClause ) .getAnExpr ( ) and
1474- (
1475- isBooleanSwitch ( sw ) and n .isAfterTrue ( caseExpr )
1476- or
1477- not isBooleanSwitch ( sw ) and n .isAfter ( caseExpr )
1478- )
1479- }
1480-
1481- /**
1482- * Holds if `n` is the control-flow node immediately after evaluating case
1483- * expression `caseExpr` of switch `sw` on the branch where `caseExpr`
1484- * does not match.
1485- */
1486- private predicate afterCaseExprNoMatch (
1487- Go:: SwitchStmt sw , Go:: Expr caseExpr , PreControlFlowNode n
1488- ) {
1489- caseExpr = sw .getACase ( ) .( Go:: CaseClause ) .getAnExpr ( ) and
1490- (
1491- isBooleanSwitch ( sw ) and n .isAfterFalse ( caseExpr )
1492- or
1493- not isBooleanSwitch ( sw ) and n .isAfter ( caseExpr )
1494- )
1495- }
1496-
14971449 /**
14981450 * Holds if `cc` is a case clause of a type switch with an assignment that
14991451 * implicitly declares a variable whose type narrows to the case type. In
@@ -1509,19 +1461,17 @@ module GoCfg {
15091461 )
15101462 }
15111463
1512- private predicate caseClause ( PreControlFlowNode n1 , PreControlFlowNode n2 ) {
1513- exists ( Go:: SwitchStmt sw , Go:: CaseClause cc , int i | cc = sw .getNonDefaultCase ( i ) |
1464+ private predicate typeCaseClause ( PreControlFlowNode n1 , PreControlFlowNode n2 ) {
1465+ exists ( Go:: TypeSwitchStmt sw , Go:: CaseClause cc , int i | cc = sw .getNonDefaultCase ( i ) |
15141466 n1 .isBefore ( cc ) and n2 .isBefore ( cc .getExpr ( 0 ) )
15151467 or
1516- // For a tagless expression switch the case expressions are themselves
1517- // booleans in a conditional context, so we only fall through to the
1518- // next case expression on the false branch.
1519- exists ( int j |
1520- afterCaseExprNoMatch ( sw , cc .getExpr ( j ) , n1 ) and n2 .isBefore ( cc .getExpr ( j + 1 ) )
1521- )
1468+ // A type switch is not boolean, so each case type test has a single
1469+ // "after" node from which control flows both to the case body (on a
1470+ // match) and on to the next test (on a mismatch).
1471+ exists ( int j | n1 .isAfter ( cc .getExpr ( j ) ) and n2 .isBefore ( cc .getExpr ( j + 1 ) ) )
15221472 or
15231473 exists ( int last | last = max ( int j | exists ( cc .getExpr ( j ) ) ) |
1524- afterCaseExprMatch ( sw , cc .getExpr ( last ) , n1 ) and
1474+ n1 . isAfter ( cc .getExpr ( last ) ) and
15251475 (
15261476 hasTypeSwitchVar ( cc ) and n2 .isAdditional ( cc , "type-switch-var" )
15271477 or
@@ -1533,7 +1483,7 @@ module GoCfg {
15331483 )
15341484 )
15351485 or
1536- afterCaseExprNoMatch ( sw , cc .getExpr ( last ) , n1 ) and
1486+ n1 . isAfter ( cc .getExpr ( last ) ) and
15371487 (
15381488 n2 .isBefore ( sw .getNonDefaultCase ( i + 1 ) )
15391489 or
@@ -1546,7 +1496,7 @@ module GoCfg {
15461496 )
15471497 )
15481498 or
1549- exists ( Go:: SwitchStmt sw , Go:: CaseClause def | def = sw .getDefault ( ) |
1499+ exists ( Go:: TypeSwitchStmt sw , Go:: CaseClause def | def = sw .getDefault ( ) |
15501500 n1 .isBefore ( def ) and
15511501 (
15521502 hasTypeSwitchVar ( def ) and n2 .isAdditional ( def , "type-switch-var" )
@@ -1560,7 +1510,7 @@ module GoCfg {
15601510 )
15611511 )
15621512 or
1563- exists ( Go:: SwitchStmt sw , Go:: CaseClause cc |
1513+ exists ( Go:: TypeSwitchStmt sw , Go:: CaseClause cc |
15641514 sw .getACase ( ) = cc and
15651515 hasTypeSwitchVar ( cc ) and
15661516 n1 .isAdditional ( cc , "type-switch-var" ) and
@@ -1571,11 +1521,10 @@ module GoCfg {
15711521 )
15721522 )
15731523 or
1574- exists ( Go:: CaseClause cc |
1524+ exists ( Go:: TypeSwitchStmt sw , Go :: CaseClause cc | cc = sw . getACase ( ) |
15751525 exists ( int j | n1 .isAfter ( cc .getStmt ( j ) ) and n2 .isBefore ( cc .getStmt ( j + 1 ) ) )
15761526 or
1577- exists ( Go:: SwitchStmt sw , int last |
1578- sw .getACase ( ) = cc and
1527+ exists ( int last |
15791528 last = max ( int j | exists ( cc .getStmt ( j ) ) ) and
15801529 n1 .isAfter ( cc .getStmt ( last ) ) and
15811530 n2 .isAfter ( sw )
0 commit comments