| // Copyright (c) 2020, 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/element2.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/element/element.dart'; |
| import 'package:analyzer/src/dart/element/extensions.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_schema.dart'; |
| import 'package:analyzer/src/dart/element/type_visitor.dart'; |
| import 'package:analyzer/src/generated/utilities_dart.dart'; |
| import 'package:analyzer/src/summary2/function_type_builder.dart'; |
| import 'package:analyzer/src/summary2/named_type_builder.dart'; |
| import 'package:analyzer/src/summary2/record_type_builder.dart'; |
| import 'package:analyzer/src/utilities/extensions/element.dart'; |
| |
| /// Helper visitor that clones a type if a nested type is replaced, and |
| /// otherwise returns `null`. |
| class ReplacementVisitor |
| implements |
| TypeVisitor<TypeImpl?>, |
| InferenceTypeVisitor<TypeImpl?>, |
| LinkingTypeVisitor<TypeImpl?> { |
| const ReplacementVisitor(); |
| |
| void changeVariance() {} |
| |
| FunctionTypeImpl? createFunctionType({ |
| required FunctionTypeImpl type, |
| required InstantiatedTypeAliasElementImpl? newAlias, |
| required List<TypeParameterElementImpl2>? newTypeParameters, |
| required List<FormalParameterElementMixin>? newParameters, |
| required TypeImpl? newReturnType, |
| required NullabilitySuffix? newNullability, |
| }) { |
| if (newAlias == null && |
| newNullability == null && |
| newReturnType == null && |
| newParameters == null) { |
| return null; |
| } |
| |
| return FunctionTypeImpl.v2( |
| typeParameters: newTypeParameters ?? type.typeParameters, |
| formalParameters: newParameters ?? type.formalParameters, |
| returnType: newReturnType ?? type.returnType, |
| nullabilitySuffix: newNullability ?? type.nullabilitySuffix, |
| alias: newAlias ?? type.alias, |
| ); |
| } |
| |
| FunctionTypeBuilder? createFunctionTypeBuilder({ |
| required FunctionTypeBuilder type, |
| required List<TypeParameterElementImpl2>? newTypeParameters, |
| required List<FormalParameterElementImpl>? newFormalParameters, |
| required TypeImpl? newReturnType, |
| required NullabilitySuffix? newNullability, |
| }) { |
| if (newNullability == null && |
| newReturnType == null && |
| newFormalParameters == null) { |
| return null; |
| } |
| |
| return FunctionTypeBuilder.v2( |
| typeParameters: newTypeParameters ?? type.typeParameters, |
| formalParameters: newFormalParameters ?? type.formalParameters, |
| returnType: newReturnType ?? type.returnType, |
| nullabilitySuffix: newNullability ?? type.nullabilitySuffix, |
| ); |
| } |
| |
| InterfaceTypeImpl? createInterfaceType({ |
| required InterfaceTypeImpl type, |
| required InstantiatedTypeAliasElementImpl? newAlias, |
| required List<TypeImpl>? newTypeArguments, |
| required NullabilitySuffix? newNullability, |
| }) { |
| if (newAlias == null && |
| newTypeArguments == null && |
| newNullability == null) { |
| return null; |
| } |
| |
| return InterfaceTypeImpl( |
| element: type.element3, |
| typeArguments: newTypeArguments ?? type.typeArguments, |
| nullabilitySuffix: newNullability ?? type.nullabilitySuffix, |
| alias: newAlias ?? type.alias, |
| ); |
| } |
| |
| NamedTypeBuilder? createNamedTypeBuilder({ |
| required NamedTypeBuilder type, |
| required List<TypeImpl>? newTypeArguments, |
| required NullabilitySuffix? newNullability, |
| }) { |
| if (newTypeArguments == null && newNullability == null) { |
| return null; |
| } |
| |
| return NamedTypeBuilder.v2( |
| linker: type.linker, |
| typeSystem: type.typeSystem, |
| element: type.element3, |
| arguments: newTypeArguments ?? type.arguments, |
| nullabilitySuffix: newNullability ?? type.nullabilitySuffix, |
| ); |
| } |
| |
| NeverTypeImpl? createNeverType({ |
| required NeverTypeImpl type, |
| required NullabilitySuffix? newNullability, |
| }) { |
| if (newNullability == null) { |
| return null; |
| } |
| |
| return type.withNullability(newNullability); |
| } |
| |
| TypeParameterTypeImpl? createPromotedTypeParameterType({ |
| required TypeParameterType type, |
| required NullabilitySuffix? newNullability, |
| required DartType? newPromotedBound, |
| }) { |
| if (newNullability == null && newPromotedBound == null) { |
| return null; |
| } |
| |
| var promotedBound = (type as TypeParameterTypeImpl).promotedBound; |
| return TypeParameterTypeImpl( |
| element3: type.element3, |
| nullabilitySuffix: newNullability ?? type.nullabilitySuffix, |
| promotedBound: newPromotedBound ?? promotedBound, |
| alias: type.alias, |
| ); |
| } |
| |
| TypeParameterTypeImpl? createTypeParameterType({ |
| required TypeParameterTypeImpl type, |
| required NullabilitySuffix? newNullability, |
| }) { |
| if (newNullability == null) { |
| return null; |
| } |
| |
| return TypeParameterTypeImpl( |
| element3: type.element3, |
| nullabilitySuffix: newNullability, |
| alias: type.alias, |
| ); |
| } |
| |
| @override |
| TypeImpl? visitDynamicType(DynamicType type) { |
| return null; |
| } |
| |
| @override |
| TypeImpl? visitFunctionType(FunctionType node) { |
| // TODO(scheglov): avoid this cast |
| node as FunctionTypeImpl; |
| var newNullability = visitNullability(node); |
| |
| List<TypeParameterElementImpl2>? newTypeParameters; |
| for (var i = 0; i < node.typeParameters.length; i++) { |
| var typeParameter = node.typeParameters[i]; |
| var bound = typeParameter.bound; |
| if (bound != null) { |
| var newBound = visitTypeParameterBound(bound); |
| if (newBound != null) { |
| newTypeParameters ??= node.typeParameters.toList(growable: false); |
| newTypeParameters[i] = typeParameter.freshCopy() |
| ..bound = |
| // TODO(paulberry): eliminate this cast by changing the return |
| // type of `visitTypeParameterBound`. |
| newBound as TypeImpl; |
| } |
| } |
| } |
| |
| Substitution? substitution; |
| if (newTypeParameters != null) { |
| var map = <TypeParameterElement2, DartType>{}; |
| for (var i = 0; i < newTypeParameters.length; ++i) { |
| var typeParameter = node.typeParameters[i]; |
| var newTypeParameter = newTypeParameters[i]; |
| map[typeParameter] = newTypeParameter.instantiate( |
| nullabilitySuffix: NullabilitySuffix.none, |
| ); |
| } |
| |
| substitution = Substitution.fromMap2(map); |
| |
| for (var i = 0; i < newTypeParameters.length; i++) { |
| var newTypeParameter = newTypeParameters[i]; |
| var bound = newTypeParameter.bound; |
| if (bound != null) { |
| var newBound = substitution.substituteType(bound); |
| newTypeParameter.bound = newBound; |
| } |
| } |
| } |
| |
| TypeImpl? visitType(TypeImpl? type) { |
| if (type == null) return null; |
| var result = type.accept(this); |
| if (substitution != null) { |
| result = substitution.substituteType(result ?? type); |
| } |
| return result; |
| } |
| |
| var newReturnType = visitType(node.returnType); |
| |
| InstantiatedTypeAliasElementImpl? newAlias; |
| var alias = node.alias; |
| if (alias != null) { |
| List<TypeImpl>? newArguments; |
| var aliasArguments = alias.typeArguments; |
| for (var i = 0; i < aliasArguments.length; i++) { |
| var substitution = aliasArguments[i].accept(this); |
| if (substitution != null) { |
| newArguments ??= aliasArguments.toList(growable: false); |
| newArguments[i] = substitution; |
| } |
| } |
| if (newArguments != null) { |
| newAlias = InstantiatedTypeAliasElementImpl.v2( |
| element: alias.element2, |
| typeArguments: newArguments, |
| ); |
| } |
| } |
| |
| changeVariance(); |
| |
| List<FormalParameterElementMixin>? newParameters; |
| for (var i = 0; i < node.formalParameters.length; i++) { |
| var parameter = node.formalParameters[i]; |
| |
| var type = parameter.type; |
| var newType = visitType(type); |
| |
| var kind = parameter.parameterKind; |
| var newKind = visitParameterKind(kind); |
| |
| if (newType != null || newKind != null) { |
| newParameters ??= node.formalParameters.toList(growable: false); |
| newParameters[i] = parameter.copyWith( |
| type: newType, |
| kind: newKind, |
| ); |
| } |
| } |
| |
| changeVariance(); |
| |
| return createFunctionType( |
| type: node, |
| newAlias: newAlias, |
| newTypeParameters: newTypeParameters, |
| newParameters: newParameters, |
| newReturnType: newReturnType, |
| newNullability: newNullability, |
| ); |
| } |
| |
| @override |
| TypeImpl? visitFunctionTypeBuilder(FunctionTypeBuilder node) { |
| var newNullability = visitNullability(node); |
| |
| List<TypeParameterElementImpl2>? newTypeParameters; |
| for (var i = 0; i < node.typeParameters.length; i++) { |
| var typeParameter = node.typeParameters[i]; |
| var bound = typeParameter.bound; |
| if (bound != null) { |
| var newBound = visitTypeParameterBound(bound); |
| if (newBound != null) { |
| newTypeParameters ??= node.typeParameters.toList(growable: false); |
| newTypeParameters[i] = typeParameter.freshCopy() |
| ..bound = |
| // TODO(paulberry): eliminate this cast by changing the return |
| // type of `visitTypeParameterBound`. |
| newBound as TypeImpl; |
| } |
| } |
| } |
| |
| Substitution? substitution; |
| if (newTypeParameters != null) { |
| var map = <TypeParameterElement2, DartType>{}; |
| for (var i = 0; i < newTypeParameters.length; ++i) { |
| var typeParameter = node.typeParameters[i]; |
| var newTypeParameter = newTypeParameters[i]; |
| map[typeParameter] = newTypeParameter.instantiate( |
| nullabilitySuffix: NullabilitySuffix.none, |
| ); |
| } |
| |
| substitution = Substitution.fromMap2(map); |
| |
| for (var i = 0; i < newTypeParameters.length; i++) { |
| var newTypeParameter = newTypeParameters[i]; |
| var bound = newTypeParameter.bound; |
| if (bound != null) { |
| var newBound = substitution.substituteType(bound); |
| newTypeParameter.bound = newBound; |
| } |
| } |
| } |
| |
| TypeImpl? visitType(DartType? type) { |
| if (type == null) return null; |
| var result = type.accept(this); |
| if (substitution != null) { |
| result = substitution.substituteType(result ?? type); |
| } |
| return result; |
| } |
| |
| var newReturnType = visitType(node.returnType); |
| |
| changeVariance(); |
| |
| List<FormalParameterElementImpl>? newFormalParameters; |
| for (var i = 0; i < node.formalParameters.length; i++) { |
| var parameter = node.formalParameters[i]; |
| |
| var type = parameter.type; |
| var newType = visitType(type); |
| |
| var kind = parameter.parameterKind; |
| var newKind = visitParameterKind(kind); |
| |
| if (newType != null || newKind != null) { |
| newFormalParameters ??= node.formalParameters.toList(growable: false); |
| newFormalParameters[i] = parameter.copyWith( |
| type: newType, |
| kind: newKind, |
| ); |
| } |
| } |
| |
| changeVariance(); |
| |
| return createFunctionTypeBuilder( |
| type: node, |
| newTypeParameters: newTypeParameters, |
| newFormalParameters: newFormalParameters, |
| newReturnType: newReturnType, |
| newNullability: newNullability, |
| ); |
| } |
| |
| @override |
| TypeImpl? visitInterfaceType(covariant InterfaceTypeImpl type) { |
| var newNullability = visitNullability(type); |
| |
| InstantiatedTypeAliasElementImpl? newAlias; |
| var alias = type.alias; |
| if (alias != null) { |
| var newArguments = _typeArguments( |
| alias.element2.typeParameters2, |
| alias.typeArguments, |
| ); |
| if (newArguments != null) { |
| newAlias = InstantiatedTypeAliasElementImpl.v2( |
| element: alias.element2, |
| typeArguments: newArguments, |
| ); |
| } |
| } |
| |
| var newTypeArguments = _typeArguments( |
| type.element3.typeParameters2, |
| type.typeArguments, |
| ); |
| |
| return createInterfaceType( |
| type: type, |
| newAlias: newAlias, |
| newTypeArguments: newTypeArguments, |
| newNullability: newNullability, |
| ); |
| } |
| |
| @override |
| TypeImpl? visitInvalidType(InvalidType type) { |
| return null; |
| } |
| |
| @override |
| TypeImpl? visitNamedTypeBuilder(NamedTypeBuilder type) { |
| var newNullability = visitNullability(type); |
| |
| var parameters = const <TypeParameterElementImpl2>[]; |
| var element = type.element3; |
| if (element is InterfaceElementImpl2) { |
| parameters = element.typeParameters2; |
| } else if (element is TypeAliasElementImpl2) { |
| parameters = element.typeParameters2; |
| } |
| |
| var newArguments = _typeArguments(parameters, type.arguments); |
| return createNamedTypeBuilder( |
| type: type, |
| newTypeArguments: newArguments, |
| newNullability: newNullability, |
| ); |
| } |
| |
| @override |
| TypeImpl? visitNeverType(covariant NeverTypeImpl type) { |
| var newNullability = visitNullability(type); |
| |
| return createNeverType( |
| type: type, |
| newNullability: newNullability, |
| ); |
| } |
| |
| NullabilitySuffix? visitNullability(DartType type) { |
| return null; |
| } |
| |
| ParameterKind? visitParameterKind(ParameterKind kind) { |
| return null; |
| } |
| |
| @override |
| TypeImpl? visitRecordType(covariant RecordTypeImpl type) { |
| var newNullability = visitNullability(type); |
| |
| InstantiatedTypeAliasElementImpl? newAlias; |
| var alias = type.alias; |
| if (alias != null) { |
| var newArguments = _typeArguments( |
| alias.element2.typeParameters2, |
| alias.typeArguments, |
| ); |
| if (newArguments != null) { |
| newAlias = InstantiatedTypeAliasElementImpl.v2( |
| element: alias.element2, |
| typeArguments: newArguments, |
| ); |
| } |
| } |
| |
| List<RecordTypePositionalFieldImpl>? newPositionalFields; |
| var positionalFields = type.positionalFields; |
| for (var i = 0; i < positionalFields.length; i++) { |
| var field = positionalFields[i]; |
| var newType = field.type.accept(this); |
| if (newType != null) { |
| newPositionalFields ??= positionalFields.toList(growable: false); |
| newPositionalFields[i] = RecordTypePositionalFieldImpl( |
| type: newType, |
| ); |
| } |
| } |
| |
| List<RecordTypeNamedFieldImpl>? newNamedFields; |
| var namedFields = type.namedFields; |
| for (var i = 0; i < namedFields.length; i++) { |
| var field = namedFields[i]; |
| var newType = field.type.accept(this); |
| if (newType != null) { |
| newNamedFields ??= namedFields.toList(growable: false); |
| newNamedFields[i] = RecordTypeNamedFieldImpl( |
| name: field.name, |
| type: newType, |
| ); |
| } |
| } |
| |
| if (newAlias == null && |
| newPositionalFields == null && |
| newNamedFields == null && |
| newNullability == null) { |
| return null; |
| } |
| |
| return RecordTypeImpl( |
| positionalFields: newPositionalFields ?? type.positionalFields, |
| namedFields: newNamedFields ?? type.namedFields, |
| nullabilitySuffix: newNullability ?? type.nullabilitySuffix, |
| alias: newAlias ?? type.alias, |
| ); |
| } |
| |
| @override |
| TypeImpl? visitRecordTypeBuilder(RecordTypeBuilder type) { |
| List<DartType>? newFieldTypes; |
| var fieldTypes = type.fieldTypes; |
| for (var i = 0; i < fieldTypes.length; i++) { |
| var fieldType = fieldTypes[i]; |
| var newFieldType = fieldType.accept(this); |
| if (newFieldType != null) { |
| newFieldTypes ??= fieldTypes.toList(growable: false); |
| newFieldTypes[i] = newFieldType; |
| } |
| } |
| |
| var newNullability = visitNullability(type); |
| |
| if (newFieldTypes == null && newNullability == null) { |
| return null; |
| } |
| |
| return RecordTypeBuilder( |
| typeSystem: type.typeSystem, |
| node: type.node, |
| fieldTypes: newFieldTypes ?? type.fieldTypes, |
| nullabilitySuffix: newNullability ?? type.nullabilitySuffix, |
| ); |
| } |
| |
| TypeImpl? visitTypeArgument( |
| TypeParameterElementImpl2 parameter, |
| TypeImpl argument, |
| ) { |
| return argument.accept(this); |
| } |
| |
| DartType? visitTypeParameterBound(DartType type) { |
| return type.accept(this); |
| } |
| |
| @override |
| TypeImpl? visitTypeParameterType(TypeParameterType type) { |
| // TODO(scheglov): avoid this cast |
| type as TypeParameterTypeImpl; |
| var newNullability = visitNullability(type); |
| |
| var promotedBound = type.promotedBound; |
| if (promotedBound != null) { |
| var newPromotedBound = promotedBound.accept(this); |
| return createPromotedTypeParameterType( |
| type: type, |
| newNullability: newNullability, |
| newPromotedBound: newPromotedBound, |
| ); |
| } |
| |
| return createTypeParameterType( |
| type: type, |
| newNullability: newNullability, |
| ); |
| } |
| |
| @override |
| TypeImpl? visitUnknownInferredType(UnknownInferredType type) { |
| return null; |
| } |
| |
| @override |
| TypeImpl? visitVoidType(VoidType type) { |
| return null; |
| } |
| |
| List<TypeImpl>? _typeArguments( |
| List<TypeParameterElementImpl2> parameters, |
| List<TypeImpl> arguments, |
| ) { |
| if (arguments.length != parameters.length) { |
| return null; |
| } |
| |
| List<TypeImpl>? newArguments; |
| for (var i = 0; i < arguments.length; i++) { |
| var substitution = visitTypeArgument(parameters[i], arguments[i]); |
| if (substitution != null) { |
| newArguments ??= arguments.toList(growable: false); |
| newArguments[i] = substitution; |
| } |
| } |
| |
| return newArguments; |
| } |
| } |