mirror of
https://github.com/dart-lang/sdk
synced 2024-09-19 21:11:40 +00:00
[_fe_analyzer_shared] Handle empty types in exhaustiveness
This handles Never, and as a consequence, empty sealed types in exhaustiveness. Since these contain no value, we should not try to exhaust them. This avoid invalid non-exhaustive errors in these cases. Included is handling of a scrutinee with invalid type as Never in the CFE, thus avoid cascading non-exhaustive or unreachable errors. Change-Id: Ife097731bb5399c42bf83f1d77aff773346adfb6 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/293682 Reviewed-by: Paul Berry <paulberry@google.com> Commit-Queue: Johnni Winther <johnniwinther@google.com>
This commit is contained in:
parent
17dd6d27cc
commit
65335cb4b3
|
@ -109,6 +109,10 @@ class _Checker {
|
|||
List<StaticType> stack = [firstValuePattern.type];
|
||||
while (stack.isNotEmpty) {
|
||||
StaticType type = stack.removeAt(0);
|
||||
if (type.isSubtypeOf(StaticType.neverType)) {
|
||||
// Don't try to exhaust the Never type.
|
||||
continue;
|
||||
}
|
||||
if (type.isSealed) {
|
||||
Witness? result = _filterByType(
|
||||
contextType,
|
||||
|
|
|
@ -49,3 +49,38 @@ emptyEnum(E e) {
|
|||
|
||||
};
|
||||
}
|
||||
|
||||
sealed class Empty {}
|
||||
|
||||
emptySealed(Empty empty) => /*
|
||||
checkingOrder={Empty},
|
||||
type=Empty
|
||||
*/
|
||||
switch (empty) {
|
||||
|
||||
};
|
||||
|
||||
emptyNever(Never never) => /*type=Never*/ switch (never) { };
|
||||
|
||||
emptyUnresolved(
|
||||
Unresolved
|
||||
unresolved) => /*cfe.type=Never*/ /*analyzer.
|
||||
checkingOrder={Object?,Object,Null},
|
||||
error=non-exhaustive:Object(),
|
||||
subtypes={Object,Null},
|
||||
type=Object?
|
||||
*/
|
||||
switch (unresolved) {
|
||||
|
||||
};
|
||||
|
||||
nonEmptyUnresolved(
|
||||
Unresolved
|
||||
unresolved) => /*cfe.type=Never*/ /*analyzer.
|
||||
checkingOrder={Object?,Object,Null},
|
||||
subtypes={Object,Null},
|
||||
type=Object?
|
||||
*/
|
||||
switch (unresolved) {
|
||||
_ /*cfe.space=∅*/ /*analyzer.space=()*/ => 0,
|
||||
};
|
||||
|
|
|
@ -2,7 +2,17 @@
|
|||
// 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.
|
||||
|
||||
class A<T> {}
|
||||
class I<T> {}
|
||||
|
||||
class J<T> extends I<T> {}
|
||||
|
||||
class A<T> extends J<T> {}
|
||||
|
||||
extension<T> on I<T> {
|
||||
num get member {
|
||||
return T == int ? 0.5 : 1;
|
||||
}
|
||||
}
|
||||
|
||||
extension<T> on A<T> {
|
||||
void member(T t) {}
|
||||
|
@ -10,36 +20,27 @@ extension<T> on A<T> {
|
|||
|
||||
exhaustiveInferred(
|
||||
A<num>
|
||||
a) => /*cfe.
|
||||
fields={A<int>.member:void Function(int),A<num>.member:void Function(num)},
|
||||
type=<invalid>
|
||||
*/ /*analyzer.
|
||||
checkingOrder={Object?,Object,Null},
|
||||
error=non-exhaustive:Object(),
|
||||
fields={A<int>.member:void Function(int),A<num>.member:void Function(num)},
|
||||
subtypes={Object,Null},
|
||||
type=Object?
|
||||
*/
|
||||
switch (o) {
|
||||
a) => /*
|
||||
fields={A<int>.member:void Function(int),A<num>.member:void Function(num)},
|
||||
type=A<num>
|
||||
*/
|
||||
switch (a) {
|
||||
A<int>(
|
||||
:var member
|
||||
) /*cfe.space=<invalid>(A<int>.member: void Function(int) (void Function(int)))*/ /*analyzer.space=A<int>(A<int>.member: void Function(int) (void Function(int)))*/ =>
|
||||
) /*space=A<int>(A<int>.member: void Function(int) (void Function(int)))*/ =>
|
||||
0,
|
||||
A<num>(
|
||||
:var member
|
||||
) /*cfe.
|
||||
error=unreachable,
|
||||
space=<invalid>(A<num>.member: void Function(num) (void Function(num)))
|
||||
*/ /*analyzer.space=A<num>(A<num>.member: void Function(num) (void Function(num)))*/ =>
|
||||
) /*space=A<num>(A<num>.member: void Function(num) (void Function(num)))*/ =>
|
||||
1,
|
||||
};
|
||||
|
||||
exhaustiveTyped(
|
||||
A<num>
|
||||
a) => /*cfe.
|
||||
fields={A<int>.member:void Function(int),A<num>.member:void Function(num)},
|
||||
type=<invalid>
|
||||
*/ /*analyzer.
|
||||
fields={A<int>.member:void Function(int),A<num>.member:void Function(num)},
|
||||
type=Never
|
||||
*/ /*analyzer.
|
||||
checkingOrder={Object?,Object,Null},
|
||||
error=non-exhaustive:Object(),
|
||||
fields={A<int>.member:void Function(int),A<num>.member:void Function(num)},
|
||||
|
@ -49,23 +50,23 @@ exhaustiveTyped(
|
|||
switch (o) {
|
||||
A<int>(
|
||||
:void Function(int) member
|
||||
) /*cfe.space=<invalid>(A<int>.member: void Function(int) (void Function(int)))*/ /*analyzer.space=A<int>(A<int>.member: void Function(int) (void Function(int)))*/ =>
|
||||
) /*cfe.space=Never(A<int>.member: void Function(int) (void Function(int)))*/ /*analyzer.space=A<int>(A<int>.member: void Function(int) (void Function(int)))*/ =>
|
||||
0,
|
||||
A<num>(
|
||||
:void Function(num) member
|
||||
) /*cfe.
|
||||
error=unreachable,
|
||||
space=<invalid>(A<num>.member: void Function(num) (void Function(num)))
|
||||
*/ /*analyzer.space=A<num>(A<num>.member: void Function(num) (void Function(num)))*/ =>
|
||||
error=unreachable,
|
||||
space=Never(A<num>.member: void Function(num) (void Function(num)))
|
||||
*/ /*analyzer.space=A<num>(A<num>.member: void Function(num) (void Function(num)))*/ =>
|
||||
1,
|
||||
};
|
||||
|
||||
unreachable(
|
||||
A<num>
|
||||
a) => /*cfe.
|
||||
fields={A<int>.member:void Function(int),A<num>.member:void Function(num)},
|
||||
type=<invalid>
|
||||
*/ /*analyzer.
|
||||
fields={A<int>.member:void Function(int),A<num>.member:void Function(num)},
|
||||
type=Never
|
||||
*/ /*analyzer.
|
||||
checkingOrder={Object?,Object,Null},
|
||||
error=non-exhaustive:Object(),
|
||||
fields={A<int>.member:void Function(int),A<num>.member:void Function(num)},
|
||||
|
@ -75,14 +76,14 @@ unreachable(
|
|||
switch (o) {
|
||||
A<num>(
|
||||
:var member
|
||||
) /*cfe.space=<invalid>(A<num>.member: void Function(num) (void Function(num)))*/ /*analyzer.space=A<num>(A<num>.member: void Function(num) (void Function(num)))*/ =>
|
||||
) /*cfe.space=Never(A<num>.member: void Function(num) (void Function(num)))*/ /*analyzer.space=A<num>(A<num>.member: void Function(num) (void Function(num)))*/ =>
|
||||
1,
|
||||
A<int>(
|
||||
:var member
|
||||
) /*cfe.
|
||||
error=unreachable,
|
||||
space=<invalid>(A<int>.member: void Function(int) (void Function(int)))
|
||||
*/ /*analyzer.
|
||||
error=unreachable,
|
||||
space=Never(A<int>.member: void Function(int) (void Function(int)))
|
||||
*/ /*analyzer.
|
||||
error=unreachable,
|
||||
space=A<int>(A<int>.member: void Function(int) (void Function(int)))
|
||||
*/
|
||||
|
@ -93,9 +94,9 @@ unreachable(
|
|||
nonExhaustiveRestricted(
|
||||
A<num>
|
||||
a) => /*cfe.
|
||||
fields={A<int>.member:void Function(int),A<num>.member:void Function(num)},
|
||||
type=<invalid>
|
||||
*/ /*analyzer.
|
||||
fields={A<int>.member:void Function(int),A<num>.member:void Function(num)},
|
||||
type=Never
|
||||
*/ /*analyzer.
|
||||
checkingOrder={Object?,Object,Null},
|
||||
error=non-exhaustive:Object(),
|
||||
fields={A<int>.member:void Function(int),A<num>.member:void Function(num)},
|
||||
|
@ -105,14 +106,14 @@ nonExhaustiveRestricted(
|
|||
switch (o) {
|
||||
A<num>(
|
||||
:void Function(num) member
|
||||
) /*cfe.space=<invalid>(A<num>.member: void Function(num) (void Function(num)))*/ /*analyzer.space=A<num>(A<num>.member: void Function(num) (void Function(num)))*/ =>
|
||||
) /*cfe.space=Never(A<num>.member: void Function(num) (void Function(num)))*/ /*analyzer.space=A<num>(A<num>.member: void Function(num) (void Function(num)))*/ =>
|
||||
1,
|
||||
A<int>(
|
||||
:var member
|
||||
) /*cfe.
|
||||
error=unreachable,
|
||||
space=<invalid>(A<int>.member: void Function(int) (void Function(int)))
|
||||
*/ /*analyzer.
|
||||
error=unreachable,
|
||||
space=Never(A<int>.member: void Function(int) (void Function(int)))
|
||||
*/ /*analyzer.
|
||||
error=unreachable,
|
||||
space=A<int>(A<int>.member: void Function(int) (void Function(int)))
|
||||
*/
|
||||
|
@ -135,3 +136,17 @@ intersection(o) {
|
|||
A<num>(member: var member2):
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(johnniwinther): This should be exhaustive.
|
||||
num exhaustiveMixed(
|
||||
I<num>
|
||||
i) => /*
|
||||
error=non-exhaustive:I<num>(member: double()),
|
||||
fields={I<num>.member:num,J<num>.member:num},
|
||||
type=I<num>
|
||||
*/
|
||||
switch (i) {
|
||||
I<num>(:int member) /*space=I<num>(I<num>.member: int (num))*/ => member,
|
||||
J<num>(:double member) /*space=J<num>(J<num>.member: double (num))*/ =>
|
||||
member,
|
||||
};
|
||||
|
|
|
@ -13,9 +13,30 @@ membersMethod(o) {
|
|||
*/
|
||||
switch (o) {
|
||||
Typedef(:var hashCode) /*space=Never(hashCode: ∅)*/ => hashCode,
|
||||
Typedef(:var runtimeType) /*space=Never(runtimeType: ∅)*/ => runtimeType,
|
||||
Typedef(:var toString) /*space=Never(toString: ∅)*/ => toString(),
|
||||
Typedef(:var noSuchMethod) /*space=Never(noSuchMethod: ∅)*/ => noSuchMethod,
|
||||
Typedef(
|
||||
:var runtimeType
|
||||
) /*
|
||||
error=unreachable,
|
||||
space=Never(runtimeType: ∅)
|
||||
*/
|
||||
=>
|
||||
runtimeType,
|
||||
Typedef(
|
||||
:var toString
|
||||
) /*
|
||||
error=unreachable,
|
||||
space=Never(toString: ∅)
|
||||
*/
|
||||
=>
|
||||
toString(),
|
||||
Typedef(
|
||||
:var noSuchMethod
|
||||
) /*
|
||||
error=unreachable,
|
||||
space=Never(noSuchMethod: ∅)
|
||||
*/
|
||||
=>
|
||||
noSuchMethod,
|
||||
_ /*space=()*/ => null,
|
||||
};
|
||||
}
|
||||
|
@ -69,7 +90,6 @@ exhaustiveNoSuchMethod(Typedef o) {
|
|||
|
||||
nonExhaustiveRestrictedValue(Typedef o) {
|
||||
return /*
|
||||
error=non-exhaustive:Never(hashCode: int())/Never(),
|
||||
fields={hashCode:int},
|
||||
type=Never
|
||||
*/
|
||||
|
@ -80,7 +100,6 @@ nonExhaustiveRestrictedValue(Typedef o) {
|
|||
|
||||
nonExhaustiveRestrictedType(Typedef o) {
|
||||
return /*
|
||||
error=non-exhaustive:Never(noSuchMethod: dynamic Function(Invocation) _)/Never(),
|
||||
fields={noSuchMethod:dynamic Function(Invocation)},
|
||||
type=Never
|
||||
*/
|
||||
|
@ -94,14 +113,34 @@ nonExhaustiveRestrictedType(Typedef o) {
|
|||
|
||||
unreachableMethod(Typedef o) {
|
||||
return /*
|
||||
error=non-exhaustive:Never(hashCode: int(), noSuchMethod: dynamic Function(Invocation) _, runtimeType: Type(), toString: String Function() _)/Never(),
|
||||
fields={hashCode:int,noSuchMethod:dynamic Function(Invocation),runtimeType:Type,toString:String Function()},
|
||||
type=Never
|
||||
*/
|
||||
switch (o) {
|
||||
Typedef(:var hashCode) /*space=Never(hashCode: ∅)*/ => hashCode,
|
||||
Typedef(:var runtimeType) /*space=Never(runtimeType: ∅)*/ => runtimeType,
|
||||
Typedef(:var toString) /*space=Never(toString: ∅)*/ => toString(),
|
||||
Typedef(:var noSuchMethod) /*space=Never(noSuchMethod: ∅)*/ => noSuchMethod,
|
||||
Typedef(
|
||||
:var runtimeType
|
||||
) /*
|
||||
error=unreachable,
|
||||
space=Never(runtimeType: ∅)
|
||||
*/
|
||||
=>
|
||||
runtimeType,
|
||||
Typedef(
|
||||
:var toString
|
||||
) /*
|
||||
error=unreachable,
|
||||
space=Never(toString: ∅)
|
||||
*/
|
||||
=>
|
||||
toString(),
|
||||
Typedef(
|
||||
:var noSuchMethod
|
||||
) /*
|
||||
error=unreachable,
|
||||
space=Never(noSuchMethod: ∅)
|
||||
*/
|
||||
=>
|
||||
noSuchMethod,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1537,7 +1537,11 @@ class ConstantsTransformer extends RemovingTransformer {
|
|||
required bool hasDefault,
|
||||
required bool mustBeExhaustive,
|
||||
required bool isSwitchExpression}) {
|
||||
StaticType type = exhaustivenessCache.getStaticType(expressionType);
|
||||
StaticType type = exhaustivenessCache.getStaticType(
|
||||
// Treat invalid types as empty.
|
||||
expressionType is InvalidType
|
||||
? const NeverType.nonNullable()
|
||||
: expressionType);
|
||||
List<Space> cases = [];
|
||||
PatternConverter patternConverter = new PatternConverter(
|
||||
exhaustivenessCache, staticTypeContext,
|
||||
|
|
|
@ -504,6 +504,7 @@ exchanging
|
|||
execute
|
||||
executor
|
||||
executors
|
||||
exhaust
|
||||
exhausted
|
||||
exhaustively
|
||||
exhaustiveness
|
||||
|
|
Loading…
Reference in a new issue