RecordElement.instantiate(), substitution, type alias.
Change-Id: Icd0e234e57d17a1c86140ed58fc9732da31bf1b3
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/254942
Reviewed-by: Samuel Rawlins <srawlins@google.com>
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
diff --git a/pkg/analyzer/lib/dart/element/element.dart b/pkg/analyzer/lib/dart/element/element.dart
index 3b074ce..444e18a 100644
--- a/pkg/analyzer/lib/dart/element/element.dart
+++ b/pkg/analyzer/lib/dart/element/element.dart
@@ -2261,6 +2261,11 @@
/// The positional fields (might be empty).
List<RecordPositionalFieldElement> get positionalFields;
+
+ /// Returns [RecordType] with [nullabilitySuffix] and declared field types.
+ RecordType instantiate({
+ required NullabilitySuffix nullabilitySuffix,
+ });
}
/// A field in a [RecordElement].
diff --git a/pkg/analyzer/lib/src/dart/element/display_string_builder.dart b/pkg/analyzer/lib/src/dart/element/display_string_builder.dart
index efb8bbe..6cd85f2 100644
--- a/pkg/analyzer/lib/src/dart/element/display_string_builder.dart
+++ b/pkg/analyzer/lib/src/dart/element/display_string_builder.dart
@@ -178,22 +178,21 @@
final fieldCount = positionalFields.length + namedFields.length;
_write('(');
- for (var i = 0; i < positionalFields.length; i++) {
- final field = positionalFields[i];
+ var index = 0;
+ for (final field in positionalFields) {
_writeType(field.type);
- if (i < fieldCount - 1) {
+ if (index++ < fieldCount - 1) {
_write(', ');
}
}
if (namedFields.isNotEmpty) {
_write('{');
- for (var i = 0; i < namedFields.length; i++) {
- final field = namedFields[i];
+ for (final field in namedFields) {
_writeType(field.type);
_write(' ');
_write(field.name);
- if (i < fieldCount - 1) {
+ if (index++ < fieldCount - 1) {
_write(', ');
}
}
diff --git a/pkg/analyzer/lib/src/dart/element/element.dart b/pkg/analyzer/lib/src/dart/element/element.dart
index a4f1282..cf547ee 100644
--- a/pkg/analyzer/lib/src/dart/element/element.dart
+++ b/pkg/analyzer/lib/src/dart/element/element.dart
@@ -6219,6 +6219,20 @@
throw UnimplementedError();
}
+ @override
+ RecordTypeImpl instantiate({
+ required NullabilitySuffix nullabilitySuffix,
+ }) {
+ return RecordTypeImpl(
+ element2: this,
+ fieldTypes: [
+ ...positionalFields.map((field) => field.type),
+ ...namedFieldsSorted.map((field) => field.type),
+ ],
+ nullabilitySuffix: nullabilitySuffix,
+ );
+ }
+
/// Returns [fields], if already sorted, or the sorted copy.
static List<RecordNamedFieldElementImpl> _sortNamedFields(
List<RecordNamedFieldElementImpl> fields,
@@ -6597,6 +6611,16 @@
typeArguments: typeArguments,
),
);
+ } else if (type is RecordTypeImpl) {
+ return RecordTypeImpl(
+ element2: type.element2,
+ fieldTypes: type.fieldTypes,
+ nullabilitySuffix: resultNullability,
+ alias: InstantiatedTypeAliasElementImpl(
+ element: this,
+ typeArguments: typeArguments,
+ ),
+ );
} else if (type is TypeParameterType) {
return TypeParameterTypeImpl(
element: type.element2,
diff --git a/pkg/analyzer/lib/src/dart/element/type.dart b/pkg/analyzer/lib/src/dart/element/type.dart
index 1b57924..6361b9a 100644
--- a/pkg/analyzer/lib/src/dart/element/type.dart
+++ b/pkg/analyzer/lib/src/dart/element/type.dart
@@ -16,6 +16,7 @@
import 'package:analyzer/src/dart/element/type_system.dart';
import 'package:analyzer/src/generated/element_type_provider.dart';
import 'package:analyzer/src/generated/utilities_dart.dart';
+import 'package:collection/collection.dart';
/// The [Type] representing the type `dynamic`.
class DynamicTypeImpl extends TypeImpl implements DynamicType {
@@ -995,16 +996,18 @@
@override
final RecordElementImpl element2;
- final Substitution substitution;
+ /// The types of all fields, first positional, then named.
+ final List<DartType> fieldTypes;
@override
final NullabilitySuffix nullabilitySuffix;
RecordTypeImpl({
required this.element2,
- required this.substitution,
+ required this.fieldTypes,
required this.nullabilitySuffix,
- }) : super(element2);
+ InstantiatedTypeAliasElement? alias,
+ }) : super(element2, alias: alias);
@override
RecordElementImpl get element => element2;
@@ -1015,23 +1018,22 @@
@override
List<RecordTypeNamedField> get namedFields {
- return element.namedFieldsSorted.map((field) {
- final type = substitution.substituteType(field.type);
+ final baseIndex = element.positionalFields.length;
+ return element.namedFieldsSorted.mapIndexed((index, field) {
return RecordTypeNamedFieldImpl(
element: field,
name: field.name,
- type: type,
+ type: fieldTypes[baseIndex + index],
);
}).toList();
}
@override
List<RecordTypePositionalField> get positionalFields {
- return element.positionalFields.map((field) {
- final type = substitution.substituteType(field.type);
+ return element.positionalFields.mapIndexed((index, field) {
return RecordTypePositionalFieldImpl(
element: field,
- type: type,
+ type: fieldTypes[index],
);
}).toList();
}
diff --git a/pkg/analyzer/lib/src/dart/element/type_algebra.dart b/pkg/analyzer/lib/src/dart/element/type_algebra.dart
index 8bf961f..fb61734 100644
--- a/pkg/analyzer/lib/src/dart/element/type_algebra.dart
+++ b/pkg/analyzer/lib/src/dart/element/type_algebra.dart
@@ -546,9 +546,20 @@
DartType visitNeverType(NeverType type) => type;
@override
- DartType visitRecordType(RecordType type) {
- // TODO: implement visitRecordType
- throw UnimplementedError();
+ DartType visitRecordType(covariant RecordTypeImpl type) {
+ final before = useCounter;
+ final fieldTypes = _mapList(type.fieldTypes);
+ final alias = _mapAlias(type.alias);
+ if (useCounter == before) {
+ return type;
+ }
+
+ return RecordTypeImpl(
+ element2: type.element2,
+ fieldTypes: fieldTypes,
+ nullabilitySuffix: type.nullabilitySuffix,
+ alias: alias,
+ );
}
@override
diff --git a/pkg/analyzer/test/generated/elements_types_mixin.dart b/pkg/analyzer/test/generated/elements_types_mixin.dart
index 855e246..a3594b3 100644
--- a/pkg/analyzer/test/generated/elements_types_mixin.dart
+++ b/pkg/analyzer/test/generated/elements_types_mixin.dart
@@ -10,7 +10,6 @@
import 'package:analyzer/src/dart/analysis/session.dart';
import 'package:analyzer/src/dart/element/element.dart';
import 'package:analyzer/src/dart/element/type.dart';
-import 'package:analyzer/src/dart/element/type_algebra.dart';
import 'package:analyzer/src/dart/element/type_system.dart';
import 'package:analyzer/src/dart/resolver/variance.dart';
import 'package:analyzer/src/generated/engine.dart';
@@ -592,10 +591,8 @@
RecordTypeImpl recordTypeNone({
required RecordElementImpl element,
}) {
- return RecordTypeImpl(
- element2: element,
+ return element.instantiate(
nullabilitySuffix: NullabilitySuffix.none,
- substitution: Substitution.empty,
);
}
diff --git a/pkg/analyzer/test/src/dart/element/type_algebra_test.dart b/pkg/analyzer/test/src/dart/element/type_algebra_test.dart
index 15ed44b..b1830f4 100644
--- a/pkg/analyzer/test/src/dart/element/type_algebra_test.dart
+++ b/pkg/analyzer/test/src/dart/element/type_algebra_test.dart
@@ -377,6 +377,100 @@
_assertIdenticalType(type, {T: intNone});
}
+ test_record_doesNotUseTypeParameter() async {
+ final T = typeParameter('T');
+
+ final type = recordTypeNone(
+ element: recordElement(
+ positionalFields: [
+ recordPositionalField(type: intNone),
+ ],
+ ),
+ );
+
+ assertType(type, '(int)');
+ _assertIdenticalType(type, {T: intNone});
+ }
+
+ test_record_fromAlias() async {
+ // typedef Alias<T> = (int, String);
+ final T = typeParameter('T');
+ final Alias = typeAlias(
+ name: 'Alias',
+ typeParameters: [T],
+ aliasedType: recordTypeNone(
+ element: recordElement(
+ positionalFields: [
+ recordPositionalField(type: intNone),
+ recordPositionalField(type: stringNone),
+ ],
+ ),
+ ),
+ );
+
+ final U = typeParameter('U');
+ final type = typeAliasTypeNone(Alias, typeArguments: [
+ typeParameterTypeNone(U),
+ ]);
+ assertType(type, '(int, String) via Alias<U>');
+ _assertSubstitution(type, {U: intNone}, '(int, String) via Alias<int>');
+ }
+
+ test_record_fromAlias2() async {
+ // typedef Alias<T> = (T, List<T>);
+ final T = typeParameter('T');
+ final T_none = typeParameterTypeNone(T);
+ final Alias = typeAlias(
+ name: 'Alias',
+ typeParameters: [T],
+ aliasedType: recordTypeNone(
+ element: recordElement(
+ positionalFields: [
+ recordPositionalField(type: T_none),
+ recordPositionalField(type: listNone(T_none)),
+ ],
+ ),
+ ),
+ );
+
+ final type = typeAliasTypeNone(Alias, typeArguments: [intNone]);
+ assertType(type, '(int, List<int>) via Alias<int>');
+ }
+
+ test_record_named() async {
+ final T = typeParameter('T');
+ final T_none = typeParameterTypeNone(T);
+
+ final type = recordTypeNone(
+ element: recordElement(
+ namedFields: [
+ recordNamedField(name: 'f1', type: T_none),
+ recordNamedField(name: 'f2', type: listNone(T_none)),
+ ],
+ ),
+ );
+
+ assertType(type, '({T f1, List<T> f2})');
+ _assertSubstitution(type, {T: intNone}, '({int f1, List<int> f2})');
+ }
+
+ test_record_positional() async {
+ final T = typeParameter('T');
+ final T_none = typeParameterTypeNone(T);
+
+ final type = recordTypeNone(
+ element: recordElement(
+ positionalFields: [
+ recordPositionalField(type: T_none),
+ recordPositionalField(type: listNone(T_none)),
+ ],
+ ),
+ );
+
+ assertType(type, '(T, List<T>)');
+ _assertSubstitution(type, {T: intNone}, '(int, List<int>)');
+ }
+
test_typeParameter_nullability() async {
var tElement = typeParameter('T');