Implement UP for RecordType(s).
Change-Id: I9960fbdc5cadbd600ad2ae5c9991b83852c428d9
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/255143
Reviewed-by: Samuel Rawlins <srawlins@google.com>
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
diff --git a/pkg/analyzer/lib/src/dart/element/least_upper_bound.dart b/pkg/analyzer/lib/src/dart/element/least_upper_bound.dart
index e6447f4..076b037 100644
--- a/pkg/analyzer/lib/src/dart/element/least_upper_bound.dart
+++ b/pkg/analyzer/lib/src/dart/element/least_upper_bound.dart
@@ -710,6 +710,21 @@
return getLeastUpperBound(T1, _typeSystem.objectNone);
}
+ // Record types.
+ if (T1 is RecordTypeImpl && T2 is RecordTypeImpl) {
+ return _recordType(T1, T2);
+ }
+
+ // UP(RecordType, T2) = UP(Object, T2)
+ if (T1 is RecordTypeImpl) {
+ return getLeastUpperBound(_typeSystem.objectNone, T2);
+ }
+
+ // UP(T1, RecordType) = UP(T1, Object)
+ if (T2 is RecordTypeImpl) {
+ return getLeastUpperBound(T1, _typeSystem.objectNone);
+ }
+
var futureOrResult = _futureOr(T1, T2);
if (futureOrResult != null) {
return futureOrResult;
@@ -896,6 +911,45 @@
return _typeSystem.getGreatestLowerBound(a.type, b.type);
}
+ DartType _recordType(RecordTypeImpl T1, RecordTypeImpl T2) {
+ final positional1 = T1.positionalFields;
+ final positional2 = T2.positionalFields;
+ if (positional1.length != positional2.length) {
+ return _typeSystem.typeProvider.recordType;
+ }
+
+ final named1 = T1.namedFields;
+ final named2 = T2.namedFields;
+ if (named1.length != named2.length) {
+ return _typeSystem.typeProvider.recordType;
+ }
+
+ final fieldTypes = <DartType>[];
+
+ for (var i = 0; i < positional1.length; i++) {
+ final field1 = positional1[i];
+ final field2 = positional2[i];
+ final type = getLeastUpperBound(field1.type, field2.type);
+ fieldTypes.add(type);
+ }
+
+ for (var i = 0; i < named1.length; i++) {
+ final field1 = named1[i];
+ final field2 = named2[i];
+ if (field1.name != field2.name) {
+ return _typeSystem.typeProvider.recordType;
+ }
+ final type = getLeastUpperBound(field1.type, field2.type);
+ fieldTypes.add(type);
+ }
+
+ return RecordTypeImpl(
+ element2: T1.element2,
+ fieldTypes: fieldTypes,
+ nullabilitySuffix: NullabilitySuffix.none,
+ );
+ }
+
/// Return the promoted or declared bound of the type parameter.
DartType _typeParameterBound(TypeParameterTypeImpl type) {
var bound = type.promotedBound ?? type.element2.bound;
diff --git a/pkg/analyzer/lib/src/dart/element/type_provider.dart b/pkg/analyzer/lib/src/dart/element/type_provider.dart
index a16dcb9..d313754 100644
--- a/pkg/analyzer/lib/src/dart/element/type_provider.dart
+++ b/pkg/analyzer/lib/src/dart/element/type_provider.dart
@@ -142,6 +142,7 @@
InterfaceType? _numType;
InterfaceType? _numTypeQuestion;
InterfaceType? _objectType;
+ InterfaceType? _recordType;
InterfaceType? _stackTraceType;
InterfaceType? _streamDynamicType;
InterfaceType? _stringType;
@@ -396,6 +397,13 @@
return _recordElement ??= _getClassElement(_coreLibrary, 'Record');
}
+ InterfaceType get recordType {
+ return _recordType ??= recordElement.instantiate(
+ typeArguments: const [],
+ nullabilitySuffix: NullabilitySuffix.none,
+ );
+ }
+
@override
ClassElement get setElement {
return _setElement ??= _getClassElement(_coreLibrary, 'Set');
diff --git a/pkg/analyzer/test/src/dart/element/string_types.dart b/pkg/analyzer/test/src/dart/element/string_types.dart
new file mode 100644
index 0000000..b346f81
--- /dev/null
+++ b/pkg/analyzer/test/src/dart/element/string_types.dart
@@ -0,0 +1,766 @@
+// Copyright (c) 2022, 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:analyzer/dart/element/element.dart';
+import 'package:analyzer/dart/element/type.dart';
+import 'package:analyzer/dart/element/type_visitor.dart';
+import 'package:test/test.dart';
+
+import '../../../generated/type_system_test.dart';
+
+mixin StringTypes on AbstractTypeSystemTest {
+ final Map<String, DartType> _types = {};
+
+ void assertExpectedString(DartType type, String? expectedString) {
+ if (expectedString != null) {
+ var typeStr = _typeStr(type);
+
+ typeStr += _typeParametersStr(type);
+
+ expect(typeStr, expectedString);
+ }
+ }
+
+ void defineStringTypes() {
+ _defineType('dynamic', dynamicNone);
+ _defineType('void', voidNone);
+
+ _defineType('Never', neverNone);
+ _defineType('Never*', neverStar);
+ _defineType('Never?', neverQuestion);
+
+ _defineType('Null?', nullQuestion);
+
+ _defineType('Object', objectNone);
+ _defineType('Object*', objectStar);
+ _defineType('Object?', objectQuestion);
+
+ _defineType('Comparable<Object*>*', comparableStar(objectStar));
+ _defineType('Comparable<num*>*', comparableStar(numStar));
+ _defineType('Comparable<int*>*', comparableStar(intStar));
+
+ _defineType('num', numNone);
+ _defineType('num*', numStar);
+ _defineType('num?', numQuestion);
+
+ _defineType('int', intNone);
+ _defineType('int*', intStar);
+ _defineType('int?', intQuestion);
+
+ _defineType('double', doubleNone);
+ _defineType('double*', doubleStar);
+ _defineType('double?', doubleQuestion);
+
+ _defineType('List<Object*>*', listStar(objectStar));
+ _defineType('List<num*>*', listStar(numStar));
+ _defineType('List<int>', listNone(intNone));
+ _defineType('List<int>*', listStar(intNone));
+ _defineType('List<int>?', listQuestion(intNone));
+ _defineType('List<int*>', listNone(intStar));
+ _defineType('List<int*>*', listStar(intStar));
+ _defineType('List<int*>?', listQuestion(intStar));
+ _defineType('List<int?>', listNone(intQuestion));
+
+ _defineType(
+ 'List<Comparable<Object*>*>*',
+ listStar(
+ comparableStar(objectStar),
+ ),
+ );
+ _defineType(
+ 'List<Comparable<num*>*>*',
+ listStar(
+ comparableStar(numStar),
+ ),
+ );
+ _defineType(
+ 'List<Comparable<Comparable<num*>*>*>*',
+ listStar(
+ comparableStar(
+ comparableStar(numStar),
+ ),
+ ),
+ );
+
+ _defineType('Iterable<Object*>*', iterableStar(objectStar));
+ _defineType('Iterable<int*>*', iterableStar(intStar));
+ _defineType('Iterable<num*>*', iterableStar(numStar));
+
+ _defineFunctionTypes();
+ _defineFutureTypes();
+ _defineRecordTypes();
+ }
+
+ DartType typeOfString(String str) {
+ var type = _types[str];
+ if (type == null) {
+ fail('No DartType for: $str');
+ }
+ return type;
+ }
+
+ void _defineFunctionTypes() {
+ _defineType('Function', functionNone);
+ _defineType('Function*', functionStar);
+ _defineType('Function?', functionQuestion);
+
+ _defineType(
+ 'void Function()',
+ functionTypeNone(
+ returnType: voidNone,
+ ),
+ );
+
+ _defineType(
+ 'int* Function()',
+ functionTypeNone(
+ returnType: intStar,
+ ),
+ );
+ _defineType(
+ 'int* Function()*',
+ functionTypeStar(
+ returnType: intStar,
+ ),
+ );
+ _defineType(
+ 'int* Function()?',
+ functionTypeQuestion(
+ returnType: intStar,
+ ),
+ );
+
+ _defineType(
+ 'num Function(num)',
+ functionTypeNone(
+ parameters: [requiredParameter(type: numNone)],
+ returnType: numNone,
+ ),
+ );
+ _defineType(
+ 'num Function(num)?',
+ functionTypeQuestion(
+ parameters: [requiredParameter(type: numNone)],
+ returnType: numNone,
+ ),
+ );
+ _defineType(
+ 'num Function(num)*',
+ functionTypeStar(
+ parameters: [requiredParameter(type: numNone)],
+ returnType: numNone,
+ ),
+ );
+
+ _defineType(
+ 'num* Function(num*)',
+ functionTypeNone(
+ parameters: [requiredParameter(type: numStar)],
+ returnType: numStar,
+ ),
+ );
+ _defineType(
+ 'num* Function(num*)*',
+ functionTypeStar(
+ parameters: [requiredParameter(type: numStar)],
+ returnType: numStar,
+ ),
+ );
+ _defineType(
+ 'num* Function(num*)?',
+ functionTypeQuestion(
+ parameters: [requiredParameter(type: numStar)],
+ returnType: numStar,
+ ),
+ );
+
+ _defineType(
+ 'int* Function(num*)*',
+ functionTypeStar(
+ parameters: [requiredParameter(type: numStar)],
+ returnType: intStar,
+ ),
+ );
+
+ _defineType(
+ 'num* Function(int*)',
+ functionTypeNone(
+ parameters: [requiredParameter(type: intStar)],
+ returnType: numStar,
+ ),
+ );
+ _defineType(
+ 'num* Function(int*)*',
+ functionTypeStar(
+ parameters: [requiredParameter(type: intStar)],
+ returnType: numStar,
+ ),
+ );
+ _defineType(
+ 'num* Function(int*)?',
+ functionTypeQuestion(
+ parameters: [requiredParameter(type: intStar)],
+ returnType: numStar,
+ ),
+ );
+
+ _defineType(
+ 'int* Function(int*)*',
+ functionTypeStar(
+ parameters: [requiredParameter(type: intStar)],
+ returnType: intStar,
+ ),
+ );
+
+ _defineType(
+ 'num Function(num?)',
+ functionTypeNone(
+ parameters: [requiredParameter(type: numQuestion)],
+ returnType: numNone,
+ ),
+ );
+ _defineType(
+ 'num? Function(num)',
+ functionTypeNone(
+ parameters: [requiredParameter(type: numNone)],
+ returnType: numQuestion,
+ ),
+ );
+ _defineType(
+ 'num? Function(num?)',
+ functionTypeNone(
+ parameters: [requiredParameter(type: numQuestion)],
+ returnType: numQuestion,
+ ),
+ );
+
+ _defineType(
+ 'num Function({num x})',
+ functionTypeNone(
+ parameters: [namedParameter(name: 'x', type: numNone)],
+ returnType: numNone,
+ ),
+ );
+ _defineType(
+ 'num Function({num? x})',
+ functionTypeNone(
+ parameters: [namedParameter(name: 'x', type: numQuestion)],
+ returnType: numNone,
+ ),
+ );
+ _defineType(
+ 'num? Function({num x})',
+ functionTypeNone(
+ parameters: [namedParameter(name: 'x', type: numNone)],
+ returnType: numQuestion,
+ ),
+ );
+ _defineType(
+ 'num? Function({num? x})',
+ functionTypeNone(
+ parameters: [namedParameter(name: 'x', type: numQuestion)],
+ returnType: numQuestion,
+ ),
+ );
+
+ _defineType(
+ 'num Function([num])',
+ functionTypeNone(
+ parameters: [positionalParameter(type: numNone)],
+ returnType: numNone,
+ ),
+ );
+ _defineType(
+ 'num Function([num?])',
+ functionTypeNone(
+ parameters: [positionalParameter(type: numQuestion)],
+ returnType: numNone,
+ ),
+ );
+ _defineType(
+ 'num? Function([num])',
+ functionTypeNone(
+ parameters: [positionalParameter(type: numNone)],
+ returnType: numQuestion,
+ ),
+ );
+ _defineType(
+ 'num? Function([num?])',
+ functionTypeNone(
+ parameters: [positionalParameter(type: numQuestion)],
+ returnType: numQuestion,
+ ),
+ );
+ }
+
+ void _defineFutureTypes() {
+ _defineType('FutureOr<Object*>*', futureOrStar(objectStar));
+ _defineType('FutureOr<num*>*', futureOrStar(numStar));
+ _defineType('FutureOr<int*>*', futureOrStar(intStar));
+ _defineType('FutureOr<num?>?', futureOrQuestion(numQuestion));
+
+ _defineType('FutureOr<Object>', futureOrNone(objectNone));
+ _defineType('FutureOr<Object>?', futureOrQuestion(objectNone));
+ _defineType('FutureOr<Object?>', futureOrNone(objectQuestion));
+ _defineType('FutureOr<Object?>?', futureOrQuestion(objectQuestion));
+
+ _defineType('Future<num>', futureNone(numNone));
+ _defineType('Future<num>?', futureQuestion(numNone));
+ _defineType('Future<num?>', futureNone(numQuestion));
+ _defineType('Future<num?>?', futureQuestion(numQuestion));
+
+ _defineType('FutureOr<int>', futureOrNone(intNone));
+ _defineType('FutureOr<int>?', futureOrQuestion(intNone));
+ _defineType('FutureOr<int?>', futureOrNone(intQuestion));
+ _defineType('FutureOr<int?>?', futureOrQuestion(intQuestion));
+
+ _defineType('FutureOr<int>*', futureOrStar(intNone));
+ _defineType('FutureOr<int*>', futureOrNone(intStar));
+ _defineType('Future<int*>*', futureStar(intStar));
+
+ _defineType('FutureOr<num>', futureOrNone(numNone));
+ _defineType('FutureOr<num>*', futureOrStar(numNone));
+ _defineType('FutureOr<num>?', futureOrQuestion(numNone));
+
+ _defineType('FutureOr<num*>', futureOrNone(numStar));
+ _defineType('FutureOr<num?>', futureOrNone(numQuestion));
+
+ _defineType('Future<Object>', futureNone(objectNone));
+ _defineType(
+ 'FutureOr<Future<Object>>',
+ futureOrNone(
+ futureNone(objectNone),
+ ),
+ );
+ _defineType(
+ 'FutureOr<Future<Object>>?',
+ futureOrQuestion(
+ futureNone(objectNone),
+ ),
+ );
+ _defineType(
+ 'FutureOr<Future<Object>?>',
+ futureOrNone(
+ futureQuestion(objectNone),
+ ),
+ );
+ _defineType(
+ 'FutureOr<Future<Object>?>?',
+ futureOrQuestion(
+ futureQuestion(objectNone),
+ ),
+ );
+
+ _defineType(
+ 'Future<Future<num>>?',
+ futureQuestion(
+ futureNone(numNone),
+ ),
+ );
+ _defineType(
+ 'Future<Future<num?>?>?',
+ futureQuestion(
+ futureQuestion(numQuestion),
+ ),
+ );
+
+ _defineType(
+ 'Future<Future<Future<num>>>?',
+ futureQuestion(
+ futureNone(
+ futureNone(numNone),
+ ),
+ ),
+ );
+ _defineType(
+ 'Future<Future<Future<num?>?>?>?',
+ futureQuestion(
+ futureQuestion(
+ futureQuestion(numQuestion),
+ ),
+ ),
+ );
+
+ _defineType(
+ 'FutureOr<FutureOr<FutureOr<num>>?>',
+ futureOrNone(
+ futureOrQuestion(
+ futureOrNone(numNone),
+ ),
+ ),
+ );
+ _defineType(
+ 'FutureOr<FutureOr<FutureOr<num?>>>',
+ futureOrNone(
+ futureOrNone(
+ futureOrNone(numQuestion),
+ ),
+ ),
+ );
+ }
+
+ void _defineRecordTypes() {
+ _defineType('Record', recordNone);
+
+ _defineType(
+ '(double)',
+ recordTypeNone(
+ element: recordElement(
+ positionalFields: [
+ recordPositionalField(type: doubleNone),
+ ],
+ ),
+ ),
+ );
+ _defineType(
+ '(int)',
+ recordTypeNone(
+ element: recordElement(
+ positionalFields: [
+ recordPositionalField(type: intNone),
+ ],
+ ),
+ ),
+ );
+ _defineType(
+ '(num)',
+ recordTypeNone(
+ element: recordElement(
+ positionalFields: [
+ recordPositionalField(type: numNone),
+ ],
+ ),
+ ),
+ );
+
+ _defineType(
+ '(double, int)',
+ recordTypeNone(
+ element: recordElement(
+ positionalFields: [
+ recordPositionalField(type: doubleNone),
+ recordPositionalField(type: intNone),
+ ],
+ ),
+ ),
+ );
+ _defineType(
+ '(int, double)',
+ recordTypeNone(
+ element: recordElement(
+ positionalFields: [
+ recordPositionalField(type: intNone),
+ recordPositionalField(type: doubleNone),
+ ],
+ ),
+ ),
+ );
+ _defineType(
+ '(int, Object)',
+ recordTypeNone(
+ element: recordElement(
+ positionalFields: [
+ recordPositionalField(type: intNone),
+ recordPositionalField(type: objectNone),
+ ],
+ ),
+ ),
+ );
+ _defineType(
+ '(int, String)',
+ recordTypeNone(
+ element: recordElement(
+ positionalFields: [
+ recordPositionalField(type: intNone),
+ recordPositionalField(type: stringNone),
+ ],
+ ),
+ ),
+ );
+ _defineType(
+ '(num, num)',
+ recordTypeNone(
+ element: recordElement(
+ positionalFields: [
+ recordPositionalField(type: numNone),
+ recordPositionalField(type: numNone),
+ ],
+ ),
+ ),
+ );
+ _defineType(
+ '(num, Object)',
+ recordTypeNone(
+ element: recordElement(
+ positionalFields: [
+ recordPositionalField(type: numNone),
+ recordPositionalField(type: objectNone),
+ ],
+ ),
+ ),
+ );
+ _defineType(
+ '(num, String)',
+ recordTypeNone(
+ element: recordElement(
+ positionalFields: [
+ recordPositionalField(type: numNone),
+ recordPositionalField(type: stringNone),
+ ],
+ ),
+ ),
+ );
+
+ _defineType(
+ '({double f1})',
+ recordTypeNone(
+ element: recordElement(
+ namedFields: [
+ recordNamedField(name: 'f1', type: doubleNone),
+ ],
+ ),
+ ),
+ );
+ _defineType(
+ '({int f1})',
+ recordTypeNone(
+ element: recordElement(
+ namedFields: [
+ recordNamedField(name: 'f1', type: intNone),
+ ],
+ ),
+ ),
+ );
+ _defineType(
+ '({num f1})',
+ recordTypeNone(
+ element: recordElement(
+ namedFields: [
+ recordNamedField(name: 'f1', type: numNone),
+ ],
+ ),
+ ),
+ );
+ _defineType(
+ '({int f2})',
+ recordTypeNone(
+ element: recordElement(
+ namedFields: [
+ recordNamedField(name: 'f2', type: intNone),
+ ],
+ ),
+ ),
+ );
+ _defineType(
+ '({double f1, int f2})',
+ recordTypeNone(
+ element: recordElement(
+ namedFields: [
+ recordNamedField(name: 'f1', type: doubleNone),
+ recordNamedField(name: 'f2', type: intNone),
+ ],
+ ),
+ ),
+ );
+ _defineType(
+ '({int f1, double f2})',
+ recordTypeNone(
+ element: recordElement(
+ namedFields: [
+ recordNamedField(name: 'f1', type: intNone),
+ recordNamedField(name: 'f2', type: doubleNone),
+ ],
+ ),
+ ),
+ );
+ _defineType(
+ '({int f1, String f2})',
+ recordTypeNone(
+ element: recordElement(
+ namedFields: [
+ recordNamedField(name: 'f1', type: intNone),
+ recordNamedField(name: 'f2', type: stringNone),
+ ],
+ ),
+ ),
+ );
+ _defineType(
+ '({int f1, Object f2})',
+ recordTypeNone(
+ element: recordElement(
+ namedFields: [
+ recordNamedField(name: 'f1', type: intNone),
+ recordNamedField(name: 'f2', type: objectNone),
+ ],
+ ),
+ ),
+ );
+ _defineType(
+ '({num f1, num f2})',
+ recordTypeNone(
+ element: recordElement(
+ namedFields: [
+ recordNamedField(name: 'f1', type: numNone),
+ recordNamedField(name: 'f2', type: numNone),
+ ],
+ ),
+ ),
+ );
+ _defineType(
+ '({num f1, String f2})',
+ recordTypeNone(
+ element: recordElement(
+ namedFields: [
+ recordNamedField(name: 'f1', type: numNone),
+ recordNamedField(name: 'f2', type: stringNone),
+ ],
+ ),
+ ),
+ );
+ _defineType(
+ '({num f1, Object f2})',
+ recordTypeNone(
+ element: recordElement(
+ namedFields: [
+ recordNamedField(name: 'f1', type: numNone),
+ recordNamedField(name: 'f2', type: objectNone),
+ ],
+ ),
+ ),
+ );
+
+ _defineType(
+ '(int, {String f2})',
+ recordTypeNone(
+ element: recordElement(
+ positionalFields: [
+ recordPositionalField(type: intNone),
+ ],
+ namedFields: [
+ recordNamedField(name: 'f2', type: stringNone),
+ ],
+ ),
+ ),
+ );
+ _defineType(
+ '(int, {Object f2})',
+ recordTypeNone(
+ element: recordElement(
+ positionalFields: [
+ recordPositionalField(type: intNone),
+ ],
+ namedFields: [
+ recordNamedField(name: 'f2', type: objectNone),
+ ],
+ ),
+ ),
+ );
+ }
+
+ void _defineType(String str, DartType type) {
+ if (_typeStr(type) != str) {
+ fail('Expected: $str\nActual: ${_typeStr(type)}');
+ }
+
+ for (var entry in _types.entries) {
+ var key = entry.key;
+ if (key == 'Never' || _typeStr(type) == 'Never') {
+ // We have aliases for Never.
+ } else {
+ var value = entry.value;
+ if (key == str) {
+ fail('Duplicate type: $str; existing: $value; new: $type');
+ }
+ if (_typeStr(value) == _typeStr(type)) {
+ fail('Duplicate type: $str');
+ }
+ }
+ }
+ _types[str] = type;
+ }
+
+ String _typeParametersStr(DartType type) {
+ var typeStr = '';
+
+ var typeParameterCollector = _TypeParameterCollector();
+ type.accept(typeParameterCollector);
+ for (var typeParameter in typeParameterCollector.typeParameters) {
+ typeStr += ', $typeParameter';
+ }
+ return typeStr;
+ }
+
+ static String _typeStr(DartType type) {
+ return type.getDisplayString(withNullability: true);
+ }
+}
+
+class _TypeParameterCollector extends TypeVisitor<void> {
+ final Set<String> typeParameters = {};
+
+ /// We don't need to print bounds for these type parameters, because
+ /// they are already included into the function type itself, and cannot
+ /// be promoted.
+ final Set<TypeParameterElement> functionTypeParameters = {};
+
+ @override
+ void visitDynamicType(DynamicType type) {}
+
+ @override
+ void visitFunctionType(FunctionType type) {
+ functionTypeParameters.addAll(type.typeFormals);
+ for (var typeParameter in type.typeFormals) {
+ var bound = typeParameter.bound;
+ if (bound != null) {
+ bound.accept(this);
+ }
+ }
+ for (var parameter in type.parameters) {
+ parameter.type.accept(this);
+ }
+ type.returnType.accept(this);
+ }
+
+ @override
+ void visitInterfaceType(InterfaceType type) {
+ for (var typeArgument in type.typeArguments) {
+ typeArgument.accept(this);
+ }
+ }
+
+ @override
+ void visitNeverType(NeverType type) {}
+
+ @override
+ void visitRecordType(RecordType type) {
+ final fields = [
+ ...type.positionalFields,
+ ...type.namedFields,
+ ];
+ for (final field in fields) {
+ field.type.accept(this);
+ }
+ }
+
+ @override
+ void visitTypeParameterType(TypeParameterType type) {
+ if (!functionTypeParameters.contains(type.element2)) {
+ var bound = type.element2.bound;
+
+ if (bound == null) {
+ return;
+ }
+
+ var str = '';
+
+ var boundStr = bound.getDisplayString(withNullability: true);
+ str += '${type.element2.name} extends $boundStr';
+
+ typeParameters.add(str);
+ }
+ }
+
+ @override
+ void visitVoidType(VoidType type) {}
+}
diff --git a/pkg/analyzer/test/src/dart/element/subtype_test.dart b/pkg/analyzer/test/src/dart/element/subtype_test.dart
index cd2c985..a53a92f 100644
--- a/pkg/analyzer/test/src/dart/element/subtype_test.dart
+++ b/pkg/analyzer/test/src/dart/element/subtype_test.dart
@@ -5,12 +5,12 @@
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/nullability_suffix.dart';
import 'package:analyzer/dart/element/type.dart';
-import 'package:analyzer/dart/element/type_visitor.dart';
import 'package:analyzer/src/dart/resolver/variance.dart';
import 'package:test/test.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
import '../../../generated/type_system_test.dart';
+import 'string_types.dart';
main() {
defineReflectiveSuite(() {
@@ -20,19 +20,7 @@
}
@reflectiveTest
-class SubtypeTest extends _SubtypingTestBase {
- final Map<String, DartType> _types = {};
-
- void assertExpectedString(DartType type, String? expectedString) {
- if (expectedString != null) {
- var typeStr = _typeStr(type);
-
- typeStr += _typeParametersStr(type);
-
- expect(typeStr, expectedString);
- }
- }
-
+class SubtypeTest extends _SubtypingTestBase with StringTypes {
void isNotSubtype(
DartType T0,
DartType T1, {
@@ -48,8 +36,8 @@
String strT0,
String strT1,
) {
- var T0 = _getTypeByStr(strT0);
- var T1 = _getTypeByStr(strT1);
+ var T0 = typeOfString(strT0);
+ var T1 = typeOfString(strT1);
expect(typeSystem.isSubtypeOf(T0, T1), isFalse);
}
@@ -75,15 +63,15 @@
String strT0,
String strT1,
) {
- var T0 = _getTypeByStr(strT0);
- var T1 = _getTypeByStr(strT1);
+ var T0 = typeOfString(strT0);
+ var T1 = typeOfString(strT1);
expect(typeSystem.isSubtypeOf(T0, T1), isTrue);
}
@override
void setUp() {
super.setUp();
- _defineTypes();
+ defineStringTypes();
}
test_functionType_01() {
@@ -4122,17 +4110,17 @@
}
test_record_functionType() {
- isNotSubtype2('({int foo})', 'void Function()');
+ isNotSubtype2('({int f1})', 'void Function()');
}
test_record_interfaceType() {
- isNotSubtype2('({int foo})', 'int');
- isNotSubtype2('int', '({int foo})');
+ isNotSubtype2('({int f1})', 'int');
+ isNotSubtype2('int', '({int f1})');
}
test_record_Never() {
- isNotSubtype2('({int foo})', 'Never');
- isSubtype2('Never', '({int foo})');
+ isNotSubtype2('({int f1})', 'Never');
+ isSubtype2('Never', '({int f1})');
}
test_record_record2_differentShape() {
@@ -4143,8 +4131,8 @@
check('(int)', '(int, String)');
- check('({int foo, String bar})', '({int foo})');
- check('({int foo})', '({int bar})');
+ check('({int f1, String f2})', '({int f1})');
+ check('({int f1})', '({int f2})');
}
test_record_record2_sameShape_mixed() {
@@ -4153,7 +4141,7 @@
isNotSubtype2(superType, subType);
}
- check('(int, {String bar})', '(int, {Object bar})');
+ check('(int, {String f2})', '(int, {Object f2})');
}
test_record_record2_sameShape_named() {
@@ -4162,12 +4150,12 @@
isNotSubtype2(superType, subType);
}
- check('({int foo})', '({num foo})');
+ check('({int f1})', '({num f1})');
- isSubtype2('({int foo, String bar})', '({int foo, String bar})');
- check('({int foo, String bar})', '({int foo, Object bar})');
- check('({int foo, String bar})', '({num foo, String bar})');
- check('({int foo, String bar})', '({num foo, Object bar})');
+ isSubtype2('({int f1, String f2})', '({int f1, String f2})');
+ check('({int f1, String f2})', '({int f1, Object f2})');
+ check('({int f1, String f2})', '({num f1, String f2})');
+ check('({int f1, String f2})', '({num f1, Object f2})');
}
test_record_record2_sameShape_named_order() {
@@ -4180,20 +4168,20 @@
recordTypeNone(
element: recordElement(
namedFields: [
- recordNamedField(name: 'foo01', type: intNone),
- recordNamedField(name: 'foo02', type: intNone),
- recordNamedField(name: 'foo03', type: intNone),
- recordNamedField(name: 'foo04', type: intNone),
+ recordNamedField(name: 'f1', type: intNone),
+ recordNamedField(name: 'f2', type: intNone),
+ recordNamedField(name: 'f3', type: intNone),
+ recordNamedField(name: 'f4', type: intNone),
],
),
),
recordTypeNone(
element: recordElement(
namedFields: [
- recordNamedField(name: 'foo04', type: intNone),
- recordNamedField(name: 'foo03', type: intNone),
- recordNamedField(name: 'foo02', type: intNone),
- recordNamedField(name: 'foo01', type: intNone),
+ recordNamedField(name: 'f4', type: intNone),
+ recordNamedField(name: 'f3', type: intNone),
+ recordNamedField(name: 'f2', type: intNone),
+ recordNamedField(name: 'f1', type: intNone),
],
),
),
@@ -4215,9 +4203,9 @@
}
test_record_top() {
- isSubtype2('({int foo})', 'dynamic');
- isSubtype2('({int foo})', 'Object');
- isSubtype2('({int foo})', 'Record');
+ isSubtype2('({int f1})', 'dynamic');
+ isSubtype2('({int f1})', 'Object');
+ isSubtype2('({int f1})', 'Record');
}
/// The class `Record` is a subtype of `Object` and `dynamic`, and a
@@ -5336,577 +5324,6 @@
strT1: 'FutureOr<T>, T extends FutureOr<T>',
);
}
-
- void _defineType(String str, DartType type) {
- for (var entry in _types.entries) {
- var key = entry.key;
- if (key == 'Never' || _typeStr(type) == 'Never') {
- // We have aliases for Never.
- } else {
- var value = entry.value;
- if (key == str) {
- fail('Duplicate type: $str; existing: $value; new: $type');
- }
- if (_typeStr(value) == _typeStr(type)) {
- fail('Duplicate type: $str');
- }
- }
- }
- _types[str] = type;
- }
-
- void _defineTypes() {
- _defineType('dynamic', dynamicNone);
- _defineType('void', voidNone);
-
- _defineType('Never', neverNone);
- _defineType('Never*', neverStar);
- _defineType('Never?', neverQuestion);
-
- _defineType('Null?', nullQuestion);
-
- _defineType('Object', objectNone);
- _defineType('Object*', objectStar);
- _defineType('Object?', objectQuestion);
-
- _defineType('Comparable<Object*>*', comparableStar(objectStar));
- _defineType('Comparable<num*>*', comparableStar(numStar));
- _defineType('Comparable<int*>*', comparableStar(intStar));
-
- _defineType('num', numNone);
- _defineType('num*', numStar);
- _defineType('num?', numQuestion);
-
- _defineType('int', intNone);
- _defineType('int*', intStar);
- _defineType('int?', intQuestion);
-
- _defineType('double', doubleNone);
- _defineType('double*', doubleStar);
- _defineType('double?', doubleQuestion);
-
- _defineType('List<Object*>*', listStar(objectStar));
- _defineType('List<num*>*', listStar(numStar));
- _defineType('List<int>', listNone(intNone));
- _defineType('List<int>*', listStar(intNone));
- _defineType('List<int>?', listQuestion(intNone));
- _defineType('List<int*>', listNone(intStar));
- _defineType('List<int*>*', listStar(intStar));
- _defineType('List<int*>?', listQuestion(intStar));
- _defineType('List<int?>', listNone(intQuestion));
-
- _defineType(
- 'List<Comparable<Object*>*>*',
- listStar(
- comparableStar(objectStar),
- ),
- );
- _defineType(
- 'List<Comparable<num*>*>*',
- listStar(
- comparableStar(numStar),
- ),
- );
- _defineType(
- 'List<Comparable<Comparable<num*>*>*>*',
- listStar(
- comparableStar(
- comparableStar(numStar),
- ),
- ),
- );
-
- _defineType('Iterable<Object*>*', iterableStar(objectStar));
- _defineType('Iterable<int*>*', iterableStar(intStar));
- _defineType('Iterable<num*>*', iterableStar(numStar));
-
- _defineType('Function', functionNone);
- _defineType('Function*', functionStar);
- _defineType('Function?', functionQuestion);
-
- _defineType('FutureOr<Object*>*', futureOrStar(objectStar));
- _defineType('FutureOr<num*>*', futureOrStar(numStar));
- _defineType('FutureOr<int*>*', futureOrStar(intStar));
- _defineType('FutureOr<num?>?', futureOrQuestion(numQuestion));
-
- _defineType('FutureOr<Object>', futureOrNone(objectNone));
- _defineType('FutureOr<Object>?', futureOrQuestion(objectNone));
- _defineType('FutureOr<Object?>', futureOrNone(objectQuestion));
- _defineType('FutureOr<Object?>?', futureOrQuestion(objectQuestion));
-
- _defineType('Future<num>', futureNone(numNone));
- _defineType('Future<num>?', futureQuestion(numNone));
- _defineType('Future<num?>', futureNone(numQuestion));
- _defineType('Future<num?>?', futureQuestion(numQuestion));
-
- _defineType('FutureOr<int>', futureOrNone(intNone));
- _defineType('FutureOr<int>?', futureOrQuestion(intNone));
- _defineType('FutureOr<int?>', futureOrNone(intQuestion));
- _defineType('FutureOr<int?>?', futureOrQuestion(intQuestion));
-
- _defineType('FutureOr<int>*', futureOrStar(intNone));
- _defineType('FutureOr<int*>', futureOrNone(intStar));
- _defineType('Future<int*>*', futureStar(intStar));
-
- _defineType('FutureOr<num>', futureOrNone(numNone));
- _defineType('FutureOr<num>*', futureOrStar(numNone));
- _defineType('FutureOr<num>?', futureOrQuestion(numNone));
-
- _defineType('FutureOr<num*>', futureOrNone(numStar));
- _defineType('FutureOr<num?>', futureOrNone(numQuestion));
-
- _defineType('Future<Object>', futureNone(objectNone));
- _defineType(
- 'FutureOr<Future<Object>>',
- futureOrNone(
- futureNone(objectNone),
- ),
- );
- _defineType(
- 'FutureOr<Future<Object>>?',
- futureOrQuestion(
- futureNone(objectNone),
- ),
- );
- _defineType(
- 'FutureOr<Future<Object>?>',
- futureOrNone(
- futureQuestion(objectNone),
- ),
- );
- _defineType(
- 'FutureOr<Future<Object>?>?',
- futureOrQuestion(
- futureQuestion(objectNone),
- ),
- );
-
- _defineType(
- 'Future<Future<num>>?',
- futureQuestion(
- futureNone(numNone),
- ),
- );
- _defineType(
- 'Future<Future<num?>?>?',
- futureQuestion(
- futureQuestion(numQuestion),
- ),
- );
-
- _defineType(
- 'Future<Future<Future<num>>>?',
- futureQuestion(
- futureNone(
- futureNone(numNone),
- ),
- ),
- );
- _defineType(
- 'Future<Future<Future<num?>?>?>?',
- futureQuestion(
- futureQuestion(
- futureQuestion(numQuestion),
- ),
- ),
- );
-
- _defineType(
- 'FutureOr<FutureOr<FutureOr<num>>?>',
- futureOrNone(
- futureOrQuestion(
- futureOrNone(numNone),
- ),
- ),
- );
- _defineType(
- 'FutureOr<FutureOr<FutureOr<num?>>>',
- futureOrNone(
- futureOrNone(
- futureOrNone(numQuestion),
- ),
- ),
- );
-
- _defineType(
- 'void Function()',
- functionTypeNone(
- returnType: voidNone,
- ),
- );
-
- _defineType(
- 'int* Function()',
- functionTypeNone(
- returnType: intStar,
- ),
- );
- _defineType(
- 'int* Function()*',
- functionTypeStar(
- returnType: intStar,
- ),
- );
- _defineType(
- 'int* Function()?',
- functionTypeQuestion(
- returnType: intStar,
- ),
- );
-
- _defineType(
- 'num Function(num)',
- functionTypeNone(
- parameters: [requiredParameter(type: numNone)],
- returnType: numNone,
- ),
- );
- _defineType(
- 'num Function(num)?',
- functionTypeQuestion(
- parameters: [requiredParameter(type: numNone)],
- returnType: numNone,
- ),
- );
- _defineType(
- 'num Function(num)*',
- functionTypeStar(
- parameters: [requiredParameter(type: numNone)],
- returnType: numNone,
- ),
- );
-
- _defineType(
- 'num* Function(num*)',
- functionTypeNone(
- parameters: [requiredParameter(type: numStar)],
- returnType: numStar,
- ),
- );
- _defineType(
- 'num* Function(num*)*',
- functionTypeStar(
- parameters: [requiredParameter(type: numStar)],
- returnType: numStar,
- ),
- );
- _defineType(
- 'num* Function(num*)?',
- functionTypeQuestion(
- parameters: [requiredParameter(type: numStar)],
- returnType: numStar,
- ),
- );
-
- _defineType(
- 'int* Function(num*)*',
- functionTypeStar(
- parameters: [requiredParameter(type: numStar)],
- returnType: intStar,
- ),
- );
-
- _defineType(
- 'num* Function(int*)',
- functionTypeNone(
- parameters: [requiredParameter(type: intStar)],
- returnType: numStar,
- ),
- );
- _defineType(
- 'num* Function(int*)*',
- functionTypeStar(
- parameters: [requiredParameter(type: intStar)],
- returnType: numStar,
- ),
- );
- _defineType(
- 'num* Function(int*)?',
- functionTypeQuestion(
- parameters: [requiredParameter(type: intStar)],
- returnType: numStar,
- ),
- );
-
- _defineType(
- 'int* Function(int*)*',
- functionTypeStar(
- parameters: [requiredParameter(type: intStar)],
- returnType: intStar,
- ),
- );
-
- _defineType(
- 'num Function(num?)',
- functionTypeNone(
- parameters: [requiredParameter(type: numQuestion)],
- returnType: numNone,
- ),
- );
- _defineType(
- 'num? Function(num)',
- functionTypeNone(
- parameters: [requiredParameter(type: numNone)],
- returnType: numQuestion,
- ),
- );
- _defineType(
- 'num? Function(num?)',
- functionTypeNone(
- parameters: [requiredParameter(type: numQuestion)],
- returnType: numQuestion,
- ),
- );
-
- _defineType(
- 'num Function({num x})',
- functionTypeNone(
- parameters: [namedParameter(name: 'x', type: numNone)],
- returnType: numNone,
- ),
- );
- _defineType(
- 'num Function({num? x})',
- functionTypeNone(
- parameters: [namedParameter(name: 'x', type: numQuestion)],
- returnType: numNone,
- ),
- );
- _defineType(
- 'num? Function({num x})',
- functionTypeNone(
- parameters: [namedParameter(name: 'x', type: numNone)],
- returnType: numQuestion,
- ),
- );
- _defineType(
- 'num? Function({num? x})',
- functionTypeNone(
- parameters: [namedParameter(name: 'x', type: numQuestion)],
- returnType: numQuestion,
- ),
- );
-
- _defineType(
- 'num Function([num])',
- functionTypeNone(
- parameters: [positionalParameter(type: numNone)],
- returnType: numNone,
- ),
- );
- _defineType(
- 'num Function([num?])',
- functionTypeNone(
- parameters: [positionalParameter(type: numQuestion)],
- returnType: numNone,
- ),
- );
- _defineType(
- 'num? Function([num])',
- functionTypeNone(
- parameters: [positionalParameter(type: numNone)],
- returnType: numQuestion,
- ),
- );
- _defineType(
- 'num? Function([num?])',
- functionTypeNone(
- parameters: [positionalParameter(type: numQuestion)],
- returnType: numQuestion,
- ),
- );
-
- _defineType('Record', recordNone);
- _defineType(
- '(int, String)',
- recordTypeNone(
- element: recordElement(
- positionalFields: [
- recordPositionalField(type: intNone),
- recordPositionalField(type: stringNone),
- ],
- ),
- ),
- );
- _defineType(
- '(num, String)',
- recordTypeNone(
- element: recordElement(
- positionalFields: [
- recordPositionalField(type: numNone),
- recordPositionalField(type: stringNone),
- ],
- ),
- ),
- );
- _defineType(
- '(int, Object)',
- recordTypeNone(
- element: recordElement(
- positionalFields: [
- recordPositionalField(type: intNone),
- recordPositionalField(type: objectNone),
- ],
- ),
- ),
- );
- _defineType(
- '(num, Object)',
- recordTypeNone(
- element: recordElement(
- positionalFields: [
- recordPositionalField(type: numNone),
- recordPositionalField(type: objectNone),
- ],
- ),
- ),
- );
- _defineType(
- '(int)',
- recordTypeNone(
- element: recordElement(
- positionalFields: [
- recordPositionalField(type: intNone),
- ],
- ),
- ),
- );
- _defineType(
- '(num)',
- recordTypeNone(
- element: recordElement(
- positionalFields: [
- recordPositionalField(type: numNone),
- ],
- ),
- ),
- );
-
- _defineType(
- '({int foo})',
- recordTypeNone(
- element: recordElement(
- namedFields: [
- recordNamedField(name: 'foo', type: intNone),
- ],
- ),
- ),
- );
- _defineType(
- '({num foo})',
- recordTypeNone(
- element: recordElement(
- namedFields: [
- recordNamedField(name: 'foo', type: numNone),
- ],
- ),
- ),
- );
- _defineType(
- '({int bar})',
- recordTypeNone(
- element: recordElement(
- namedFields: [
- recordNamedField(name: 'bar', type: intNone),
- ],
- ),
- ),
- );
- _defineType(
- '({int foo, String bar})',
- recordTypeNone(
- element: recordElement(
- namedFields: [
- recordNamedField(name: 'foo', type: intNone),
- recordNamedField(name: 'bar', type: stringNone),
- ],
- ),
- ),
- );
- _defineType(
- '({int foo, Object bar})',
- recordTypeNone(
- element: recordElement(
- namedFields: [
- recordNamedField(name: 'foo', type: intNone),
- recordNamedField(name: 'bar', type: objectNone),
- ],
- ),
- ),
- );
- _defineType(
- '({num foo, String bar})',
- recordTypeNone(
- element: recordElement(
- namedFields: [
- recordNamedField(name: 'foo', type: numNone),
- recordNamedField(name: 'bar', type: stringNone),
- ],
- ),
- ),
- );
- _defineType(
- '({num foo, Object bar})',
- recordTypeNone(
- element: recordElement(
- namedFields: [
- recordNamedField(name: 'foo', type: numNone),
- recordNamedField(name: 'bar', type: objectNone),
- ],
- ),
- ),
- );
-
- _defineType(
- '(int, {String bar})',
- recordTypeNone(
- element: recordElement(
- positionalFields: [
- recordPositionalField(type: intNone),
- ],
- namedFields: [
- recordNamedField(name: 'bar', type: stringNone),
- ],
- ),
- ),
- );
- _defineType(
- '(int, {Object bar})',
- recordTypeNone(
- element: recordElement(
- positionalFields: [
- recordPositionalField(type: intNone),
- ],
- namedFields: [
- recordNamedField(name: 'bar', type: objectNone),
- ],
- ),
- ),
- );
- }
-
- DartType _getTypeByStr(String str) {
- var type = _types[str];
- if (type == null) {
- fail('No DartType for: $str');
- }
- return type;
- }
-
- String _typeParametersStr(DartType type) {
- var typeStr = '';
-
- var typeParameterCollector = _TypeParameterCollector();
- type.accept(typeParameterCollector);
- for (var typeParameter in typeParameterCollector.typeParameters) {
- typeStr += ', $typeParameter';
- }
- return typeStr;
- }
-
- static String _typeStr(DartType type) {
- return type.getDisplayString(withNullability: true);
- }
}
@reflectiveTest
@@ -6311,72 +5728,3 @@
}
class _SubtypingTestBase extends AbstractTypeSystemTest {}
-
-class _TypeParameterCollector extends TypeVisitor<void> {
- final Set<String> typeParameters = {};
-
- /// We don't need to print bounds for these type parameters, because
- /// they are already included into the function type itself, and cannot
- /// be promoted.
- final Set<TypeParameterElement> functionTypeParameters = {};
-
- @override
- void visitDynamicType(DynamicType type) {}
-
- @override
- void visitFunctionType(FunctionType type) {
- functionTypeParameters.addAll(type.typeFormals);
- for (var typeParameter in type.typeFormals) {
- var bound = typeParameter.bound;
- if (bound != null) {
- bound.accept(this);
- }
- }
- for (var parameter in type.parameters) {
- parameter.type.accept(this);
- }
- type.returnType.accept(this);
- }
-
- @override
- void visitInterfaceType(InterfaceType type) {
- for (var typeArgument in type.typeArguments) {
- typeArgument.accept(this);
- }
- }
-
- @override
- void visitNeverType(NeverType type) {}
-
- @override
- void visitRecordType(RecordType type) {
- final fields = [
- ...type.positionalFields,
- ...type.namedFields,
- ];
- for (final field in fields) {
- field.type.accept(this);
- }
- }
-
- @override
- void visitTypeParameterType(TypeParameterType type) {
- if (!functionTypeParameters.contains(type.element2)) {
- var bound = type.element2.bound;
-
- if (bound == null) {
- return;
- }
-
- var str = '';
-
- var boundStr = bound.getDisplayString(withNullability: true);
- str += '${type.element2.name} extends $boundStr';
-
- typeParameters.add(str);
- }
- }
-
- @override
- void visitVoidType(VoidType type) {}
-}
diff --git a/pkg/analyzer/test/src/dart/element/upper_lower_bound_test.dart b/pkg/analyzer/test/src/dart/element/upper_lower_bound_test.dart
index 0cd3424..4231cb3 100644
--- a/pkg/analyzer/test/src/dart/element/upper_lower_bound_test.dart
+++ b/pkg/analyzer/test/src/dart/element/upper_lower_bound_test.dart
@@ -15,6 +15,7 @@
import 'package:test_reflective_loader/test_reflective_loader.dart';
import '../../../generated/type_system_test.dart';
+import 'string_types.dart';
main() {
defineReflectiveSuite(() {
@@ -22,6 +23,7 @@
defineReflectiveTests(LowerBoundTest);
defineReflectiveTests(UpperBound_FunctionTypes_Test);
defineReflectiveTests(UpperBound_InterfaceTypes_Test);
+ defineReflectiveTests(UpperBound_RecordTypes_Test);
defineReflectiveTests(UpperBoundTest);
});
}
@@ -1972,23 +1974,26 @@
);
{
- var T = typeParameter('T', bound: numNone);
- var U = typeParameter('U', bound: numNone);
- var R = typeParameter('R', bound: numNone);
- check(
- functionTypeNone(
- returnType: typeParameterTypeNone(T),
- typeFormals: [T],
- ),
- functionTypeNone(
- returnType: typeParameterTypeNone(U),
- typeFormals: [U],
- ),
- functionTypeNone(
- returnType: typeParameterTypeNone(R),
- typeFormals: [R],
- ),
+ final T = typeParameter('T', bound: numNone);
+ final U = typeParameter('U', bound: numNone);
+ final T1 = functionTypeNone(
+ returnType: typeParameterTypeNone(T),
+ typeFormals: [T],
);
+ final T2 = functionTypeNone(
+ returnType: typeParameterTypeNone(U),
+ typeFormals: [U],
+ );
+ {
+ final result = typeSystem.getLeastUpperBound(T1, T2);
+ final resultStr = _typeString(result);
+ expect(resultStr, 'T Function<T extends num>()');
+ }
+ {
+ final result = typeSystem.getLeastUpperBound(T2, T1);
+ final resultStr = _typeString(result);
+ expect(resultStr, 'U Function<U extends num>()');
+ }
}
}
@@ -2546,6 +2551,84 @@
}
@reflectiveTest
+class UpperBound_RecordTypes_Test extends _BoundsTestBase {
+ @override
+ void setUp() {
+ super.setUp();
+ defineStringTypes();
+ }
+
+ test_differentShape() {
+ void check(String T1, String T2) {
+ _checkLeastUpperBound2(T1, T2, 'Record');
+ }
+
+ check('(int)', '(int, String)');
+
+ check('({int f1, String f2})', '({int f1})');
+ check('({int f1})', '({int f2})');
+ }
+
+ test_Never() {
+ _checkLeastUpperBound2('(int)', 'Never', '(int)');
+ }
+
+ test_record_andNot() {
+ _checkLeastUpperBound2('(int)', 'int', 'Object');
+ _checkLeastUpperBound2('(int)', 'void Function()', 'Object');
+ }
+
+ test_sameShape_named() {
+ _checkLeastUpperBound2(
+ '({int f1})',
+ '({int f1})',
+ '({int f1})',
+ );
+
+ _checkLeastUpperBound2(
+ '({int f1})',
+ '({num f1})',
+ '({num f1})',
+ );
+
+ _checkLeastUpperBound2(
+ '({int f1})',
+ '({double f1})',
+ '({num f1})',
+ );
+
+ _checkLeastUpperBound2(
+ '({int f1, double f2})',
+ '({double f1, int f2})',
+ '({num f1, num f2})',
+ );
+ }
+
+ test_sameShape_positional() {
+ _checkLeastUpperBound2('(int)', '(int)', '(int)');
+ _checkLeastUpperBound2('(int)', '(num)', '(num)');
+ _checkLeastUpperBound2('(int)', '(double)', '(num)');
+
+ _checkLeastUpperBound2(
+ '(int, String)',
+ '(int, String)',
+ '(int, String)',
+ );
+
+ _checkLeastUpperBound2(
+ '(int, double)',
+ '(double, int)',
+ '(num, num)',
+ );
+ }
+
+ test_top() {
+ _checkLeastUpperBound2('(int)', 'dynamic', 'dynamic');
+ _checkLeastUpperBound2('(int)', 'Object?', 'Object?');
+ }
+}
+
+@reflectiveTest
class UpperBoundTest extends _BoundsTestBase {
test_bottom_any() {
void check(DartType T1, DartType T2) {
@@ -3339,7 +3422,7 @@
}
@reflectiveTest
-class _BoundsTestBase extends AbstractTypeSystemTest {
+class _BoundsTestBase extends AbstractTypeSystemTest with StringTypes {
void _assertBottom(DartType type) {
if (!typeSystem.isBottom(type)) {
fail('isBottom must be true: ${_typeString(type)}');
@@ -3418,10 +3501,7 @@
var result = typeSystem.getLeastUpperBound(T1, T2);
var resultStr = _typeString(result);
- expect(result, expected, reason: '''
-expected: $expectedStr
-actual: $resultStr
-''');
+ expect(resultStr, expectedStr);
// Check that the result is an upper bound.
expect(typeSystem.isSubtypeOf(T1, result), true);
@@ -3430,10 +3510,15 @@
// Check for symmetry.
result = typeSystem.getLeastUpperBound(T2, T1);
resultStr = _typeString(result);
- expect(result, expected, reason: '''
-expected: $expectedStr
-actual: $resultStr
-''');
+ expect(resultStr, expectedStr);
+ }
+
+ void _checkLeastUpperBound2(String T1, String T2, String expected) {
+ _checkLeastUpperBound(
+ typeOfString(T1),
+ typeOfString(T2),
+ typeOfString(expected),
+ );
}
String _typeParametersStr(DartType type) {