Update ReplacementVisitor to support RecordType.
Change-Id: I4ada89fc87709dd15f2add86005d6c8927d7b773
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/255684
Reviewed-by: Samuel Rawlins <srawlins@google.com>
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
diff --git a/pkg/analyzer/lib/src/dart/element/replacement_visitor.dart b/pkg/analyzer/lib/src/dart/element/replacement_visitor.dart
index e02c15f..d58ef88 100644
--- a/pkg/analyzer/lib/src/dart/element/replacement_visitor.dart
+++ b/pkg/analyzer/lib/src/dart/element/replacement_visitor.dart
@@ -139,6 +139,24 @@
);
}
+ RecordTypeImpl? createRecordType({
+ required RecordTypeImpl type,
+ required InstantiatedTypeAliasElement? newAlias,
+ required List<DartType>? newFieldTypes,
+ required NullabilitySuffix? newNullability,
+ }) {
+ if (newAlias == null && newFieldTypes == null && newNullability == null) {
+ return null;
+ }
+
+ return RecordTypeImpl(
+ element2: type.element2,
+ fieldTypes: newFieldTypes ?? type.fieldTypes,
+ nullabilitySuffix: newNullability ?? type.nullabilitySuffix,
+ alias: newAlias ?? type.alias,
+ );
+ }
+
DartType? createTypeParameterType({
required TypeParameterType type,
required NullabilitySuffix? newNullability,
@@ -423,9 +441,32 @@
}
@override
- DartType? visitRecordType(RecordType type) {
- // TODO: implement visitRecordType
- throw UnimplementedError();
+ DartType? visitRecordType(covariant RecordTypeImpl type) {
+ var newNullability = visitNullability(type);
+
+ InstantiatedTypeAliasElement? newAlias;
+ var alias = type.alias;
+ if (alias != null) {
+ var newArguments = _typeArguments(
+ alias.element.typeParameters,
+ alias.typeArguments,
+ );
+ if (newArguments != null) {
+ newAlias = InstantiatedTypeAliasElementImpl(
+ element: alias.element,
+ typeArguments: newArguments,
+ );
+ }
+ }
+
+ final newFieldTypes = _typeList(type.fieldTypes);
+
+ return createRecordType(
+ type: type,
+ newAlias: newAlias,
+ newFieldTypes: newFieldTypes,
+ newNullability: newNullability,
+ );
}
DartType? visitTypeArgument(
@@ -488,4 +529,17 @@
return newArguments;
}
+
+ List<DartType>? _typeList(List<DartType> arguments) {
+ List<DartType>? newArguments;
+ for (var i = 0; i < arguments.length; i++) {
+ var substitution = arguments[i].accept(this);
+ if (substitution != null) {
+ newArguments ??= arguments.toList(growable: false);
+ newArguments[i] = substitution;
+ }
+ }
+
+ return newArguments;
+ }
}
diff --git a/pkg/analyzer/test/generated/elements_types_mixin.dart b/pkg/analyzer/test/generated/elements_types_mixin.dart
index 592a74d..9c1da07 100644
--- a/pkg/analyzer/test/generated/elements_types_mixin.dart
+++ b/pkg/analyzer/test/generated/elements_types_mixin.dart
@@ -560,12 +560,18 @@
}
RecordElementImpl recordElement({
- List<RecordPositionalFieldElementImpl> positionalFields = const [],
- List<RecordNamedFieldElementImpl> namedFields = const [],
+ List<DartType> positionalTypes = const [],
+ Map<String, DartType> namedTypes = const {},
}) {
return RecordElementImpl(
- positionalFields: positionalFields,
- namedFields: namedFields,
+ positionalFields: positionalTypes.map(
+ (fieldType) {
+ return recordPositionalField(type: fieldType);
+ },
+ ).toList(),
+ namedFields: namedTypes.entries.map((entry) {
+ return recordNamedField(name: entry.key, type: entry.value);
+ }).toList(),
);
}
@@ -590,14 +596,52 @@
);
}
- RecordTypeImpl recordTypeNone({
- required RecordElementImpl element,
+ RecordTypeImpl recordType({
+ List<DartType> positionalTypes = const [],
+ Map<String, DartType> namedTypes = const {},
+ required NullabilitySuffix nullabilitySuffix,
}) {
- return element.instantiate(
+ return recordElement(
+ positionalTypes: positionalTypes,
+ namedTypes: namedTypes,
+ ).instantiate(
+ nullabilitySuffix: nullabilitySuffix,
+ );
+ }
+
+ RecordTypeImpl recordTypeNone({
+ List<DartType> positionalTypes = const [],
+ Map<String, DartType> namedTypes = const {},
+ }) {
+ return recordType(
+ positionalTypes: positionalTypes,
+ namedTypes: namedTypes,
nullabilitySuffix: NullabilitySuffix.none,
);
}
+ RecordTypeImpl recordTypeQuestion({
+ List<DartType> positionalTypes = const [],
+ Map<String, DartType> namedTypes = const {},
+ }) {
+ return recordType(
+ positionalTypes: positionalTypes,
+ namedTypes: namedTypes,
+ nullabilitySuffix: NullabilitySuffix.question,
+ );
+ }
+
+ RecordTypeImpl recordTypeStar({
+ List<DartType> positionalTypes = const [],
+ Map<String, DartType> namedTypes = const {},
+ }) {
+ return recordType(
+ positionalTypes: positionalTypes,
+ namedTypes: namedTypes,
+ nullabilitySuffix: NullabilitySuffix.star,
+ );
+ }
+
ParameterElement requiredParameter({
String? name,
required DartType type,
diff --git a/pkg/analyzer/test/src/dart/element/normalize_type_test.dart b/pkg/analyzer/test/src/dart/element/normalize_type_test.dart
index aa6ea5d..da0f5a2 100644
--- a/pkg/analyzer/test/src/dart/element/normalize_type_test.dart
+++ b/pkg/analyzer/test/src/dart/element/normalize_type_test.dart
@@ -341,52 +341,40 @@
test_recordType() {
_check(
recordTypeNone(
- element: recordElement(
- positionalFields: [
- recordPositionalField(type: intNone),
- ],
- ),
+ positionalTypes: [
+ intNone,
+ ],
),
recordTypeNone(
- element: recordElement(
- positionalFields: [
- recordPositionalField(type: intNone),
- ],
- ),
+ positionalTypes: [
+ intNone,
+ ],
),
);
_check(
recordTypeNone(
- element: recordElement(
- positionalFields: [
- recordPositionalField(type: futureOrNone(objectNone)),
- ],
- ),
+ positionalTypes: [
+ futureOrNone(objectNone),
+ ],
),
recordTypeNone(
- element: recordElement(
- positionalFields: [
- recordPositionalField(type: objectNone),
- ],
- ),
+ positionalTypes: [
+ objectNone,
+ ],
),
);
_check(
recordTypeNone(
- element: recordElement(
- namedFields: [
- recordNamedField(name: 'foo', type: futureOrNone(objectNone)),
- ],
- ),
+ namedTypes: {
+ 'foo': futureOrNone(objectNone),
+ },
),
recordTypeNone(
- element: recordElement(
- namedFields: [
- recordNamedField(name: 'foo', type: objectNone),
- ],
- ),
+ namedTypes: {
+ 'foo': objectNone,
+ },
),
);
}
diff --git a/pkg/analyzer/test/src/dart/element/nullability_eliminator_test.dart b/pkg/analyzer/test/src/dart/element/nullability_eliminator_test.dart
index 05c1d38..aef063f 100644
--- a/pkg/analyzer/test/src/dart/element/nullability_eliminator_test.dart
+++ b/pkg/analyzer/test/src/dart/element/nullability_eliminator_test.dart
@@ -10,6 +10,7 @@
import 'package:test_reflective_loader/test_reflective_loader.dart';
import '../../../generated/type_system_base.dart';
+import 'string_types.dart';
main() {
defineReflectiveSuite(() {
@@ -18,7 +19,14 @@
}
@reflectiveTest
-class NullabilityEliminatorTest extends AbstractTypeSystemTest {
+class NullabilityEliminatorTest extends AbstractTypeSystemTest
+ with StringTypes {
+ @override
+ void setUp() {
+ super.setUp();
+ defineStringTypes();
+ }
+
test_dynamicType() {
_verifySame(typeProvider.dynamicType);
}
@@ -312,6 +320,61 @@
_verify(listStar(neverNone), listStar(nullStar));
}
+ test_recordType_fromAlias() {
+ var T = typeParameter('T');
+ var A = typeAlias(
+ name: 'A',
+ typeParameters: [T],
+ aliasedType: recordTypeNone(
+ positionalTypes: [
+ typeParameterTypeNone(T),
+ ],
+ ),
+ );
+
+ var input = A.instantiate(
+ typeArguments: [intNone],
+ nullabilitySuffix: NullabilitySuffix.none,
+ );
+ expect(_typeToString(input), '(int)');
+
+ var result = NullabilityEliminator.perform(typeProvider, input);
+ expect(_typeToString(result), '(int*)*');
+ _assertInstantiatedAlias(result, A, 'int*');
+ }
+
+ test_recordType_named() {
+ final expected = '({int* f1})*';
+
+ _verify2('({int f1})', expected);
+ _verify2('({int? f1})', expected);
+ _verify2('({int* f1})', expected);
+
+ _verify2('({int f1})?', expected);
+ _verify2('({int? f1})?', expected);
+ _verify2('({int* f1})?', expected);
+
+ _verify2('({int f1})*', expected);
+ _verify2('({int? f1})*', expected);
+ _verifySame(typeOfString(expected));
+ }
+
+ test_recordType_positional() {
+ final expected = '(int*)*';
+
+ _verify2('(int)', expected);
+ _verify2('(int?)', expected);
+ _verify2('(int*)', expected);
+
+ _verify2('(int)?', expected);
+ _verify2('(int?)?', expected);
+ _verify2('(int*)?', expected);
+
+ _verify2('(int)*', expected);
+ _verify2('(int?)*', expected);
+ _verifySame(typeOfString(expected));
+ }
+
test_typeParameterType() {
var T = typeParameter('T');
_verify(
@@ -348,6 +411,10 @@
expect(result, expected);
}
+ void _verify2(String input, String expected) {
+ _verify(typeOfString(input), typeOfString(expected));
+ }
+
void _verifySame(DartType input) {
var result = NullabilityEliminator.perform(typeProvider, input);
expect(result, same(input));
diff --git a/pkg/analyzer/test/src/dart/element/string_types.dart b/pkg/analyzer/test/src/dart/element/string_types.dart
index 2081d30..883cfe5 100644
--- a/pkg/analyzer/test/src/dart/element/string_types.dart
+++ b/pkg/analyzer/test/src/dart/element/string_types.dart
@@ -409,18 +409,9 @@
Map<String, DartType> namedTypes,
) {
final type = recordTypeNone(
- element: recordElement(
- positionalFields: positionalTypes.map(
- (fieldType) {
- return recordPositionalField(type: fieldType);
- },
- ).toList(),
- namedFields: namedTypes.entries.map((entry) {
- return recordNamedField(name: entry.key, type: entry.value);
- }).toList(),
- ),
+ positionalTypes: positionalTypes,
+ namedTypes: namedTypes,
);
- expect(type.toString(), str);
_defineType(str, type);
}
@@ -428,12 +419,34 @@
mixed(str, types, const {});
}
+ void allPositionalQuestion(String str, List<DartType> types) {
+ final type = recordTypeQuestion(
+ positionalTypes: types,
+ );
+ _defineType(str, type);
+ }
+
+ void allPositionalStar(String str, List<DartType> types) {
+ final type = recordTypeStar(
+ positionalTypes: types,
+ );
+ _defineType(str, type);
+ }
+
allPositional('(double)', [doubleNone]);
allPositional('(int)', [intNone]);
allPositional('(int?)', [intQuestion]);
allPositional('(int*)', [intStar]);
allPositional('(num)', [numNone]);
+ allPositionalQuestion('(int)?', [intNone]);
+ allPositionalQuestion('(int?)?', [intQuestion]);
+ allPositionalQuestion('(int*)?', [intStar]);
+
+ allPositionalStar('(int)*', [intNone]);
+ allPositionalStar('(int?)*', [intQuestion]);
+ allPositionalStar('(int*)*', [intStar]);
+
allPositional('(double, int)', [doubleNone, intNone]);
allPositional('(int, double)', [intNone, doubleNone]);
allPositional('(int, int)', [intNone, intNone]);
@@ -447,6 +460,20 @@
mixed(str, const [], types);
}
+ void allNamedQuestion(String str, Map<String, DartType> types) {
+ final type = recordTypeQuestion(
+ namedTypes: types,
+ );
+ _defineType(str, type);
+ }
+
+ void allNamedStar(String str, Map<String, DartType> types) {
+ final type = recordTypeStar(
+ namedTypes: types,
+ );
+ _defineType(str, type);
+ }
+
allNamed('({double f1})', {'f1': doubleNone});
allNamed('({int f1})', {'f1': intNone});
allNamed('({int? f1})', {'f1': intQuestion});
@@ -454,6 +481,14 @@
allNamed('({num f1})', {'f1': numNone});
allNamed('({int f2})', {'f2': intNone});
+ allNamedQuestion('({int f1})?', {'f1': intNone});
+ allNamedQuestion('({int? f1})?', {'f1': intQuestion});
+ allNamedQuestion('({int* f1})?', {'f1': intStar});
+
+ allNamedStar('({int f1})*', {'f1': intNone});
+ allNamedStar('({int? f1})*', {'f1': intQuestion});
+ allNamedStar('({int* f1})*', {'f1': intStar});
+
allNamed('({double f1, int f2})', {'f1': doubleNone, 'f2': intNone});
allNamed('({int f1, double f2})', {'f1': intNone, 'f2': doubleNone});
allNamed('({int f1, int f2})', {'f1': intNone, 'f2': intNone});
diff --git a/pkg/analyzer/test/src/dart/element/subtype_test.dart b/pkg/analyzer/test/src/dart/element/subtype_test.dart
index ee31ff1e..46cdf59 100644
--- a/pkg/analyzer/test/src/dart/element/subtype_test.dart
+++ b/pkg/analyzer/test/src/dart/element/subtype_test.dart
@@ -4166,24 +4166,20 @@
check(
recordTypeNone(
- element: recordElement(
- namedFields: [
- recordNamedField(name: 'f1', type: intNone),
- recordNamedField(name: 'f2', type: intNone),
- recordNamedField(name: 'f3', type: intNone),
- recordNamedField(name: 'f4', type: intNone),
- ],
- ),
+ namedTypes: {
+ 'f1': intNone,
+ 'f2': intNone,
+ 'f3': intNone,
+ 'f4': intNone,
+ },
),
recordTypeNone(
- element: recordElement(
- namedFields: [
- recordNamedField(name: 'f4', type: intNone),
- recordNamedField(name: 'f3', type: intNone),
- recordNamedField(name: 'f2', type: intNone),
- recordNamedField(name: 'f1', type: intNone),
- ],
- ),
+ namedTypes: {
+ 'f4': intNone,
+ 'f3': intNone,
+ 'f2': intNone,
+ 'f1': intNone,
+ },
),
);
}
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 e355e61..36e0c11 100644
--- a/pkg/analyzer/test/src/dart/element/type_algebra_test.dart
+++ b/pkg/analyzer/test/src/dart/element/type_algebra_test.dart
@@ -381,11 +381,7 @@
final T = typeParameter('T');
final type = recordTypeNone(
- element: recordElement(
- positionalFields: [
- recordPositionalField(type: intNone),
- ],
- ),
+ positionalTypes: [intNone],
);
assertType(type, '(int)');
@@ -399,12 +395,7 @@
name: 'Alias',
typeParameters: [T],
aliasedType: recordTypeNone(
- element: recordElement(
- positionalFields: [
- recordPositionalField(type: intNone),
- recordPositionalField(type: stringNone),
- ],
- ),
+ positionalTypes: [intNone, stringNone],
),
);
@@ -424,12 +415,10 @@
name: 'Alias',
typeParameters: [T],
aliasedType: recordTypeNone(
- element: recordElement(
- positionalFields: [
- recordPositionalField(type: T_none),
- recordPositionalField(type: listNone(T_none)),
- ],
- ),
+ positionalTypes: [
+ T_none,
+ listNone(T_none),
+ ],
),
);
@@ -442,12 +431,10 @@
final T_none = typeParameterTypeNone(T);
final type = recordTypeNone(
- element: recordElement(
- namedFields: [
- recordNamedField(name: 'f1', type: T_none),
- recordNamedField(name: 'f2', type: listNone(T_none)),
- ],
- ),
+ namedTypes: {
+ 'f1': T_none,
+ 'f2': listNone(T_none),
+ },
);
assertType(type, '({T f1, List<T> f2})');
@@ -459,12 +446,10 @@
final T_none = typeParameterTypeNone(T);
final type = recordTypeNone(
- element: recordElement(
- positionalFields: [
- recordPositionalField(type: T_none),
- recordPositionalField(type: listNone(T_none)),
- ],
- ),
+ positionalTypes: [
+ T_none,
+ listNone(T_none),
+ ],
);
assertType(type, '(T, List<T>)');