| // Copyright (c) 2024, the Dart project authors.  Please see the AUTHORS file | 
 | // for details. All rights reserved. Use of this source code is governed by a | 
 | // BSD-style license that can be found in the LICENSE file. | 
 |  | 
 | // Test switch statement "fallthrough" behavior. | 
 | // | 
 | // Test behavior for Dart from version 2.12 until 3.0. | 
 | // @dart=2.19 | 
 | // | 
 | // From Dart 2.12 until Dart 3.0, it's an error if control can reach the end | 
 | // of a case block. This is backed by a competent control flow analysis. | 
 | // | 
 | // From Dart 2.0 until Dart 2.12 there was a warning if a case block did | 
 | // not end with a control flow statement (like `break`). | 
 | // | 
 | // From Dart 3.0 and forward, there is no warning or error. | 
 |  | 
 | // These switch-case statements should not cause static warnings or errors, | 
 | // and also shouldn't in Dart 2.0 until Dart 2.12. | 
 | dynamic testSwitch(int x) { | 
 |   // Catch all control flow leaving the switch. | 
 |   // Run switch in catch clause to check that `rethrow` is control flow. | 
 |   TRY: | 
 |   try { | 
 |     throw x; | 
 |   } catch (_) { | 
 |     // Add loop as break/continue target. | 
 |     LOOP: | 
 |     do { | 
 |       switch (x) { | 
 |         case 0: | 
 |         case 1: | 
 |           nop(x); | 
 |           break; // Break switch. | 
 |         case 2: | 
 |           nop(x); | 
 |           break LOOP; | 
 |         case 3: | 
 |           nop(x); | 
 |           continue; // Continue loop. | 
 |         case 4: | 
 |           nop(x); | 
 |           continue LOOP; | 
 |         case 5: | 
 |           nop(x); | 
 |           continue LAST; | 
 |         case 6: | 
 |           nop(x); | 
 |           return; | 
 |         case 7: | 
 |           nop(x); | 
 |           return x; | 
 |         case 8: | 
 |           nop(x); | 
 |           throw x; | 
 |         case 9: | 
 |           nop(x); | 
 |           rethrow; | 
 |         case 10: | 
 |         case 11: | 
 |           { | 
 |             nop(x); | 
 |             break; | 
 |           } | 
 |         case 12: | 
 |           { | 
 |             nop(x); | 
 |             break LOOP; | 
 |           } | 
 |         case 13: | 
 |           { | 
 |             nop(x); | 
 |             continue; // Continue loop. | 
 |           } | 
 |         case 14: | 
 |           { | 
 |             nop(x); | 
 |             continue LOOP; | 
 |           } | 
 |         case 15: | 
 |           { | 
 |             nop(x); | 
 |             continue LAST; | 
 |           } | 
 |         case 16: | 
 |           { | 
 |             nop(x); | 
 |             return; | 
 |           } | 
 |         case 17: | 
 |           { | 
 |             nop(x); | 
 |             return x; | 
 |           } | 
 |         case 18: | 
 |           { | 
 |             nop(x); | 
 |             throw x; | 
 |           } | 
 |         case 19: | 
 |           { | 
 |             nop(x); | 
 |             rethrow; | 
 |           } | 
 |         LAST: | 
 |         case 20: | 
 |           { | 
 |             nop(x); | 
 |             // Fallthrough allowed on last statements. | 
 |           } | 
 |       } | 
 |     } while (false); | 
 |   } finally { | 
 |     // Catch all control flow leaving the switch and ignore it. | 
 |     // Use break instead of return to avoid warning for `return` and `return e` | 
 |     // in same function. | 
 |     break TRY; | 
 |   } | 
 | } | 
 |  | 
 | // In Dart 2.0, all of these switch cases should cause warnings because the | 
 | // fallthrough analysis was very limited and syntax-based. Null safety | 
 | // includes more precise flow analysis so most of these are now valid code | 
 | // because in fact no fallthrough will occur. | 
 | // | 
 | // The cases that are now recognized as not reaching the end of the case block, | 
 | // with the Dart 2.12 control flow analysis, are still included to ensure | 
 | // that they no longer cause warnings or errors. | 
 | // The ones that are still considered as being able to reach the end | 
 | // are now expected to cause an error. | 
 | // | 
 | // Return type `dynamic` to avoid a warning for either of | 
 | // `return;` or `return x;`. | 
 | dynamic testSwitchWarn(int x) { | 
 |   // Catch all control flow from the switch and ignore it. | 
 |   TRY: | 
 |   try { | 
 |     throw 0; | 
 |   } catch (_) { | 
 |     // Enable `rethrow` as control flow. | 
 |     // Wrap in loop as target for continue/break. | 
 |     LOOP: | 
 |     do { | 
 |       switch (x) { | 
 |         case 0: // Case for same statements as next case. | 
 |         case 1: | 
 |           { | 
 |             // Dart 2.0 looked inside a single nested block, but not two. | 
 |             { | 
 |               nop(x); | 
 |               break; // Break and exit switch. | 
 |             } | 
 |           } | 
 |         case 2: | 
 |           { | 
 |             { | 
 |               nop(x); | 
 |               break LOOP; // Break loop, exits switch (and loop). | 
 |             } | 
 |           } | 
 |         case 3: | 
 |           { | 
 |             { | 
 |               nop(x); | 
 |               continue; // Continue loop, exits switch. | 
 |             } | 
 |           } | 
 |         case 4: | 
 |           { | 
 |             { | 
 |               nop(x); | 
 |               continue LOOP; // Continue loop, exits switch. | 
 |             } | 
 |           } | 
 |         case 5: | 
 |           { | 
 |             { | 
 |               nop(x); | 
 |               continue LAST; // Exits case, goto other case. | 
 |             } | 
 |           } | 
 |         case 6: | 
 |           { | 
 |             { | 
 |               nop(x); | 
 |               return; // Exits function, loop and switch. | 
 |             } | 
 |           } | 
 |         case 7: | 
 |           { | 
 |             { | 
 |               nop(x); | 
 |               return x; // Exits function, loop and switch. | 
 |             } | 
 |           } | 
 |         case 8: | 
 |           { | 
 |             { | 
 |               nop(x); | 
 |               throw x; // Exits switch. | 
 |             } | 
 |           } | 
 |         case 9: | 
 |           { | 
 |             { | 
 |               nop(x); | 
 |               rethrow; // Exits switch. | 
 |             } | 
 |           } | 
 |         case 10: | 
 |         // [error column 9, length 4] | 
 |         // [analyzer] COMPILE_TIME_ERROR.SWITCH_CASE_COMPLETES_NORMALLY | 
 |         // [cfe] Switch case may fall through to the next case. | 
 |           while (true) break; // Breaks loop, reaches end. | 
 |         case 11: | 
 |         // [error column 9, length 4] | 
 |         // [analyzer] COMPILE_TIME_ERROR.SWITCH_CASE_COMPLETES_NORMALLY | 
 |         // [cfe] Switch case may fall through to the next case. | 
 |           do break; while (true); // Breaks loop, reaches end. | 
 |         case 12: | 
 |         // [error column 9, length 4] | 
 |         // [analyzer] COMPILE_TIME_ERROR.SWITCH_CASE_COMPLETES_NORMALLY | 
 |         // [cfe] Switch case may fall through to the next case. | 
 |           for (;;) break; // Breaks loop, reaches end. | 
 |         case 13: | 
 |         // [error column 9, length 4] | 
 |         // [analyzer] COMPILE_TIME_ERROR.SWITCH_CASE_COMPLETES_NORMALLY | 
 |         // [cfe] Switch case may fall through to the next case. | 
 |           for (var _ in []) break LOOP; // Can be empty, reaches end. | 
 |         case 14: | 
 |           if (x.isEven) { | 
 |             break; // Breaks and exits switch. | 
 |           } else { | 
 |             break LOOP; // Breaks loop and exits switch. | 
 |           } | 
 |         case 15: | 
 |           (throw 0); // Exits switch. | 
 |         case 16: | 
 |         // [error column 9, length 4] | 
 |         // [analyzer] COMPILE_TIME_ERROR.SWITCH_CASE_COMPLETES_NORMALLY | 
 |         // [cfe] Switch case may fall through to the next case. | 
 |           nop(x); // Reaches end. | 
 |         case 17: | 
 |           L: | 
 |           break; // Breaks and exits switch. | 
 |         case 18: | 
 |         // [error column 9, length 4] | 
 |         // [analyzer] COMPILE_TIME_ERROR.SWITCH_CASE_COMPLETES_NORMALLY | 
 |         // [cfe] Switch case may fall through to the next case. | 
 |           L: | 
 |           break L; // Breaks break statement only, reaches end. | 
 |         case 19: | 
 |         // [error column 9, length 4] | 
 |         // [cfe] Switch case may fall through to the next case. | 
 |         // [analyzer] COMPILE_TIME_ERROR.SWITCH_CASE_COMPLETES_NORMALLY | 
 |           L: | 
 |           if (x.isOdd) { | 
 |             break L; // Breaks if, reaches end. | 
 |           } else { | 
 |             break LOOP; | 
 |           } | 
 |  | 
 |         // The type `Never` is new in Dart 2.12. These would be warnings | 
 |         // with any pre-2.12 type. | 
 |         case 20: | 
 |           notEver(x); // Type Never implies not completing. | 
 |         case 21: { | 
 |           notEver(x); | 
 |         } | 
 |         case 22: { | 
 |           { | 
 |             notEver(x); | 
 |           } | 
 |         } | 
 |         case 23: { | 
 |           do { | 
 |             notEver(x); | 
 |           } while (true); | 
 |         } | 
 |         case 24: { | 
 |           if (x.isEven) { | 
 |             notEver(x); | 
 |           } else { | 
 |             notEver(-x); | 
 |           } | 
 |         } | 
 |         case 25: | 
 |           nop(x.isEven ? notEver(x) : notEver(-x)); | 
 |         LAST: | 
 |         case 99: | 
 |         // Last case can't cause static warning. | 
 |       } | 
 |     } while (false); | 
 |   } finally { | 
 |     // Catch all control flow leaving the switch and ignore it. | 
 |     break TRY; | 
 |   } | 
 | } | 
 |  | 
 | Never notEver(int x) { | 
 |   if (x < 0) { | 
 |     notEver(x + 1); | 
 |   } else { | 
 |     throw AssertionError("Never"); | 
 |   } | 
 | } | 
 |  | 
 | main() { | 
 |   // Don't let compiler optimize for a specific value. | 
 |   for (int i = 0; i <= 100; i++) { | 
 |     testSwitch(i); | 
 |     testSwitchWarn(i); | 
 |   } | 
 | } | 
 |  | 
 | /// Don't make it obvious that a switch case isn't doing anything. | 
 | void nop(Object? x) {} |