@@ -489,14 +489,14 @@ private module ControlFlowGraphImpl {
489
489
private Stmt getSwitchStatement ( SwitchBlock switch , int i ) { result .isNthChildOf ( switch , i ) }
490
490
491
491
/**
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 ,
493
493
* immediately before either falling through to execute successor statements or execute a rule body
494
494
* if present. `completion` is the completion kind of the last operation.
495
495
*/
496
496
private predicate lastPatternCaseMatchingOp (
497
497
PatternCase pc , ControlFlowNode last , Completion completion
498
498
) {
499
- last ( pc .getPattern ( ) , last , completion ) and
499
+ last ( pc .getAPattern ( ) , last , completion ) and
500
500
completion = NormalCompletion ( ) and
501
501
not exists ( pc .getGuard ( ) )
502
502
or
@@ -776,6 +776,18 @@ private module ControlFlowGraphImpl {
776
776
last ( try .getFinally ( ) , last , NormalCompletion ( ) )
777
777
}
778
778
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
+ if immediateSucc instanceof PatternCase
785
+ then isNextNormalSwitchStmt ( switch , immediateSucc , succ )
786
+ else succ = immediateSucc
787
+ )
788
+ )
789
+ }
790
+
779
791
/**
780
792
* Bind `last` to a cfg node nested inside `n` (or, indeed, `n` itself) such
781
793
* that `last` may be the last node during an execution of `n` and finish
@@ -927,9 +939,15 @@ private module ControlFlowGraphImpl {
927
939
completion != anonymousBreakCompletion ( ) and
928
940
not completion instanceof NormalOrBooleanCompletion
929
941
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 ( )
942
+ // if a statement without a non-pattern-case successor completes normally (or for a pattern case
943
+ // the guard succeeds) then the switch completes normally.
944
+ exists ( Stmt lastNormalStmt , Completion stmtCompletion |
945
+ lastNormalStmt = getSwitchStatement ( switch , _) and
946
+ not isNextNormalSwitchStmt ( switch , lastNormalStmt , _) and
947
+ last ( lastNormalStmt , last , stmtCompletion ) and
948
+ ( stmtCompletion = NormalCompletion ( ) or stmtCompletion = BooleanCompletion ( true , _) ) and
949
+ completion = NormalCompletion ( )
950
+ )
933
951
or
934
952
// if no default case exists, then normal completion of the expression may terminate the switch
935
953
// Note this can't happen if there are pattern cases or a null literal, as
@@ -973,9 +991,9 @@ private module ControlFlowGraphImpl {
973
991
)
974
992
or
975
993
// A pattern case statement can complete:
976
- // * On failure of its type test (boolean false)
994
+ // * On failure of its final type test (boolean false)
977
995
// * 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)
996
+ // * On completion of one of its pattern variable declarations, if it is not a rule and has no guard (normal completion)
979
997
// * On success of its guard test, if it is not a rule (boolean true)
980
998
// (the latter two cases are accounted for by lastPatternCaseMatchingOp)
981
999
exists ( PatternCase pc | n = pc |
@@ -1315,9 +1333,13 @@ private module ControlFlowGraphImpl {
1315
1333
// Note this includes non-rule case statements and the successful pattern match successor
1316
1334
// of a non-rule pattern case statement. Rule case statements do not complete normally
1317
1335
// (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
1336
+ // Exception: falling through into a pattern case statement (which necessarily does not
1337
+ // declare any named variables) must skip one or more such statements, otherwise we would
1338
+ // incorrectly apply their type test and/or guard.
1339
+ exists ( Stmt pred , Stmt succ |
1340
+ isNextNormalSwitchStmt ( switch , pred , succ ) and
1341
+ last ( pred , n , completion ) and
1342
+ result = first ( succ ) and
1321
1343
( completion = NormalCompletion ( ) or completion = BooleanCompletion ( true , _) )
1322
1344
)
1323
1345
or
@@ -1328,16 +1350,19 @@ private module ControlFlowGraphImpl {
1328
1350
)
1329
1351
or
1330
1352
// Pattern cases have internal edges:
1331
- // * Type test success -true-> variable declarations
1353
+ // * Type test success -true-> one of the possible sets of variable declarations
1354
+ // n.b. for unnamed patterns (e.g. case A _, B _) this means that *one* of the
1355
+ // type tests has succeeded. There aren't enough nodes in the AST to describe
1356
+ // a sequential test in detail, so CFG consumers have to watch out for this case.
1332
1357
// * Variable declarations -normal-> guard evaluation
1333
1358
// * Variable declarations -normal-> rule execution (when there is no guard)
1334
1359
// * Guard success -true-> rule execution
1335
1360
exists ( PatternCase pc |
1336
1361
n = pc and
1337
1362
completion = basicBooleanCompletion ( true ) and
1338
- result = first ( pc .getPattern ( ) )
1363
+ result = first ( pc .getAPattern ( ) )
1339
1364
or
1340
- last ( pc .getPattern ( ) , n , completion ) and
1365
+ last ( pc .getAPattern ( ) , n , completion ) and
1341
1366
completion = NormalCompletion ( ) and
1342
1367
result = first ( pc .getGuard ( ) )
1343
1368
or
0 commit comments