blob: fdabab609a5fca9423ad964f76cb663919c33c2a [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.
// This test verifies proper functionality of the "fallback exhaustiveness
// algorithm", which we are using as a stopgap measure to allow people to try
// out the "patterns" feature before the full exhaustiveness algorithm is ready.
//
// The fallback exhaustiveness algorithm works as follows: a switch is
// considered exhaustive if either (a) it is recognized as exhaustive by flow
// analysis, or (b) it is recognized as exhaustive by the old (pre-patterns)
// exhaustiveness algorithm for enums.
//
// With the enabling of the real exhaustiveness algorithm, these switches should
// still not cause errors.
import 'dart:async';
enum E {
e1,
e2
}
void ignore(Object? value) {}
void hasDefault(bool b) {
// Flow analysis recognizes that the presence of a `default` clause makes a
// switch statement exhaustive.
//
// The real exhaustiveness handles this.
switch (b) {
case true:
break;
default:
break;
}
}
void untypedWildcard(bool b) {
// Flow analysis recognizes that the presence of an untyped wildcard pattern
// (`_`) makes a switch exhaustive.
//
// The real exhaustiveness handles this.
switch (b) {
case true:
break;
case _:
break;
}
ignore(switch (b) {
true => 0,
_ => 1
});
}
void untypedVariable(bool b) {
// Flow analysis recognizes that the presence of an untyped variable pattern
// makes a switch exhaustive.
//
// The real exhaustiveness handles this.
switch (b) {
case true:
break;
case var x:
break;
}
ignore(switch (b) {
true => 0,
var x => 1
});
}
void typedWildcard(bool b) {
// Flow analysis recognizes that the presence of a typed wildcard pattern
// (where the type is a supertype of the scrutinee type) makes a switch
// exhaustive.
//
// The real exhaustiveness handles this.
switch (b) {
case true:
break;
case bool _:
break;
}
switch (b) {
case true:
break;
case Object _:
break;
}
ignore(switch (b) {
true => 0,
bool _ => 1
});
ignore(switch (b) {
true => 0,
Object _ => 1
});
}
void typedVariable(bool b) {
// Flow analysis recognizes that the presence of a typed variable pattern
// (where the type is a supertype of the scrutinee type) makes a switch
// exhaustive.
//
// The real exhaustiveness handles this.
switch (b) {
case true:
break;
case bool x:
break;
}
switch (b) {
case true:
break;
case Object x:
break;
}
ignore(switch (b) {
true => 0,
bool x => 1,
});
ignore(switch (b) {
true => 0,
Object x => 1,
});
}
void objectPattern(bool b) {
// Flow analysis recognizes that the presence of an object pattern with no
// fields (where the type is a supertype of the scrutinee type) makes a switch
// exhaustive.
//
// The real exhaustiveness handles this.
switch (b) {
case true:
break;
case bool():
break;
}
switch (b) {
case true:
break;
case Object():
break;
}
ignore(switch (b) {
true => 0,
bool() => 1
});
ignore(switch (b) {
true => 0,
Object() => 1
});
}
void logicalOrPattern(bool b) {
// Flow analysis recognizes that the presence of a logical-or pattern (where
// one of the arms of the logical-or fully covers the scrutinee type) makes a
// switch exhaustive.
//
// The real exhaustiveness handles this.
switch (b) {
case true || bool():
break;
}
switch (b) {
case bool() || true:
break;
}
ignore(switch (b) {
true || bool() => 0
});
ignore(switch (b) {
bool() || true => 0
});
}
void logicalAndPattern(bool b) {
// Flow analysis recognizes that the presence of a logical-and pattern (where
// both of the arms of the logical-and fully cover the scrutinee type) makes a
// switch exhaustive.
//
// The real exhaustiveness handles this.
switch (b) {
case bool() && Object():
break;
}
ignore(switch (b) {
bool() && Object() => 0
});
}
void castPattern(bool? b) {
// Flow analysis recognizes that the presence of a cast pattern (where the
// inner pattern fully covers the cast type) makes a switch exhaustive.
//
// The real exhaustiveness handles this.
switch (b) {
case bool() as bool:
break;
}
ignore(switch (b) {
bool() as bool => 0
});
}
void nullAssertPattern(bool? b1, bool? b2) {
// Flow analysis recognizes that the presence of a null assert pattern (where
// the inner pattern fully covers the promoted type) makes a switch
// exhaustive.
//
// The real exhaustiveness handles this.
switch (b1) {
case bool()!:
break;
}
ignore(switch (b2) {
bool()! => 0
});
}
void nullPattern(Null n) {
// Flow analysis recognizes that the constant `null` fully covers the type
// `Null`.
//
// The real exhaustiveness handles this.
switch (n) {
case null:
break;
}
ignore(switch (n) {
null => 0
});
}
void parenthesizedPattern(bool b, E e) {
// Flow analysis recognizes that a parenthesized pattern is equivalent to its
// inner pattern.
//
// The real exhaustiveness handles this.
switch (b) {
case (bool()):
break;
}
switch (b) {
case ((bool())):
break;
}
switch (e) {
case (E.e1):
break;
case ((E.e2)):
break;
}
ignore(switch (b) {
(bool()) => 0
});
ignore(switch (b) {
((bool())) => 0
});
ignore(switch (e) {
(E.e1) => 0,
((E.e2)) => 1
});
}
void recordPattern((bool, E) r) {
// Flow analysis recognizes that a record pattern (where the subpatterns fully
// cover the respective field types) make a switch exhaustive.
//
// The real exhaustiveness handles this.
switch (r) {
case (bool(), E()):
break;
}
ignore(switch (r) {
(bool(), E()) => 0
});
}
void factorNullable(bool? b) {
// Flow analysis recognizes that the type `T?` factors into `T` and `Null`.
//
// The real exhaustiveness handles this.
switch (b) {
case null:
break;
case bool():
break;
}
ignore(switch (b) {
null => 0,
bool() => 1
});
}
void factorFutureOr(FutureOr<bool> x) {
// Flow analysis recognizes that a FutureOr<T> type factors into `T` and
// `Future<T>`.
//
// The real exhaustiveness handles this.
switch (x) {
case Future<bool>():
break;
case bool():
break;
}
ignore(switch (x) {
Future<bool>() => 0,
bool() => 1
});
}
void exhaustedEnum(E e) {
// The old exhaustiveness algorithm recognizes that all enum values exhaust an
// enum type.
//
// The real exhaustiveness handles this.
switch (e) {
case E.e1:
break;
case E.e2:
break;
}
ignore(switch (e) {
E.e1 => 0,
E.e2 => 1
});
}
void exhaustedNullableEnum(E? e) {
// The old exhaustiveness algorithm recognizes that all enum values, plus
// `null`, exhaust a nullable enum type.
//
// The real exhaustiveness handles this.
switch (e) {
case E.e1:
break;
case E.e2:
break;
case null:
break;
}
ignore(switch (e) {
E.e1 => 0,
E.e2 => 1,
null => 2
});
}
main() {
hasDefault(false);
untypedWildcard(false);
untypedVariable(false);
typedWildcard(false);
typedVariable(false);
objectPattern(false);
logicalOrPattern(false);
logicalAndPattern(false);
castPattern(false);
nullAssertPattern(false, false);
nullPattern(null);
parenthesizedPattern(false, E.e1);
recordPattern((false, E.e1));
factorNullable(null);
factorFutureOr(false);
exhaustedEnum(E.e1);
exhaustedNullableEnum(E.e1);
}