Issue 54686. Fix canBeSubtypeOf() for FutureOr and records.

Bug: https://github.com/dart-lang/sdk/issues/54686
Change-Id: Idbacac0b114f0b84d1703fb1454e0165ea5be69b
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/347643
Reviewed-by: Phil Quitslund <pquitslund@google.com>
Reviewed-by: Samuel Rawlins <srawlins@google.com>
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
This commit is contained in:
Konstantin Shcheglov
2024-01-22 20:06:59 +00:00
committed by Commit Queue
parent 155df2fdbd
commit 4983ea79be
2 changed files with 104 additions and 16 deletions
@@ -182,6 +182,22 @@ class TypeSystemImpl implements TypeSystem {
return left.isDartCoreFunction || left.isDartCoreObject;
}
// FutureOr<T> = T || Future<T>
// So, we attempt to match both to the right.
if (left.isDartAsyncFutureOr) {
final base = futureOrBase(left);
final future = typeProvider.futureType(base);
return canBeSubtypeOf(base, right) || canBeSubtypeOf(future, right);
}
// FutureOr<T> = T || Future<T>
// So, we attempt to match both to the left.
if (right.isDartAsyncFutureOr) {
final base = futureOrBase(right);
final future = typeProvider.futureType(base);
return canBeSubtypeOf(left, base) || canBeSubtypeOf(left, future);
}
if (left is InterfaceTypeImpl && right is InterfaceTypeImpl) {
final leftElement = left.element;
final rightElement = right.element;
@@ -192,22 +208,6 @@ class TypeSystemImpl implements TypeSystem {
return true;
}
// FutureOr<T> = T || Future<T>
// So, we attempt to match both to the right.
if (left.isDartAsyncFutureOr) {
final base = futureOrBase(left);
final future = typeProvider.futureType(base);
return canBeSubtypeOf(base, right) || canBeSubtypeOf(future, right);
}
// FutureOr<T> = T || Future<T>
// So, we attempt to match both to the left.
if (right.isDartAsyncFutureOr) {
final base = futureOrBase(right);
final future = typeProvider.futureType(base);
return canBeSubtypeOf(left, base) || canBeSubtypeOf(left, future);
}
bool canBeSubtypeOfInterfaces(InterfaceType left, InterfaceType right) {
assert(left.element == right.element);
final leftArguments = left.typeArguments;
@@ -858,6 +858,72 @@ enum E<T> { v1<int>(), v2<double>() }
]);
}
test_matchedFutureOrRecord_requiredFutureRecord_match() async {
await assertNoErrorsInCode('''
import 'dart:async';
void f(FutureOr<(int,)> x) {
if (x case Future<(int,)> _) {}
}
''');
}
test_matchedFutureOrRecord_requiredFutureRecord_notMatch() async {
await assertErrorsInCode('''
import 'dart:async';
void f(FutureOr<(int,)> x) {
if (x case Future<(String,)> _) {}
}
''', [
error(WarningCode.PATTERN_NEVER_MATCHES_VALUE_TYPE, 64, 17),
]);
}
test_matchedFutureOrRecord_requiredRecord_match() async {
await assertNoErrorsInCode('''
import 'dart:async';
void f(FutureOr<(int,)> x) {
if (x case (int,) _) {}
}
''');
}
test_matchedFutureOrRecord_requiredRecord_notMatch() async {
await assertErrorsInCode('''
import 'dart:async';
void f(FutureOr<(int,)> x) {
if (x case (String,) _) {}
}
''', [
error(WarningCode.PATTERN_NEVER_MATCHES_VALUE_TYPE, 64, 9),
]);
}
test_matchedFutureRecord_requiredFutureOrRecord_match() async {
await assertNoErrorsInCode('''
import 'dart:async';
void f(Future<(int,)> x) {
if (x case FutureOr<(int,)> _) {}
}
''');
}
test_matchedFutureRecord_requiredFutureOrRecord_notMatch() async {
await assertErrorsInCode('''
import 'dart:async';
void f(Future<(int,)> x) {
if (x case FutureOr<(String,)> _) {}
}
''', [
error(WarningCode.PATTERN_NEVER_MATCHES_VALUE_TYPE, 62, 19),
]);
}
test_matchedNull_requiredNotNullable() async {
await assertErrorsInCode('''
void f(Null x) {
@@ -888,6 +954,28 @@ void f(Null x) {
]);
}
test_matchedRecord_requiredFutureOrRecord_match() async {
await assertNoErrorsInCode('''
import 'dart:async';
void f((int,) x) {
if (x case FutureOr<(int,)> _) {}
}
''');
}
test_matchedRecord_requiredFutureOrRecord_notMatch() async {
await assertErrorsInCode('''
import 'dart:async';
void f((int,) x) {
if (x case FutureOr<(String,)> _) {}
}
''', [
error(WarningCode.PATTERN_NEVER_MATCHES_VALUE_TYPE, 54, 19),
]);
}
test_recordType2_named_differentCount() async {
await assertErrorsInCode('''
void f(({int f1,}) x) {