blob: 87625082e91f9ef57e8d8d2f9bce94f88a6faf3c [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.
import "package:expect/static_type_helper.dart";
test() {
{
// If the matched value type is `Null`, an `== null` pattern is guaranteed
// to match.
int? reachability0 = 0;
int? reachability1 = 0;
if (expr<Null>() case == null) {
reachability0 = null;
} else {
reachability1 = null;
}
reachability0.expectStaticType<Exactly<int?>>();
reachability1.expectStaticType<Exactly<int>>();
}
{
// An `== null` pattern inside a subpattern is guaranteed to match if the
// matched value type of the subpattern is `Null`.
int? reachability0 = 0;
int? reachability1 = 0;
if (expr<(Null,)>() case (== null,)) {
reachability0 = null;
} else {
reachability1 = null;
}
reachability0.expectStaticType<Exactly<int?>>();
reachability1.expectStaticType<Exactly<int>>();
}
{
// In the general case, a relational pattern using `==` may or may not
// match.
int? reachability0 = 0;
int? reachability1 = 0;
if (expr<Object?>() case == 0) {
reachability0 = null;
} else {
reachability1 = null;
}
reachability0.expectStaticType<Exactly<int?>>();
reachability1.expectStaticType<Exactly<int?>>();
}
{
// An `== null` pattern promotes a scrutinee in the case where it does *not*
// match.
int? reachability0 = 0;
int? reachability1 = 0;
var x = expr<int?>();
if (x case == null) {
reachability0 = null;
// Note that in this branch, `x` is known to be `null`, but we don't
// promote to the type `Null`, for consistency with the flow analysis
// behavior of `== null`. See
// https://github.com/dart-lang/language/issues/1505#issuecomment-975706918
// for details.
x.expectStaticType<Exactly<int?>>();
} else {
reachability1 = null;
x.expectStaticType<Exactly<int>>();
}
reachability0.expectStaticType<Exactly<int?>>();
reachability1.expectStaticType<Exactly<int?>>();
}
{
// An `== null` pattern doesn't promote a scrutinee that has been changed in
// a previous guard clause.
int? reachability0 = 0;
int? reachability1 = 0;
var x = expr<int?>();
switch (x) {
case _ when pickSecond(x = expr<int?>(), expr<bool>()):
break;
case == null:
reachability0 = null;
x.expectStaticType<Exactly<int?>>();
case var y:
reachability1 = null;
x.expectStaticType<Exactly<int?>>();
y.expectStaticType<Exactly<int>>();
}
reachability0.expectStaticType<Exactly<int?>>();
reachability1.expectStaticType<Exactly<int?>>();
}
{
// An `== null` pattern cannot match non-nullable types.
// As of Dart 3.9, flow analysis assumes soundness when analyzing all
// patterns, so `== null` cannot match a non-nullable type.
int? reachability0 = 0;
int? reachability1 = 0;
if (expr<int>() case == null) {
reachability0 = null;
} else {
reachability1 = null;
}
reachability0.expectStaticType<Exactly<int>>();
reachability1.expectStaticType<Exactly<int?>>();
}
{
// If a relational pattern using `==` appears inside a record pattern, the
// record's field type is not changed.
var x = expr<(Object?,)>();
if (x case (== const Object(),)) {
x.expectStaticType<Exactly<(Object?,)>>();
}
}
{
// Even if the relational pattern is an `== null` pattern.
var x = expr<(Object?,)>();
if (x case (== null,)) {
x.expectStaticType<Exactly<(Object?,)>>();
}
}
{
// A `!= null` pattern cannot match the `Null` type.
int? reachability0 = 0;
int? reachability1 = 0;
if (expr<Null>() case != null) {
reachability0 = null;
} else {
reachability1 = null;
}
reachability0.expectStaticType<Exactly<int>>();
reachability1.expectStaticType<Exactly<int?>>();
}
{
// In general, a relational pattern using `!=` may or may not match.
int? reachability0 = 0;
int? reachability1 = 0;
if (expr<Object?>() case != 0) {
reachability0 = null;
} else {
reachability1 = null;
}
reachability0.expectStaticType<Exactly<int?>>();
reachability1.expectStaticType<Exactly<int?>>();
}
{
// A `!= null` pattern promotes a scrutinee to non-nullable.
int? reachability0 = 0;
int? reachability1 = 0;
var x = expr<int?>();
if (x case != null) {
reachability0 = null;
x.expectStaticType<Exactly<int>>();
} else {
reachability1 = null;
x.expectStaticType<Exactly<int?>>();
}
reachability0.expectStaticType<Exactly<int?>>();
reachability1.expectStaticType<Exactly<int?>>();
}
{
// A `!= null` pattern doesn't promote a scrutinee that has been changed in
// a previous guard clause.
int? reachability0 = 0;
var x = expr<int?>();
switch (x) {
case _ when pickSecond(x = expr<int?>(), expr<bool>()):
break;
case != null && var y:
reachability0 = null;
x.expectStaticType<Exactly<int?>>();
y.expectStaticType<Exactly<int>>();
}
reachability0.expectStaticType<Exactly<int?>>();
}
{
// A `!= null` pattern promotes the matched value.
if (expr<int?>() case != null && var x) {
x.expectStaticType<Exactly<int>>();
}
}
{
// A `!= null` pattern always matches, if the matched value type is
// non-nullable.
// As of Dart 3.9, flow analysis assumes soundness when analyzing all
// patterns, so `!= null` always matches a non-nullable type.
int? reachability0 = 0;
int? reachability1 = 0;
if (expr<int>() case != null) {
reachability0 = null;
} else {
reachability1 = null;
}
reachability0.expectStaticType<Exactly<int?>>();
reachability1.expectStaticType<Exactly<int>>();
}
{
// If a relational pattern using `!=` appears inside a record pattern, the
// record's field type is not changed.
var x = expr<(Object?,)>();
if (x case (!= const Object(),)) {
x.expectStaticType<Exactly<(Object?,)>>();
}
}
{
// Even if the relational pattern is a `!= null` pattern.
var x = expr<(Object?,)>();
if (x case (!= null,)) {
x.expectStaticType<Exactly<(Object,)>>();
}
}
{
// A relational pattern using a non-equality operator doesn't have any flow
// analysis effects. To verify this, we use an extension method so that `<
// null` is a valid pattern; this ensures that we don't accidentally apply
// the flow analysis rules for `== null` or `!= null`.
int? reachability0 = 0;
int? reachability1 = 0;
if (expr<Null>() case < null) {
reachability0 = null;
} else {
reachability1 = null;
}
reachability0.expectStaticType<Exactly<int?>>();
reachability1.expectStaticType<Exactly<int?>>();
}
{
// If a relational pattern using a non-equality operator appears inside a
// record pattern, the record's field type is not changed.
var x = expr<(int,)>();
if (x case (> 0,)) {
x.expectStaticType<Exactly<(int,)>>();
}
}
}
T expr<T>() => throw UnimplementedError();
T pickSecond<T>(dynamic x, T y) => y;
extension on Null {
bool operator <(Object? _0) => throw UnimplementedError();
}
main() {}