@@ -489,14 +489,14 @@ private module ControlFlowGraphImpl {
489489 private Stmt getSwitchStatement ( SwitchBlock switch , int i ) { result .isNthChildOf ( switch , i ) }
490490
491491 /**
492- * Holds if `last` is the last node in a pattern case `pc`'s succeeding bind-and-test operation ,
492+ * Holds if `last` is the last node in any of pattern case `pc`'s succeeding bind-and-test operations ,
493493 * immediately before either falling through to execute successor statements or execute a rule body
494494 * if present. `completion` is the completion kind of the last operation.
495495 */
496496 private predicate lastPatternCaseMatchingOp (
497497 PatternCase pc , ControlFlowNode last , Completion completion
498498 ) {
499- last ( pc .getPattern ( ) , last , completion ) and
499+ last ( pc .getAPattern ( ) , last , completion ) and
500500 completion = NormalCompletion ( ) and
501501 not exists ( pc .getGuard ( ) )
502502 or
@@ -776,6 +776,19 @@ private module ControlFlowGraphImpl {
776776 last ( try .getFinally ( ) , last , NormalCompletion ( ) )
777777 }
778778
779+ private predicate isNextNormalSwitchStmt ( SwitchBlock switch , Stmt pred , Stmt succ ) {
780+ exists ( int i , Stmt immediateSucc |
781+ getSwitchStatement ( switch , i ) = pred and
782+ getSwitchStatement ( switch , i + 1 ) = immediateSucc and
783+ (
784+ succ = immediateSucc and
785+ not immediateSucc instanceof PatternCase
786+ or
787+ isNextNormalSwitchStmt ( switch , immediateSucc , succ )
788+ )
789+ )
790+ }
791+
779792 /**
780793 * Bind `last` to a cfg node nested inside `n` (or, indeed, `n` itself) such
781794 * that `last` may be the last node during an execution of `n` and finish
@@ -927,9 +940,15 @@ private module ControlFlowGraphImpl {
927940 completion != anonymousBreakCompletion ( ) and
928941 not completion instanceof NormalOrBooleanCompletion
929942 or
930- // if the last case completes normally, then so does the switch
931- last ( switch .getStmt ( strictcount ( switch .getAStmt ( ) ) - 1 ) , last , NormalCompletion ( ) ) and
932- completion = NormalCompletion ( )
943+ // if a statement without a non-pattern-case successor completes normally (or for a pattern case
944+ // the guard succeeds) then the switch completes normally.
945+ exists ( Stmt lastNormalStmt , Completion stmtCompletion |
946+ lastNormalStmt = getSwitchStatement ( switch , _) and
947+ not isNextNormalSwitchStmt ( switch , lastNormalStmt , _) and
948+ last ( lastNormalStmt , last , stmtCompletion ) and
949+ ( stmtCompletion = NormalCompletion ( ) or stmtCompletion = BooleanCompletion ( true , _) ) and
950+ completion = NormalCompletion ( )
951+ )
933952 or
934953 // if no default case exists, then normal completion of the expression may terminate the switch
935954 // Note this can't happen if there are pattern cases or a null literal, as
@@ -973,9 +992,9 @@ private module ControlFlowGraphImpl {
973992 )
974993 or
975994 // A pattern case statement can complete:
976- // * On failure of its type test (boolean false)
995+ // * On failure of its final type test (boolean false)
977996 // * On failure of its guard test if any (boolean false)
978- // * On completion of its variable declarations, if it is not a rule and has no guard (normal completion)
997+ // * On completion of one of its pattern variable declarations, if it is not a rule and has no guard (normal completion)
979998 // * On success of its guard test, if it is not a rule (boolean true)
980999 // (the latter two cases are accounted for by lastPatternCaseMatchingOp)
9811000 exists ( PatternCase pc | n = pc |
@@ -1315,9 +1334,13 @@ private module ControlFlowGraphImpl {
13151334 // Note this includes non-rule case statements and the successful pattern match successor
13161335 // of a non-rule pattern case statement. Rule case statements do not complete normally
13171336 // (they always break or yield).
1318- exists ( int i |
1319- last ( getSwitchStatement ( switch , i ) , n , completion ) and
1320- result = first ( getSwitchStatement ( switch , i + 1 ) ) and
1337+ // Exception: falling through into a pattern case statement (which necessarily does not
1338+ // declare any named variables) must skip one or more such statements, otherwise we would
1339+ // incorrectly apply their type test and/or guard.
1340+ exists ( Stmt pred , Stmt succ |
1341+ isNextNormalSwitchStmt ( switch , pred , succ ) and
1342+ last ( pred , n , completion ) and
1343+ result = first ( succ ) and
13211344 ( completion = NormalCompletion ( ) or completion = BooleanCompletion ( true , _) )
13221345 )
13231346 or
@@ -1328,16 +1351,19 @@ private module ControlFlowGraphImpl {
13281351 )
13291352 or
13301353 // Pattern cases have internal edges:
1331- // * Type test success -true-> variable declarations
1354+ // * Type test success -true-> one of the possible sets of variable declarations
1355+ // n.b. for unnamed patterns (e.g. case A _, B _) this means that *one* of the
1356+ // type tests has succeeded. There aren't enough nodes in the AST to describe
1357+ // a sequential test in detail, so CFG consumers have to watch out for this case.
13321358 // * Variable declarations -normal-> guard evaluation
13331359 // * Variable declarations -normal-> rule execution (when there is no guard)
13341360 // * Guard success -true-> rule execution
13351361 exists ( PatternCase pc |
13361362 n = pc and
13371363 completion = basicBooleanCompletion ( true ) and
1338- result = first ( pc .getPattern ( ) )
1364+ result = first ( pc .getAPattern ( ) )
13391365 or
1340- last ( pc .getPattern ( ) , n , completion ) and
1366+ last ( pc .getAPattern ( ) , n , completion ) and
13411367 completion = NormalCompletion ( ) and
13421368 result = first ( pc .getGuard ( ) )
13431369 or
0 commit comments