c9d54c12f6
Change-Id: I3c5cfbc1bc5e41d2ef1598427067dea00f9f42b4 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/425403 Reviewed-by: Lasse Nielsen <lrn@google.com> Commit-Queue: Lasse Nielsen <lrn@google.com> Auto-Submit: Bob Nystrom <rnystrom@google.com>
322 lines
8.4 KiB
Dart
322 lines
8.4 KiB
Dart
// 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) {}
|