mirror of
https://github.com/dart-lang/sdk
synced 2024-09-19 14:32:49 +00:00
Issue 40392. Report SWITCH_CASE_COMPLETES_NORMALLY when switch/case completes normally.
Bug: https://github.com/dart-lang/sdk/issues/40392 Change-Id: I15c80d0667be1c783cb21870b9f92ee98a4f3042 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/134245 Commit-Queue: Konstantin Shcheglov <scheglov@google.com> Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
This commit is contained in:
parent
bb24f98616
commit
a1d232a9b4
|
@ -303,6 +303,7 @@ const List<ErrorCode> errorCodeValues = [
|
|||
CompileTimeErrorCode.SUPER_IN_EXTENSION,
|
||||
CompileTimeErrorCode.SUPER_IN_INVALID_CONTEXT,
|
||||
CompileTimeErrorCode.SUPER_IN_REDIRECTING_CONSTRUCTOR,
|
||||
CompileTimeErrorCode.SWITCH_CASE_COMPLETES_NORMALLY,
|
||||
CompileTimeErrorCode.TYPE_ALIAS_CANNOT_REFERENCE_ITSELF,
|
||||
CompileTimeErrorCode.TYPE_ARGUMENT_NOT_MATCHING_BOUNDS,
|
||||
// ignore: deprecated_member_use_from_same_package
|
||||
|
|
|
@ -5096,6 +5096,16 @@ class CompileTimeErrorCode extends AnalyzerErrorCode {
|
|||
CompileTimeErrorCode('SUPER_INITIALIZER_IN_OBJECT',
|
||||
"The class 'Object' can't invoke a constructor from a superclass.");
|
||||
|
||||
/// It is an error if any case of a switch statement except the last case
|
||||
/// (the default case if present) may complete normally. The previous
|
||||
/// syntactic restriction requiring the last statement of each case to be
|
||||
/// one of an enumerated list of statements (break, continue, return,
|
||||
/// throw, or rethrow) is removed.
|
||||
static const CompileTimeErrorCode SWITCH_CASE_COMPLETES_NORMALLY =
|
||||
CompileTimeErrorCode('SWITCH_CASE_COMPLETES_NORMALLY',
|
||||
"The 'case' should not complete normally.",
|
||||
correction: "Try adding 'break', or 'return', etc.");
|
||||
|
||||
/**
|
||||
* Parameters:
|
||||
* 0: the name of the type used in the instance creation that should be
|
||||
|
|
|
@ -1885,6 +1885,8 @@ class ErrorVerifier extends RecursiveAstVisitor<void> {
|
|||
* See [StaticWarningCode.CASE_BLOCK_NOT_TERMINATED].
|
||||
*/
|
||||
void _checkForCaseBlocksNotTerminated(SwitchStatement statement) {
|
||||
if (_isNonNullableByDefault) return;
|
||||
|
||||
NodeList<SwitchMember> members = statement.members;
|
||||
int lastMember = members.length - 1;
|
||||
for (int i = 0; i < lastMember; i++) {
|
||||
|
|
|
@ -1692,6 +1692,17 @@ class ResolverVisitor extends ScopedVisitor {
|
|||
InferenceContext.setType(
|
||||
node.expression, _enclosingSwitchStatementExpressionType);
|
||||
super.visitSwitchCase(node);
|
||||
|
||||
var flow = _flowAnalysis?.flow;
|
||||
if (flow != null && flow.isReachable) {
|
||||
var switchStatement = node.parent as SwitchStatement;
|
||||
if (switchStatement.members.last != node && node.statements.isNotEmpty) {
|
||||
errorReporter.reportErrorForToken(
|
||||
CompileTimeErrorCode.SWITCH_CASE_COMPLETES_NORMALLY,
|
||||
node.keyword,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
|
@ -700,42 +700,6 @@ f() {
|
|||
]);
|
||||
}
|
||||
|
||||
test_caseBlockNotTerminated() async {
|
||||
await assertNoErrorsInCode(r'''
|
||||
f(int p) {
|
||||
for (int i = 0; i < 10; i++) {
|
||||
switch (p) {
|
||||
case 0:
|
||||
break;
|
||||
case 1:
|
||||
continue;
|
||||
case 2:
|
||||
return;
|
||||
case 3:
|
||||
throw new Object();
|
||||
case 4:
|
||||
case 5:
|
||||
return;
|
||||
case 6:
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
''');
|
||||
}
|
||||
|
||||
test_caseBlockNotTerminated_lastCase() async {
|
||||
await assertNoErrorsInCode(r'''
|
||||
f(int p) {
|
||||
switch (p) {
|
||||
case 0:
|
||||
p = p + 1;
|
||||
}
|
||||
}
|
||||
''');
|
||||
}
|
||||
|
||||
test_class_type_alias_documentationComment() async {
|
||||
await assertNoErrorsInCode('''
|
||||
/**
|
||||
|
|
|
@ -15,17 +15,96 @@ main() {
|
|||
|
||||
@reflectiveTest
|
||||
class CaseBlockNotTerminatedTest extends DriverResolutionTest {
|
||||
test_caseBlockNotTerminated() async {
|
||||
await assertErrorsInCode('''
|
||||
f(int p) {
|
||||
switch (p) {
|
||||
test_lastCase() async {
|
||||
await assertNoErrorsInCode(r'''
|
||||
f(int a) {
|
||||
switch (a) {
|
||||
case 0:
|
||||
f(p);
|
||||
case 1:
|
||||
break;
|
||||
}
|
||||
}''', [
|
||||
error(StaticWarningCode.CASE_BLOCK_NOT_TERMINATED, 30, 4),
|
||||
]);
|
||||
print(0);
|
||||
}
|
||||
}
|
||||
''');
|
||||
}
|
||||
|
||||
test_notTerminated() async {
|
||||
await assertErrorsInCode('''
|
||||
void f(int a) {
|
||||
switch (a) {
|
||||
case 0:
|
||||
print(0);
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}''', [
|
||||
error(StaticWarningCode.CASE_BLOCK_NOT_TERMINATED, 35, 4),
|
||||
]);
|
||||
}
|
||||
|
||||
test_terminated_break() async {
|
||||
await assertNoErrorsInCode(r'''
|
||||
void f(int a) {
|
||||
switch (a) {
|
||||
case 0:
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
''');
|
||||
}
|
||||
|
||||
test_terminated_continue_loop() async {
|
||||
await assertNoErrorsInCode(r'''
|
||||
void f(int a) {
|
||||
while (true) {
|
||||
switch (a) {
|
||||
case 0:
|
||||
continue;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
''');
|
||||
}
|
||||
|
||||
test_terminated_return() async {
|
||||
await assertNoErrorsInCode(r'''
|
||||
void f(int a) {
|
||||
switch (a) {
|
||||
case 0:
|
||||
return;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
''');
|
||||
}
|
||||
|
||||
test_terminated_return2() async {
|
||||
await assertNoErrorsInCode(r'''
|
||||
void f(int a) {
|
||||
switch (a) {
|
||||
case 0:
|
||||
case 1:
|
||||
return;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
''');
|
||||
}
|
||||
|
||||
test_terminated_throw() async {
|
||||
await assertNoErrorsInCode(r'''
|
||||
void f(int a) {
|
||||
switch (a) {
|
||||
case 0:
|
||||
throw 42;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
''');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,162 @@
|
|||
// Copyright (c) 2020, 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.
|
||||
|
||||
import 'package:analyzer/dart/analysis/features.dart';
|
||||
import 'package:analyzer/src/error/codes.dart';
|
||||
import 'package:analyzer/src/generated/engine.dart';
|
||||
import 'package:test_reflective_loader/test_reflective_loader.dart';
|
||||
|
||||
import '../dart/resolution/driver_resolution.dart';
|
||||
|
||||
main() {
|
||||
defineReflectiveSuite(() {
|
||||
defineReflectiveTests(SwitchCaseCompletesNormallyTest);
|
||||
});
|
||||
}
|
||||
|
||||
@reflectiveTest
|
||||
class SwitchCaseCompletesNormallyTest extends DriverResolutionTest {
|
||||
@override
|
||||
AnalysisOptionsImpl get analysisOptions => AnalysisOptionsImpl()
|
||||
..contextFeatures = FeatureSet.forTesting(
|
||||
sdkVersion: '2.7.0', additionalFeatures: [Feature.non_nullable]);
|
||||
|
||||
test_break() async {
|
||||
await assertNoErrorsInCode(r'''
|
||||
void f(int a) {
|
||||
switch (a) {
|
||||
case 0:
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
''');
|
||||
}
|
||||
|
||||
test_completes() async {
|
||||
await assertErrorsInCode('''
|
||||
void f(int a) {
|
||||
switch (a) {
|
||||
case 0:
|
||||
print(0);
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}''', [
|
||||
error(CompileTimeErrorCode.SWITCH_CASE_COMPLETES_NORMALLY, 35, 4),
|
||||
]);
|
||||
}
|
||||
|
||||
test_continue_loop() async {
|
||||
await assertNoErrorsInCode(r'''
|
||||
void f(int a) {
|
||||
while (true) {
|
||||
switch (a) {
|
||||
case 0:
|
||||
continue;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
''');
|
||||
}
|
||||
|
||||
test_for_whatever() async {
|
||||
await assertNoErrorsInCode(r'''
|
||||
void f(int a) {
|
||||
switch (a) {
|
||||
case 0:
|
||||
for (;;) {
|
||||
print(0);
|
||||
}
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
''');
|
||||
}
|
||||
|
||||
test_lastCase() async {
|
||||
await assertNoErrorsInCode(r'''
|
||||
f(int a) {
|
||||
switch (a) {
|
||||
case 0:
|
||||
print(0);
|
||||
}
|
||||
}
|
||||
''');
|
||||
}
|
||||
|
||||
test_methodInvocation_never() async {
|
||||
await assertNoErrorsInCode(r'''
|
||||
void f(int a) {
|
||||
switch (a) {
|
||||
case 0:
|
||||
neverCompletes();
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Never neverCompletes() {}
|
||||
''');
|
||||
}
|
||||
|
||||
test_return() async {
|
||||
await assertNoErrorsInCode(r'''
|
||||
void f(int a) {
|
||||
switch (a) {
|
||||
case 0:
|
||||
return;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
''');
|
||||
}
|
||||
|
||||
test_return2() async {
|
||||
await assertNoErrorsInCode(r'''
|
||||
void f(int a) {
|
||||
switch (a) {
|
||||
case 0:
|
||||
case 1:
|
||||
return;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
''');
|
||||
}
|
||||
|
||||
test_throw() async {
|
||||
await assertNoErrorsInCode(r'''
|
||||
void f(int a) {
|
||||
switch (a) {
|
||||
case 0:
|
||||
throw 42;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
''');
|
||||
}
|
||||
|
||||
test_while_true() async {
|
||||
await assertNoErrorsInCode(r'''
|
||||
void f(int a) {
|
||||
switch (a) {
|
||||
case 0:
|
||||
while (true) {
|
||||
print(0);
|
||||
}
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
''');
|
||||
}
|
||||
}
|
|
@ -412,6 +412,8 @@ import 'super_in_invalid_context_test.dart' as super_in_invalid_context;
|
|||
import 'super_in_redirecting_constructor_test.dart'
|
||||
as super_in_redirecting_constructor;
|
||||
import 'super_initializer_in_object_test.dart' as super_initializer_in_object;
|
||||
import 'switch_case_completes_normally_test.dart'
|
||||
as switch_case_completes_normally;
|
||||
import 'switch_expression_not_assignable_test.dart'
|
||||
as switch_expression_not_assignable;
|
||||
import 'todo_test.dart' as todo_test;
|
||||
|
@ -764,6 +766,7 @@ main() {
|
|||
super_in_invalid_context.main();
|
||||
super_in_redirecting_constructor.main();
|
||||
super_initializer_in_object.main();
|
||||
switch_case_completes_normally.main();
|
||||
switch_expression_not_assignable.main();
|
||||
todo_test.main();
|
||||
top_level_instance_getter.main();
|
||||
|
|
Loading…
Reference in a new issue