From 4983ea79be892aa1a910556267fd6fde078fe8e4 Mon Sep 17 00:00:00 2001 From: Konstantin Shcheglov Date: Mon, 22 Jan 2024 20:06:59 +0000 Subject: [PATCH] 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 Reviewed-by: Samuel Rawlins Commit-Queue: Konstantin Shcheglov --- .../lib/src/dart/element/type_system.dart | 32 +++---- ...pattern_never_matches_value_type_test.dart | 88 +++++++++++++++++++ 2 files changed, 104 insertions(+), 16 deletions(-) diff --git a/pkg/analyzer/lib/src/dart/element/type_system.dart b/pkg/analyzer/lib/src/dart/element/type_system.dart index a1b3a9121bc..a680faf93c5 100644 --- a/pkg/analyzer/lib/src/dart/element/type_system.dart +++ b/pkg/analyzer/lib/src/dart/element/type_system.dart @@ -182,6 +182,22 @@ class TypeSystemImpl implements TypeSystem { return left.isDartCoreFunction || left.isDartCoreObject; } + // FutureOr = T || Future + // 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 || Future + // 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 || Future - // 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 || Future - // 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; diff --git a/pkg/analyzer/test/src/diagnostics/pattern_never_matches_value_type_test.dart b/pkg/analyzer/test/src/diagnostics/pattern_never_matches_value_type_test.dart index 72f732668e0..a2eeb85d0e9 100644 --- a/pkg/analyzer/test/src/diagnostics/pattern_never_matches_value_type_test.dart +++ b/pkg/analyzer/test/src/diagnostics/pattern_never_matches_value_type_test.dart @@ -858,6 +858,72 @@ enum E { v1(), v2() } ]); } + 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) {