blob: 555d8988e5e10a9165fd4321dfeb968135292f9a [file] [log] [blame]
// 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.
// Flow analysis doesn't do full exhaustiveness analysis of switch statements,
// but it detects if a switch statement is "trivially exhaustive". A switch
// statement is trivially exhaustive if it has at least one case that fully
// covers the matched value type.
//
// Also, flow analysis understands that after a case that fully covers the
// matched value type, any further cases are unreachable.
//
// We detect whether flow analysis considers the switch exhaustive by assigning
// to a nullable variable in all cases (this promotes the variable to
// non-nullable), and seeing whether the promotion lasts after the switch.
import '../static_type_helper.dart';
void testTwoCasesSecondExhaustive(Object x) {
// Trivially exhaustive because the second case fully covers the matched type
bool? y;
switch (x) {
case int _:
y = true;
case _:
y = true;
}
y.expectStaticType<Exactly<bool>>();
}
void testTwoCasesNotExhaustive(Object x) {
// Not exhaustive because neither case fully covers the matched type
bool? y;
switch (x) {
case int _:
y = true;
case String _:
y = true;
}
y.expectStaticType<Exactly<bool?>>();
}
void testUnreachableCase(Object x) {
// Not only is this switch trivially exhaustive, but also the second case is
// unreachable, and hence `y` remains promoted after the switch.
bool? y;
switch (x) {
case _:
y = true;
case int _:
// ^^^^
// [analyzer] HINT.UNREACHABLE_SWITCH_CASE
y = null;
}
y.expectStaticType<Exactly<bool>>();
}
void testCastWhereSubpatternAlwaysMatches(Object x) {
// Trivially exhaustive
bool? y;
switch (x) {
case _ as int:
y = true;
}
y.expectStaticType<Exactly<bool>>();
}
void testCastWhereSubpatternMatchesCastType(Object x) {
// Trivially exhaustive
bool? y;
switch (x) {
case bool() as bool:
y = true;
}
y.expectStaticType<Exactly<bool>>();
}
void testCastWhereSubpatternMayFailToMatch(Object x) {
// Not exhaustive
bool? y;
switch (x) {
case (== 0) as int:
y = true;
}
y.expectStaticType<Exactly<bool?>>();
}
void testListEmpty(List<Object> x) {
// Not exhaustive
bool? y;
switch (x) {
case []:
y = true;
}
y.expectStaticType<Exactly<bool?>>();
}
void testListContainingNonRestPattern(List<Object> x) {
// Not exhaustive
bool? y;
switch (x) {
case [_]:
y = true;
}
y.expectStaticType<Exactly<bool?>>();
}
void testListContainingNonRestPatternAndRestPattern(List<Object> x) {
// Not exhaustive
bool? y;
switch (x) {
case [_, ...]:
y = true;
}
y.expectStaticType<Exactly<bool?>>();
}
void testListContainingRestPatternAndNonRestPattern(List<Object> x) {
// Not exhaustive
bool? y;
switch (x) {
case [..., _]:
y = true;
}
y.expectStaticType<Exactly<bool?>>();
}
void testListContainingOnlyRestPattern(List<Object> x) {
// Trivially exhaustive
bool? y;
switch (x) {
case [...]:
y = true;
}
y.expectStaticType<Exactly<bool>>();
}
void testListContainingOnlyRestPatternWithSubpatternWildcard(List<Object> x) {
// Trivially exhaustive
bool? y;
switch (x) {
case [..._]:
y = true;
}
y.expectStaticType<Exactly<bool>>();
}
void testListContainingOnlyRestPatternWithSubpatternAnyList(List<Object> x) {
// Trivially exhaustive
bool? y;
switch (x) {
case [...[...]]:
y = true;
}
y.expectStaticType<Exactly<bool>>();
}
void testListContainingOnlyRestPatternWithSubpatternObjectPattern(
List<Object> x) {
// Trivially exhaustive
bool? y;
switch (x) {
case [...List()]:
y = true;
}
y.expectStaticType<Exactly<bool>>();
}
void testListContainingOnlyRestPatternWithSubpatternOther(List<Object> x) {
// Not exhaustive
bool? y;
switch (x) {
case [...List(length: 1)]:
y = true;
}
y.expectStaticType<Exactly<bool?>>();
}
void testListSupertype(List<Object> x) {
// Trivially exhaustive
bool? y;
switch (x) {
case <Object?>[...]:
y = true;
}
y.expectStaticType<Exactly<bool>>();
}
void testListSubtype(List<Object> x) {
// Not exhaustive
bool? y;
switch (x) {
case <int>[...]:
y = true;
}
y.expectStaticType<Exactly<bool?>>();
}
void testListSubtypeObject(Object x) {
// Not exhaustive
bool? y;
switch (x) {
case [...]:
y = true;
}
y.expectStaticType<Exactly<bool?>>();
}
void testListUnrelatedType(List<Object> x) {
// Not exhaustive
bool? y;
switch (x) {
case <int?>[...]:
y = true;
}
y.expectStaticType<Exactly<bool?>>();
}
void testMap(Map<Object, Object> x) {
// Not exhaustive
bool? y;
switch (x) {
case {0: _}:
y = true;
}
y.expectStaticType<Exactly<bool?>>();
}
void testLogicalAndBothMatch(Object x) {
// Trivially exhaustive because both subpatterns always match
bool? y;
switch (x) {
case _ && _:
y = true;
}
y.expectStaticType<Exactly<bool>>();
}
void testLogicalAndLhsMatches(Object x) {
// Not exhaustive because only the LHS always matches
bool? y;
switch (x) {
case _ && int _:
y = true;
}
y.expectStaticType<Exactly<bool?>>();
}
void testLogicalAndRhsMatches(Object x) {
// Not exhaustive because only the RHS always matches
bool? y;
switch (x) {
case _ && int _:
y = true;
}
y.expectStaticType<Exactly<bool?>>();
}
void testLogicalAndNeitherMatches(Object x) {
// Not exhaustive because neither side always matches
bool? y;
switch (x) {
case int _ && String _:
y = true;
}
y.expectStaticType<Exactly<bool?>>();
}
void testLogicalOrBothMatch(Object x) {
// Trivially exhaustive because both subpatterns always match
bool? y;
switch (x) {
case _ || _:
y = true;
}
y.expectStaticType<Exactly<bool>>();
}
void testLogicalOrLhsMatches(Object x) {
// Trivially exhaustive because the LHS always matches
bool? y;
switch (x) {
case _ || int _:
y = true;
}
y.expectStaticType<Exactly<bool>>();
}
void testLogicalOrRhsMatches(Object x) {
// Trivially exhaustive because the RHS always matches
bool? y;
switch (x) {
case _ || int _:
y = true;
}
y.expectStaticType<Exactly<bool>>();
}
void testLogicalOrNeitherMatches(Object x) {
// Not exhaustive because neither side always matches
bool? y;
switch (x) {
case int _ || String _:
y = true;
}
y.expectStaticType<Exactly<bool?>>();
}
void testNullCheckAlwaysMatches(Object x) {
// TODO(paulberry): should be trivially exhaustive because the matched value
// type is non-nullable and the subpattern always matches
bool? y;
switch (x) {
case _?:
// ^
// [analyzer] STATIC_WARNING.UNNECESSARY_NULL_CHECK_PATTERN
y = true;
}
y.expectStaticType<Exactly<bool?>>();
}
void testNullCheckNullableMatchedValueType(Object? x) {
// Not exhaustive because the matched value type is nullable
bool? y;
switch (x) {
case _?:
y = true;
}
y.expectStaticType<Exactly<bool?>>();
}
void testNullCheckSubpatternMayFailToMatch(Object x) {
// Not exhaustive because the subpattern may fail to match
bool? y;
switch (x) {
case int _?:
// ^
// [analyzer] STATIC_WARNING.UNNECESSARY_NULL_CHECK_PATTERN
y = true;
}
y.expectStaticType<Exactly<bool?>>();
}
void testNullAssertSubpatternAlwaysMatches(Object? x) {
// Trivially exhaustive because the subpattern always matches
bool? y;
switch (x) {
case _!:
y = true;
}
y.expectStaticType<Exactly<bool>>();
}
void testNullAssertSubpatternAlwaysMatchesObjectPattern(bool? x) {
// Trivially exhaustive because the subpattern always matches
bool? y;
switch (x) {
case bool()!:
y = true;
}
y.expectStaticType<Exactly<bool>>();
}
void testNullAssertSubpatternMayFailToMatch(Object x) {
// Not exhaustive because the subpattern may fail to match
bool? y;
switch (x) {
case int _!:
// ^
// [analyzer] STATIC_WARNING.UNNECESSARY_NULL_ASSERT_PATTERN
y = true;
}
y.expectStaticType<Exactly<bool?>>();
}
void testObjectSubtype(Object x) {
// Not exhaustive
bool? y;
switch (x) {
case int():
y = true;
}
y.expectStaticType<Exactly<bool?>>();
}
void testObjectSupertype(Object x) {
// Trivially exhaustive
bool? y;
switch (x) {
case dynamic():
y = true;
}
y.expectStaticType<Exactly<bool>>();
}
void testObjectUnrelatedType(List<String> x) {
// Not exhaustive
bool? y;
switch (x) {
case List<int>():
y = true;
}
y.expectStaticType<Exactly<bool?>>();
}
void testObjectSubpatternAlwaysMatches(Object x) {
// Trivially exhaustive because the hashCode always matches
bool? y;
switch (x) {
case Object(hashCode: _):
y = true;
}
y.expectStaticType<Exactly<bool>>();
}
void testObjectSubpatternMayFailToMatch(Object x) {
// Not exhaustive because the hashCode may fail to match
bool? y;
switch (x) {
case Object(hashCode: == 0):
y = true;
}
y.expectStaticType<Exactly<bool?>>();
}
void testObjectTwoSubpatternsBothMatch(Object x) {
// Trivially exhaustive because both subpatterns always match
bool? y;
switch (x) {
case Object(hashCode: _, runtimeType: _):
y = true;
}
y.expectStaticType<Exactly<bool>>();
}
void testObjectTwoSubpatternsFirstMatches(Object x) {
// Not exhaustive because the runtimeType may not match
bool? y;
switch (x) {
case Object(hashCode: _, runtimeType: == int):
y = true;
}
y.expectStaticType<Exactly<bool?>>();
}
void testObjectTwoSubpatternsSecondMatches(Object x) {
// Not exhaustive because the hashCode may not match
bool? y;
switch (x) {
case Object(hashCode: == 0, runtimeType: _):
y = true;
}
y.expectStaticType<Exactly<bool?>>();
}
void testObjectTwoSubpatternsNeitherMatches(Object x) {
// Not exhaustive because neither subpattern always matches
bool? y;
switch (x) {
case Object(hashCode: == 0, runtimeType: == int):
y = true;
}
y.expectStaticType<Exactly<bool?>>();
}
void testRecordSubtype(Object x) {
// Not exhaustive
bool? y;
switch (x) {
case (_, _):
y = true;
}
y.expectStaticType<Exactly<bool?>>();
}
void testRecordMatchingType((Object, Object) x) {
// Trivially exhaustive
bool? y;
switch (x) {
case (_, _):
y = true;
}
y.expectStaticType<Exactly<bool>>();
}
void testRecordUnrelatedType(List<String> x) {
// Not exhaustive
bool? y;
switch (x) {
case (_, _):
y = true;
}
y.expectStaticType<Exactly<bool?>>();
}
void testRecordSubpatternAlwaysMatches((Object,) x) {
// Trivially exhaustive because the subpattern always matches
bool? y;
switch (x) {
case (_,):
y = true;
}
y.expectStaticType<Exactly<bool>>();
}
void testRecordSubpatternMayFailToMatch((Object,) x) {
// Not exhaustive because the hashCode may fail to match
bool? y;
switch (x) {
case (int _,):
y = true;
}
y.expectStaticType<Exactly<bool?>>();
}
void testRecordTwoSubpatternsBothMatch((Object, Object) x) {
// Trivially exhaustive because both subpatterns always match
bool? y;
switch (x) {
case (_, _):
y = true;
}
y.expectStaticType<Exactly<bool>>();
}
void testRecordTwoSubpatternsFirstMatches((Object, Object) x) {
// Not exhaustive because the second subpattern may not match
bool? y;
switch (x) {
case (_, int _):
y = true;
}
y.expectStaticType<Exactly<bool?>>();
}
void testRecordTwoSubpatternsSecondMatches((Object, Object) x) {
// Not exhaustive because the first subpattern may not match
bool? y;
switch (x) {
case (int _, _):
y = true;
}
y.expectStaticType<Exactly<bool?>>();
}
void testRecordTwoSubpatternsNeitherMatches((Object, Object) x) {
// Not exhaustive because neither subpattern always matches
bool? y;
switch (x) {
case (int _, int _):
y = true;
}
y.expectStaticType<Exactly<bool?>>();
}
void testVariableSubtype(Object x) {
// Not exhaustive because Object !<: int
bool? y;
switch (x) {
case int v:
y = true;
}
y.expectStaticType<Exactly<bool?>>();
}
void testVariableSupertype(Object x) {
// Trivially exhaustive because Object <: Object?
bool? y;
switch (x) {
case Object? v:
y = true;
}
y.expectStaticType<Exactly<bool>>();
}
void testVariableUnrelatedType(List<String> x) {
// Not exhaustive because List<String> !<: List<int>
bool? y;
switch (x) {
case List<int> v:
y = true;
}
y.expectStaticType<Exactly<bool?>>();
}
void testVariableUntyped(Object x) {
// Trivially exhaustive because an untyped variable always matches
bool? y;
switch (x) {
case var v:
y = true;
}
y.expectStaticType<Exactly<bool>>();
}
void testWildcardSubtype(Object x) {
// Not exhaustive because Object !<: int
bool? y;
switch (x) {
case int _:
y = true;
}
y.expectStaticType<Exactly<bool?>>();
}
void testWildcardSupertype(Object x) {
// Trivially exhaustive because Object <: Object?
bool? y;
switch (x) {
case Object? _:
y = true;
}
y.expectStaticType<Exactly<bool>>();
}
void testWildcardUnrelatedType(List<String> x) {
// Not exhaustive because List<String> !<: List<int>
bool? y;
switch (x) {
case List<int> _:
y = true;
}
y.expectStaticType<Exactly<bool?>>();
}
void testWildcardUntyped(Object x) {
// Trivially exhaustive because an untyped wildcard always matches
bool? y;
switch (x) {
case _:
y = true;
}
y.expectStaticType<Exactly<bool>>();
}
void testRelationalNotEqualsNullWithNonNullableScrutinee(Object x) {
// TODO(paulberry): this should be trivially exhaustive
bool? y;
switch (x) {
case != null:
y = true;
}
y.expectStaticType<Exactly<bool?>>();
}
void testRelationalNotEqualsNullWithNullableScrutinee(Object? x) {
// Not exhaustive
bool? y;
switch (x) {
case != null:
y = true;
}
y.expectStaticType<Exactly<bool?>>();
}
void testRelationalEqualsNullWithNullScrutinee(Null x) {
// Trivially exhaustive
bool? y;
switch (x) {
//^^^^^^
// [analyzer] COMPILE_TIME_ERROR.NON_EXHAUSTIVE_SWITCH_STATEMENT
// ^
// [cfe] The type 'Null' is not exhaustively matched by the switch cases since it doesn't match 'null'.
case == null:
y = true;
}
y.expectStaticType<Exactly<bool>>();
}
void testRelationalEqualsNullWithOtherScrutinee(Object x) {
// Not exhaustive
bool? y;
switch (x) {
case == null:
y = true;
}
y.expectStaticType<Exactly<bool?>>();
}
main() {}