Unit tests for NON_EXHAUSTIVE_SWITCH and UNREACHABLE_SWITCH_CASE.

Change-Id: I0b291046eba881acd35aeba1f4725c84f0bc9bcc
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/282160
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
This commit is contained in:
Konstantin Shcheglov
2023-02-10 21:50:27 +00:00
committed by Commit Queue
parent 973e75ab35
commit c6352b0e49
12 changed files with 656 additions and 112 deletions
@@ -209,6 +209,16 @@ final a = switch (Object()) {
Square(length: v^ar l) => l * l,
Circle(radius: var r) => math.pi * r * r
};
class Square {
final int length;
Square(this.length);
}
class Circle {
final int length;
Circle(this.radius);
}
''');
final regions = await _computeSelectionRanges(content);
@@ -6,7 +6,6 @@ import 'dart:collection';
import 'package:_fe_analyzer_shared/src/exhaustiveness/exhaustive.dart';
import 'package:_fe_analyzer_shared/src/exhaustiveness/space.dart';
import 'package:_fe_analyzer_shared/src/exhaustiveness/static_type.dart';
import 'package:analyzer/dart/analysis/declared_variables.dart';
import 'package:analyzer/dart/analysis/features.dart';
import 'package:analyzer/dart/ast/ast.dart';
@@ -369,20 +368,38 @@ class ConstantVerifier extends RecursiveAstVisitor<void> {
}
}
@override
void visitSwitchExpression(SwitchExpression node) {
_withConstantPatternValues((constantPatternValues) {
super.visitSwitchExpression(node);
_validateSwitchExhaustiveness(
node: node,
switchKeyword: node.switchKeyword,
scrutinee: node.expression,
caseNodes: node.cases,
constantPatternValues: constantPatternValues,
);
});
}
@override
void visitSwitchStatement(SwitchStatement node) {
Map<ConstantPattern, DartObjectImpl>? previousConstantPatternValues =
_constantPatternValues;
_constantPatternValues = {};
super.visitSwitchStatement(node);
if (_currentLibrary.featureSet.isEnabled(Feature.patterns)) {
_validateSwitchStatement_patterns(node, _constantPatternValues!);
} else if (_currentLibrary.isNonNullableByDefault) {
_validateSwitchStatement_nullSafety(node);
} else {
_validateSwitchStatement_legacy(node);
}
_constantPatternValues = previousConstantPatternValues;
_withConstantPatternValues((constantPatternValues) {
super.visitSwitchStatement(node);
if (_currentLibrary.featureSet.isEnabled(Feature.patterns)) {
_validateSwitchExhaustiveness(
node: node,
switchKeyword: node.switchKeyword,
scrutinee: node.expression,
caseNodes: node.members,
constantPatternValues: constantPatternValues,
);
} else if (_currentLibrary.isNonNullableByDefault) {
_validateSwitchStatement_nullSafety(node);
} else {
_validateSwitchStatement_legacy(node);
}
});
}
@override
@@ -721,6 +738,97 @@ class ConstantVerifier extends RecursiveAstVisitor<void> {
}
}
void _validateSwitchExhaustiveness({
required AstNode node,
required Token switchKeyword,
required Expression scrutinee,
required List<AstNode> caseNodes,
required Map<ConstantPattern, DartObjectImpl> constantPatternValues,
}) {
final scrutineeType = scrutinee.typeOrThrow;
final scrutineeTypeEx = _exhaustivenessCache.getStaticType(scrutineeType);
final caseNodesWithSpace = <AstNode>[];
final caseSpaces = <Space>[];
var hasDefault = false;
// Build spaces for cases.
for (final caseNode in caseNodes) {
GuardedPattern? guardedPattern;
if (caseNode is SwitchDefault) {
hasDefault = true;
} else if (caseNode is SwitchExpressionCase) {
guardedPattern = caseNode.guardedPattern;
} else if (caseNode is SwitchPatternCase) {
guardedPattern = caseNode.guardedPattern;
} else {
throw UnimplementedError('(${caseNode.runtimeType}) $caseNode');
}
if (guardedPattern != null) {
Space space;
if (guardedPattern.whenClause != null) {
// TODO(johnniwinther): Test this.
space = Space(_exhaustivenessCache.getUnknownStaticType());
} else {
final pattern = guardedPattern.pattern;
space = convertPatternToSpace(
_exhaustivenessCache, pattern, constantPatternValues);
}
caseNodesWithSpace.add(caseNode);
caseSpaces.add(space);
}
}
// Prepare for recording data for testing.
List<Space>? remainingSpaces;
final exhaustivenessDataForTesting = this.exhaustivenessDataForTesting;
if (exhaustivenessDataForTesting != null) {
remainingSpaces = [];
}
// Compute and report errors.
final errors = reportErrors(scrutineeTypeEx, caseSpaces, remainingSpaces);
for (final error in errors) {
if (error is UnreachableCaseError) {
final caseNode = caseNodesWithSpace[error.index];
final Token errorToken;
if (caseNode is SwitchExpressionCase) {
errorToken = caseNode.arrow;
} else if (caseNode is SwitchPatternCase) {
errorToken = caseNode.keyword;
} else {
throw UnimplementedError('(${caseNode.runtimeType}) $caseNode');
}
_errorReporter.reportErrorForToken(
HintCode.UNREACHABLE_SWITCH_CASE,
errorToken,
);
} else if (error is NonExhaustiveError &&
_typeSystem.isAlwaysExhaustive(scrutineeType) &&
!hasDefault) {
_errorReporter.reportErrorForToken(
CompileTimeErrorCode.NON_EXHAUSTIVE_SWITCH,
switchKeyword,
[scrutineeType, '${error.remaining}'],
);
}
}
// Record data for testing.
if (exhaustivenessDataForTesting != null && remainingSpaces != null) {
assert(remainingSpaces.length == caseSpaces.length + 1);
for (var i = 0; i < caseSpaces.length; i++) {
final caseNode = caseNodesWithSpace[i];
exhaustivenessDataForTesting.caseSpaces[caseNode] = caseSpaces[i];
exhaustivenessDataForTesting.remainingSpaces[caseNode] =
remainingSpaces[i];
}
exhaustivenessDataForTesting.switchScrutineeType[node] = scrutineeTypeEx;
exhaustivenessDataForTesting.remainingSpaces[node] = remainingSpaces.last;
}
}
void _validateSwitchStatement_legacy(SwitchStatement node) {
// TODO(paulberry): to minimize error messages, it would be nice to
// compare all types with the most popular type rather than the first
@@ -809,74 +917,14 @@ class ConstantVerifier extends RecursiveAstVisitor<void> {
}
}
void _validateSwitchStatement_patterns(SwitchStatement node,
Map<ConstantPattern, DartObjectImpl> constantPatternValues) {
DartType expressionType = node.expression.staticType!;
StaticType type = _exhaustivenessCache.getStaticType(expressionType);
List<Space> cases = [];
List<SwitchMember> caseMembers = [];
bool hasDefault = false;
for (var switchMember in node.members) {
if (switchMember is SwitchCase) {
Expression expression = switchMember.expression;
var expressionValue = _validate(
expression,
CompileTimeErrorCode.NON_CONSTANT_CASE_EXPRESSION,
);
Space space =
convertConstantValueToSpace(_exhaustivenessCache, expressionValue);
cases.add(space);
caseMembers.add(switchMember);
} else if (switchMember is SwitchPatternCase) {
Space space;
if (switchMember.guardedPattern.whenClause != null) {
// TODO(johnniwinther): Test this.
space = Space(_exhaustivenessCache.getUnknownStaticType());
} else {
DartPattern pattern = switchMember.guardedPattern.pattern;
space = convertPatternToSpace(
_exhaustivenessCache, pattern, constantPatternValues);
}
cases.add(space);
caseMembers.add(switchMember);
} else if (switchMember is SwitchDefault) {
hasDefault = true;
}
}
List<Space>? remainingSpaces;
if (exhaustivenessDataForTesting != null) {
remainingSpaces = [];
}
for (ExhaustivenessError error
in reportErrors(type, cases, remainingSpaces)) {
if (error is UnreachableCaseError) {
_errorReporter.reportErrorForToken(
HintCode.UNREACHABLE_SWITCH_CASE,
caseMembers[error.index].keyword,
[],
);
} else if (error is NonExhaustiveError &&
_typeSystem.isAlwaysExhaustive(expressionType) &&
!hasDefault) {
_errorReporter.reportErrorForNode(
CompileTimeErrorCode.NON_EXHAUSTIVE_SWITCH,
node.expression,
[expressionType, '${error.remaining}'],
);
}
}
if (exhaustivenessDataForTesting != null) {
assert(remainingSpaces!.length == cases.length + 1);
for (int i = 0; i < cases.length; i++) {
SwitchMember caseMember = caseMembers[i];
exhaustivenessDataForTesting!.caseSpaces[caseMember] = cases[i];
exhaustivenessDataForTesting!.remainingSpaces[caseMember] =
remainingSpaces![i];
}
exhaustivenessDataForTesting!.switchScrutineeType[node] = type;
exhaustivenessDataForTesting!.remainingSpaces[node] =
remainingSpaces!.last;
}
/// Runs [f] with new [_constantPatternValues].
void _withConstantPatternValues(
void Function(Map<ConstantPattern, DartObjectImpl> constantPatternValues) f,
) {
final previous = _constantPatternValues;
final values = _constantPatternValues = {};
f(values);
_constantPatternValues = previous;
}
}
@@ -40,9 +40,8 @@ Space convertPatternToSpace(
AnalyzerExhaustivenessCache cache,
DartPattern pattern,
Map<ConstantPattern, DartObjectImpl> constantPatternValues) {
if (pattern is DeclaredVariablePattern) {
DartType type =
(pattern as DeclaredVariablePatternImpl).declaredElement!.type;
if (pattern is DeclaredVariablePatternImpl) {
DartType type = pattern.declaredElement!.type;
return Space(cache.getStaticType(type));
} else if (pattern is ObjectPattern) {
Map<String, Space> fields = {};
@@ -66,7 +66,7 @@ enum E {
error(CompileTimeErrorCode.BODY_MIGHT_COMPLETE_NORMALLY, 28, 5),
error(StaticWarningCode.MISSING_ENUM_CONSTANT_IN_SWITCH, 40, 13),
] else
error(CompileTimeErrorCode.NON_EXHAUSTIVE_SWITCH, 48, 4)
error(CompileTimeErrorCode.NON_EXHAUSTIVE_SWITCH, 40, 6)
]);
}
@@ -197,7 +197,7 @@ int f(Foo foo) {
error(CompileTimeErrorCode.BODY_MIGHT_COMPLETE_NORMALLY, 23, 1),
error(StaticWarningCode.MISSING_ENUM_CONSTANT_IN_SWITCH, 38, 12),
] else
error(CompileTimeErrorCode.NON_EXHAUSTIVE_SWITCH, 46, 3),
error(CompileTimeErrorCode.NON_EXHAUSTIVE_SWITCH, 38, 6),
]);
}
@@ -220,7 +220,7 @@ int f(E e) {
error(CompileTimeErrorCode.BODY_MIGHT_COMPLETE_NORMALLY, 47, 1),
error(StaticWarningCode.MISSING_ENUM_CONSTANT_IN_SWITCH, 58, 10),
] else
error(CompileTimeErrorCode.NON_EXHAUSTIVE_SWITCH, 66, 1),
error(CompileTimeErrorCode.NON_EXHAUSTIVE_SWITCH, 58, 6),
]);
}
@@ -275,7 +275,7 @@ int f(Foo? foo) {
error(CompileTimeErrorCode.BODY_MIGHT_COMPLETE_NORMALLY, 23, 1),
error(StaticWarningCode.MISSING_ENUM_CONSTANT_IN_SWITCH, 39, 12),
] else
error(CompileTimeErrorCode.NON_EXHAUSTIVE_SWITCH, 47, 3),
error(CompileTimeErrorCode.NON_EXHAUSTIVE_SWITCH, 39, 6),
]);
}
@@ -68,7 +68,7 @@ void f(E e) {
if (!_arePatternsEnabled)
error(StaticWarningCode.MISSING_ENUM_CONSTANT_IN_SWITCH, 44, 10)
else
error(CompileTimeErrorCode.NON_EXHAUSTIVE_SWITCH, 52, 1),
error(CompileTimeErrorCode.NON_EXHAUSTIVE_SWITCH, 44, 6),
]);
}
@@ -87,7 +87,7 @@ void f(E e) {
if (!_arePatternsEnabled)
error(StaticWarningCode.MISSING_ENUM_CONSTANT_IN_SWITCH, 44, 10)
else
error(CompileTimeErrorCode.NON_EXHAUSTIVE_SWITCH, 52, 1),
error(CompileTimeErrorCode.NON_EXHAUSTIVE_SWITCH, 44, 6),
]);
}
@@ -106,7 +106,7 @@ void f(E e) {
if (!_arePatternsEnabled)
error(StaticWarningCode.MISSING_ENUM_CONSTANT_IN_SWITCH, 44, 10)
else
error(CompileTimeErrorCode.NON_EXHAUSTIVE_SWITCH, 52, 1),
error(CompileTimeErrorCode.NON_EXHAUSTIVE_SWITCH, 44, 6),
]);
}
@@ -166,7 +166,7 @@ void f(E? e) {
if (!_arePatternsEnabled)
error(StaticWarningCode.MISSING_ENUM_CONSTANT_IN_SWITCH, 38, 10)
else
error(CompileTimeErrorCode.NON_EXHAUSTIVE_SWITCH, 46, 1),
error(CompileTimeErrorCode.NON_EXHAUSTIVE_SWITCH, 38, 6),
]);
}
@@ -0,0 +1,394 @@
// Copyright (c) 2023, 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/src/error/codes.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
import '../dart/resolution/context_collection_resolution.dart';
main() {
defineReflectiveSuite(() {
defineReflectiveTests(NonExhaustiveSwitchExpressionTest);
defineReflectiveTests(NonExhaustiveSwitchStatementTest);
});
}
@reflectiveTest
class NonExhaustiveSwitchExpressionTest extends PubPackageResolutionTest {
test_bool_true() async {
await assertErrorsInCode(r'''
Object f(bool x) {
return switch (x) {
true => 0,
};
}
''', [
error(CompileTimeErrorCode.NON_EXHAUSTIVE_SWITCH, 28, 6),
]);
}
test_bool_true_false() async {
await assertNoErrorsInCode(r'''
Object f(bool x) {
return switch (x) {
true => 1,
false => 0,
};
}
''');
}
test_enum_2at2_hasWhen() async {
await assertErrorsInCode(r'''
enum E {
a, b
}
Object f(E x) {
return switch (x) {
E.a when 1 == 0 => 0,
E.b => 1,
};
}
''', [
error(CompileTimeErrorCode.NON_EXHAUSTIVE_SWITCH, 44, 6,
correctionContains: 'E.a'),
]);
}
}
@reflectiveTest
class NonExhaustiveSwitchStatementTest extends PubPackageResolutionTest {
test_alwaysExhaustive_bool_true() async {
await assertErrorsInCode(r'''
void f(bool x) {
switch (x) {
case true:
break;
}
}
''', [
error(CompileTimeErrorCode.NON_EXHAUSTIVE_SWITCH, 19, 6),
]);
}
test_alwaysExhaustive_bool_true_false() async {
await assertNoErrorsInCode(r'''
void f(bool x) {
switch (x) {
case true:
case false:
break;
}
}
''');
}
/// TODO(scheglov) Fix it.
@FailingTest(issue: 'https://github.com/dart-lang/sdk/issues/51275')
test_alwaysExhaustive_bool_wildcard() async {
await assertNoErrorsInCode(r'''
void f(bool x) {
switch (x) {
case _:
break;
}
}
''');
}
test_alwaysExhaustive_boolNullable_true_false() async {
await assertErrorsInCode(r'''
void f(bool? x) {
switch (x) {
case true:
case false:
break;
}
}
''', [
error(CompileTimeErrorCode.NON_EXHAUSTIVE_SWITCH, 20, 6),
]);
}
/// TODO(scheglov) Fix it.
@FailingTest(issue: 'https://github.com/dart-lang/sdk/issues/51275')
test_alwaysExhaustive_boolNullable_true_false_null() async {
await assertNoErrorsInCode(r'''
void f(bool? x) {
switch (x) {
case true:
case false:
case Null:
break;
}
}
''');
}
test_alwaysExhaustive_enum_2at1() async {
await assertErrorsInCode(r'''
enum E {
a, b
}
void f(E x) {
switch (x) {
case E.a:
break;
}
}
''', [
error(CompileTimeErrorCode.NON_EXHAUSTIVE_SWITCH, 35, 6),
]);
}
test_alwaysExhaustive_enum_2at2_cases() async {
await assertNoErrorsInCode(r'''
enum E {
a, b
}
void f(E x) {
switch (x) {
case E.a:
case E.b:
break;
}
}
''');
}
test_alwaysExhaustive_enum_2at2_hasWhen() async {
await assertErrorsInCode(r'''
enum E {
a, b
}
void f(E x) {
switch (x) {
case E.a when 1 == 0:
case E.b:
break;
}
}
''', [
error(CompileTimeErrorCode.NON_EXHAUSTIVE_SWITCH, 35, 6,
correctionContains: 'E.a'),
]);
}
/// TODO(scheglov) Fix it.
@FailingTest(issue: 'https://github.com/dart-lang/sdk/issues/51275')
test_alwaysExhaustive_enum_2at2_logicalOr() async {
await assertNoErrorsInCode(
r'''
enum E {
a, b
}
void f(E x) {
switch (x) {
case E.a || E.b:
break;
}
}
''',
);
}
test_alwaysExhaustive_Null_hasError() async {
await assertErrorsInCode(r'''
void f(Null x) {
switch (x) {}
}
''', [
error(CompileTimeErrorCode.NON_EXHAUSTIVE_SWITCH, 19, 6),
]);
}
test_alwaysExhaustive_Null_noError() async {
await assertNoErrorsInCode(r'''
void f(Null x) {
switch (x) {
case null:
break;
}
}
''');
}
/// TODO(scheglov) Fix it.
@FailingTest(issue: 'https://github.com/dart-lang/sdk/issues/51275')
test_alwaysExhaustive_recordType_bool_bool_4at4() async {
await assertNoErrorsInCode(r'''
void f((bool, bool) x) {
switch (x) {
case (false, false):
case (false, true):
case (true, false):
case (true, true):
break;
}
}
''');
}
test_alwaysExhaustive_sealedClass_2at1() async {
await assertErrorsInCode(r'''
sealed class A {}
class B extends A {}
class C extends A {}
void f(A x) {
switch (x) {
case B():
break;
}
}
''', [
error(CompileTimeErrorCode.NON_EXHAUSTIVE_SWITCH, 77, 6),
]);
}
test_alwaysExhaustive_sealedClass_2at2() async {
await assertNoErrorsInCode(r'''
sealed class A {}
class B extends A {}
class C extends A {}
void f(A x) {
switch (x) {
case B():
break;
case C():
break;
}
}
''');
}
/// TODO(scheglov) Fix it.
@FailingTest(issue: 'https://github.com/dart-lang/sdk/issues/51275')
test_alwaysExhaustive_sealedClass_2at2_wildcard() async {
await assertNoErrorsInCode(r'''
sealed class A {}
class B extends A {}
class C extends A {}
void f(A x) {
switch (x) {
case B():
break;
case _:
break;
}
}
''');
}
test_alwaysExhaustive_sealedMixin_2at1() async {
await assertErrorsInCode(r'''
sealed mixin M {}
class A with M {}
class B with M {}
void f(M x) {
switch (x) {
case A():
break;
}
}
''', [
error(CompileTimeErrorCode.NON_EXHAUSTIVE_SWITCH, 71, 6),
]);
}
/// TODO(scheglov) Fix it.
@FailingTest(issue: 'https://github.com/dart-lang/sdk/issues/51275')
test_alwaysExhaustive_sealedMixin_2at2() async {
await assertNoErrorsInCode(r'''
sealed mixin M {}
class A with M {}
class B with M {}
void f(M x) {
switch (x) {
case A():
case B():
break;
}
}
''');
}
test_alwaysExhaustive_typeVariable_bound_bool_true() async {
await assertErrorsInCode(r'''
void f<T extends bool>(T x) {
switch (x) {
case true:
break;
}
}
''', [
error(CompileTimeErrorCode.NON_EXHAUSTIVE_SWITCH, 32, 6),
]);
}
test_alwaysExhaustive_typeVariable_bound_bool_true_false() async {
await assertErrorsInCode(r'''
void f<T extends bool>(T x) {
switch (x) {
case true:
case false:
break;
}
}
''', [
error(CompileTimeErrorCode.NON_EXHAUSTIVE_SWITCH, 32, 6),
]);
}
test_alwaysExhaustive_typeVariable_promoted_bool_true() async {
await assertErrorsInCode(r'''
void f<T>(T x) {
if (x is bool) {
switch (x) {
case true:
break;
}
}
}
''', [
error(CompileTimeErrorCode.NON_EXHAUSTIVE_SWITCH, 40, 6),
]);
}
/// TODO(scheglov) Fix it.
@FailingTest(issue: 'https://github.com/dart-lang/sdk/issues/51275')
test_alwaysExhaustive_typeVariable_promoted_bool_true_false() async {
await assertNoErrorsInCode(r'''
void f<T>(T x) {
if (x is bool) {
switch (x) {
case true:
case false:
break;
}
}
}
''');
}
test_notAlwaysExhaustive_int() async {
await assertNoErrorsInCode(r'''
void f(int x) {
switch (x) {
case 0:
break;
}
}
''');
}
}
@@ -571,6 +571,7 @@ import 'non_constant_relational_pattern_expression_test.dart'
as non_constant_relational_pattern_expression;
import 'non_constant_set_element_test.dart' as non_constant_set_element;
import 'non_constant_type_argument_test.dart' as non_constant_type_argument;
import 'non_exhaustive_switch_test.dart' as non_exhaustive_switch;
import 'non_final_field_in_enum_test.dart' as non_final_field_in_enum;
import 'non_generative_constructor_test.dart' as non_generative_constructor;
import 'non_generative_implicit_constructor_test.dart'
@@ -832,6 +833,7 @@ import 'unqualified_reference_to_non_local_static_member_test.dart'
as unqualified_reference_to_non_local_static_member;
import 'unqualified_reference_to_static_member_of_extended_type_test.dart'
as unqualified_reference_to_static_member_of_extended_type;
import 'unreachable_switch_case_test.dart' as unreachable_switch_case;
import 'unused_catch_clause_test.dart' as unused_catch_clause;
import 'unused_catch_stack_test.dart' as unused_catch_stack;
import 'unused_element_test.dart' as unused_element;
@@ -1236,6 +1238,7 @@ main() {
non_constant_map_value_from_deferred_library.main();
non_constant_set_element.main();
non_constant_type_argument.main();
non_exhaustive_switch.main();
non_final_field_in_enum.main();
non_generative_constructor.main();
non_generative_implicit_constructor.main();
@@ -1414,6 +1417,7 @@ main() {
unnecessary_type_check.main();
unqualified_reference_to_non_local_static_member.main();
unqualified_reference_to_static_member_of_extended_type.main();
unreachable_switch_case.main();
unused_catch_clause.main();
unused_catch_stack.main();
unused_element.main();
@@ -0,0 +1,69 @@
// Copyright (c) 2023, 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/src/error/codes.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
import '../dart/resolution/context_collection_resolution.dart';
main() {
defineReflectiveSuite(() {
defineReflectiveTests(UnreachableSwitchCaseTest_SwitchExpression);
defineReflectiveTests(UnreachableSwitchCaseTest_SwitchStatement);
});
}
@reflectiveTest
class UnreachableSwitchCaseTest_SwitchExpression
extends PubPackageResolutionTest {
test_bool_false_true_false() async {
await assertErrorsInCode(r'''
Object f(bool x) {
return switch (x) {
false => 0,
true => 1,
false => 2,
};
}
''', [
error(HintCode.UNREACHABLE_SWITCH_CASE, 82, 2),
]);
}
/// TODO(scheglov) Fix it.
@FailingTest(issue: 'https://github.com/dart-lang/sdk/issues/51275')
test_bool_wildcard_true_false() async {
await assertErrorsInCode(r'''
Object f(bool x) {
return switch (x) {
_ => 0,
true => 1,
false => 2,
};
}
''', [
error(HintCode.UNREACHABLE_SWITCH_CASE, 62, 2),
error(HintCode.UNREACHABLE_SWITCH_CASE, 78, 2),
]);
}
}
@reflectiveTest
class UnreachableSwitchCaseTest_SwitchStatement
extends PubPackageResolutionTest {
test_bool() async {
await assertErrorsInCode(r'''
void f(bool x) {
switch (x) {
case false:
case true:
case false:
break;
}
}
''', [
error(HintCode.UNREACHABLE_SWITCH_CASE, 67, 4),
]);
}
}
@@ -45,8 +45,9 @@ const f2 = false;
void nonExhaustiveSwitch1(bool b) {
switch (b) /* Error */ {
//^^^^^^
// [analyzer] COMPILE_TIME_ERROR.NON_EXHAUSTIVE_SWITCH
// ^
// [analyzer] COMPILE_TIME_ERROR.NON_EXHAUSTIVE_SWITCH
// [cfe] The type 'bool' is not exhaustively matched by the switch cases.
case true:
print('true');
@@ -56,8 +57,9 @@ void nonExhaustiveSwitch1(bool b) {
void nonExhaustiveSwitch2(bool b) {
switch (b) /* Error */ {
//^^^^^^
// [analyzer] COMPILE_TIME_ERROR.NON_EXHAUSTIVE_SWITCH
// ^
// [analyzer] COMPILE_TIME_ERROR.NON_EXHAUSTIVE_SWITCH
// [cfe] The type 'bool' is not exhaustively matched by the switch cases.
case false:
print('false');
@@ -92,8 +94,9 @@ void exhaustiveNullableSwitch(bool? b) {
void nonExhaustiveNullableSwitch1(bool? b) {
switch (b) /* Error */ {
//^^^^^^
// [analyzer] COMPILE_TIME_ERROR.NON_EXHAUSTIVE_SWITCH
// ^
// [analyzer] COMPILE_TIME_ERROR.NON_EXHAUSTIVE_SWITCH
// [cfe] The type 'bool?' is not exhaustively matched by the switch cases.
case true:
print('true');
@@ -106,8 +109,9 @@ void nonExhaustiveNullableSwitch1(bool? b) {
void nonExhaustiveNullableSwitch2(bool? b) {
switch (b) /* Error */ {
//^^^^^^
// [analyzer] COMPILE_TIME_ERROR.NON_EXHAUSTIVE_SWITCH
// ^
// [analyzer] COMPILE_TIME_ERROR.NON_EXHAUSTIVE_SWITCH
// [cfe] The type 'bool?' is not exhaustively matched by the switch cases.
case true:
print('true');
@@ -58,8 +58,9 @@ const c2 = Enum.c;
void nonExhaustiveSwitch1(Enum e) {
switch (e) /* Error */ {
// ^
//^^^^^^
// [analyzer] COMPILE_TIME_ERROR.NON_EXHAUSTIVE_SWITCH
// ^
// [cfe] The type 'Enum' is not exhaustively matched by the switch cases.
case Enum.a:
print('a');
@@ -72,8 +73,9 @@ void nonExhaustiveSwitch1(Enum e) {
void nonExhaustiveSwitch2(Enum e) {
switch (e) /* Error */ {
// ^
//^^^^^^
// [analyzer] COMPILE_TIME_ERROR.NON_EXHAUSTIVE_SWITCH
// ^
// [cfe] The type 'Enum' is not exhaustively matched by the switch cases.
case Enum.a:
print('a');
@@ -86,8 +88,9 @@ void nonExhaustiveSwitch2(Enum e) {
void nonExhaustiveSwitch3(Enum e) {
switch (e) /* Error */ {
// ^
//^^^^^^
// [analyzer] COMPILE_TIME_ERROR.NON_EXHAUSTIVE_SWITCH
// ^
// [cfe] The type 'Enum' is not exhaustively matched by the switch cases.
case Enum.b:
print('b');
@@ -100,8 +103,9 @@ void nonExhaustiveSwitch3(Enum e) {
void nonExhaustiveSwitch4(Enum e) {
switch (e) /* Error */ {
// ^
//^^^^^^
// [analyzer] COMPILE_TIME_ERROR.NON_EXHAUSTIVE_SWITCH
// ^
// [cfe] The type 'Enum' is not exhaustively matched by the switch cases.
case Enum.b:
print('b');
@@ -139,8 +143,9 @@ void exhaustiveNullableSwitch(Enum? e) {
void nonExhaustiveNullableSwitch1(Enum? e) {
switch (e) /* Error */ {
// ^
//^^^^^^
// [analyzer] COMPILE_TIME_ERROR.NON_EXHAUSTIVE_SWITCH
// ^
// [cfe] The type 'Enum?' is not exhaustively matched by the switch cases.
case Enum.a:
print('a');
@@ -156,8 +161,9 @@ void nonExhaustiveNullableSwitch1(Enum? e) {
void nonExhaustiveNullableSwitch2(Enum? e) {
switch (e) /* Error */ {
// ^
//^^^^^^
// [analyzer] COMPILE_TIME_ERROR.NON_EXHAUSTIVE_SWITCH
// ^
// [cfe] The type 'Enum?' is not exhaustively matched by the switch cases.
case Enum.a:
print('a');
@@ -192,8 +198,9 @@ void unreachableCase1(Enum e) {
void unreachableCase2(Enum e) {
switch (e) /* Non-exhaustive */ {
// ^
//^^^^^^
// [analyzer] COMPILE_TIME_ERROR.NON_EXHAUSTIVE_SWITCH
// ^
// [cfe] The type 'Enum' is not exhaustively matched by the switch cases.
case Enum.a:
print('a1');
@@ -36,8 +36,9 @@ void exhaustiveSwitch(A r) {
void nonExhaustiveSwitch1(A r) {
switch (r) /* Error */ {
//^^^^^^
// [analyzer] COMPILE_TIME_ERROR.NON_EXHAUSTIVE_SWITCH
// ^
// [analyzer] COMPILE_TIME_ERROR.NON_EXHAUSTIVE_SWITCH
// [cfe] The type 'A' is not exhaustively matched by the switch cases.
case A(a: Enum.a, b: false):
print('A(a, false)');
@@ -53,8 +54,9 @@ void nonExhaustiveSwitch1(A r) {
void nonExhaustiveSwitch2(A r) {
switch (r) /* Error */ {
//^^^^^^
// [analyzer] COMPILE_TIME_ERROR.NON_EXHAUSTIVE_SWITCH
// ^
// [analyzer] COMPILE_TIME_ERROR.NON_EXHAUSTIVE_SWITCH
// [cfe] The type 'A' is not exhaustively matched by the switch cases.
case A(a: Enum.b, b: false):
print('A(b, false)');
@@ -101,8 +103,9 @@ void exhaustiveNullableSwitch(A? r) {
void nonExhaustiveNullableSwitch1(A? r) {
switch (r) /* Error */ {
//^^^^^^
// [analyzer] COMPILE_TIME_ERROR.NON_EXHAUSTIVE_SWITCH
// ^
// [analyzer] COMPILE_TIME_ERROR.NON_EXHAUSTIVE_SWITCH
// [cfe] The type 'A?' is not exhaustively matched by the switch cases.
case A(a: Enum.a, b: false):
print('A(a, false)');
@@ -121,8 +124,9 @@ void nonExhaustiveNullableSwitch1(A? r) {
void nonExhaustiveNullableSwitch2(A? r) {
switch (r) /* Error */ {
//^^^^^^
// [analyzer] COMPILE_TIME_ERROR.NON_EXHAUSTIVE_SWITCH
// ^
// [analyzer] COMPILE_TIME_ERROR.NON_EXHAUSTIVE_SWITCH
// [cfe] The type 'A?' is not exhaustively matched by the switch cases.
case A(a: Enum.a, b: false):
print('A(a, false)');
@@ -38,8 +38,9 @@ void exhaustiveSwitch2(A a) {
void nonExhaustiveSwitch1(A a) {
switch (a) /* Error */ {
//^^^^^^
// [analyzer] COMPILE_TIME_ERROR.NON_EXHAUSTIVE_SWITCH
// ^
// [analyzer] COMPILE_TIME_ERROR.NON_EXHAUSTIVE_SWITCH
// [cfe] The type 'A' is not exhaustively matched by the switch cases.
case B b:
print('B');
@@ -52,8 +53,9 @@ void nonExhaustiveSwitch1(A a) {
void nonExhaustiveSwitch2(A a) {
switch (a) /* Error */ {
//^^^^^^
// [analyzer] COMPILE_TIME_ERROR.NON_EXHAUSTIVE_SWITCH
// ^
// [analyzer] COMPILE_TIME_ERROR.NON_EXHAUSTIVE_SWITCH
// [cfe] The type 'A' is not exhaustively matched by the switch cases.
case C c:
print('C');
@@ -66,8 +68,9 @@ void nonExhaustiveSwitch2(A a) {
void nonExhaustiveSwitch3(A a) {
switch (a) /* Error */ {
//^^^^^^
// [analyzer] COMPILE_TIME_ERROR.NON_EXHAUSTIVE_SWITCH
// ^
// [analyzer] COMPILE_TIME_ERROR.NON_EXHAUSTIVE_SWITCH
// [cfe] The type 'A' is not exhaustively matched by the switch cases.
case B b:
print('B');
@@ -108,8 +111,9 @@ void exhaustiveNullableSwitch(A? a) {
void nonExhaustiveNullableSwitch1(A? a) {
switch (a) /* Error */ {
//^^^^^^
// [analyzer] COMPILE_TIME_ERROR.NON_EXHAUSTIVE_SWITCH
// ^
// [analyzer] COMPILE_TIME_ERROR.NON_EXHAUSTIVE_SWITCH
// [cfe] The type 'A?' is not exhaustively matched by the switch cases.
case A a:
print('A');
@@ -119,8 +123,9 @@ void nonExhaustiveNullableSwitch1(A? a) {
void nonExhaustiveNullableSwitch2(A? a) {
switch (a) /* Error */ {
//^^^^^^
// [analyzer] COMPILE_TIME_ERROR.NON_EXHAUSTIVE_SWITCH
// ^
// [analyzer] COMPILE_TIME_ERROR.NON_EXHAUSTIVE_SWITCH
// [cfe] The type 'A?' is not exhaustively matched by the switch cases.
case B b:
print('B');