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');