| // Copyright (c) 2017, 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.md file. |
| |
| import '../ast.dart' hide MapEntry; |
| import '../type_algebra.dart'; |
| |
| /// Helper visitor that clones a type if a nested type is replaced, and |
| /// otherwise returns `null`. |
| class ReplacementVisitor implements DartTypeVisitor<DartType> { |
| const ReplacementVisitor(); |
| |
| void changeVariance() {} |
| |
| Nullability visitNullability(DartType node) => null; |
| |
| @override |
| DartType visitFunctionType(FunctionType node) { |
| Nullability newNullability = visitNullability(node); |
| |
| List<TypeParameter> newTypeParameters; |
| for (int i = 0; i < node.typeParameters.length; i++) { |
| TypeParameter typeParameter = node.typeParameters[i]; |
| DartType newBound = typeParameter.bound.accept(this); |
| DartType newDefaultType = typeParameter.defaultType?.accept(this); |
| if (newBound != null || newDefaultType != null) { |
| newTypeParameters ??= node.typeParameters.toList(growable: false); |
| newTypeParameters[i] = new TypeParameter( |
| typeParameter.name, |
| newBound ?? typeParameter.bound, |
| newDefaultType ?? typeParameter.defaultType); |
| } |
| } |
| |
| Substitution substitution; |
| if (newTypeParameters != null) { |
| List<TypeParameterType> typeParameterTypes = |
| new List<TypeParameterType>(newTypeParameters.length); |
| for (int i = 0; i < newTypeParameters.length; i++) { |
| typeParameterTypes[i] = new TypeParameterType.forAlphaRenaming( |
| node.typeParameters[i], newTypeParameters[i]); |
| } |
| substitution = |
| Substitution.fromPairs(node.typeParameters, typeParameterTypes); |
| for (int i = 0; i < newTypeParameters.length; i++) { |
| newTypeParameters[i].bound = |
| substitution.substituteType(newTypeParameters[i].bound); |
| } |
| } |
| |
| DartType visitType(DartType type) { |
| if (type == null) return null; |
| DartType result = type.accept(this); |
| if (substitution != null) { |
| result = substitution.substituteType(result ?? type); |
| } |
| return result; |
| } |
| |
| DartType newReturnType = visitType(node.returnType); |
| changeVariance(); |
| List<DartType> newPositionalParameters = null; |
| for (int i = 0; i < node.positionalParameters.length; i++) { |
| DartType newType = visitType(node.positionalParameters[i]); |
| if (newType != null) { |
| newPositionalParameters ??= |
| node.positionalParameters.toList(growable: false); |
| newPositionalParameters[i] = newType; |
| } |
| } |
| List<NamedType> newNamedParameters = null; |
| for (int i = 0; i < node.namedParameters.length; i++) { |
| DartType newType = visitType(node.namedParameters[i].type); |
| NamedType newNamedType = |
| createNamedType(node.namedParameters[i], newType); |
| if (newNamedType != null) { |
| newNamedParameters ??= node.namedParameters.toList(growable: false); |
| newNamedParameters[i] = newNamedType; |
| } |
| } |
| changeVariance(); |
| DartType newTypedefType = visitType(node.typedefType); |
| |
| return createFunctionType( |
| node, |
| newNullability, |
| newTypeParameters, |
| newReturnType, |
| newPositionalParameters, |
| newNamedParameters, |
| newTypedefType); |
| } |
| |
| NamedType createNamedType(NamedType node, DartType newType) { |
| if (newType == null) { |
| return null; |
| } else { |
| return new NamedType(node.name, newType, isRequired: node.isRequired); |
| } |
| } |
| |
| DartType createFunctionType( |
| FunctionType node, |
| Nullability newNullability, |
| List<TypeParameter> newTypeParameters, |
| DartType newReturnType, |
| List<DartType> newPositionalParameters, |
| List<NamedType> newNamedParameters, |
| TypedefType newTypedefType) { |
| if (newNullability == null && |
| newReturnType == null && |
| newPositionalParameters == null && |
| newNamedParameters == null && |
| newTypedefType == null) { |
| // No nullability or types had to be substituted. |
| return null; |
| } else { |
| return new FunctionType( |
| newPositionalParameters ?? node.positionalParameters, |
| newReturnType ?? node.returnType, |
| newNullability ?? node.nullability, |
| namedParameters: newNamedParameters ?? node.namedParameters, |
| typeParameters: newTypeParameters ?? node.typeParameters, |
| requiredParameterCount: node.requiredParameterCount, |
| typedefType: newTypedefType ?? node.typedefType); |
| } |
| } |
| |
| @override |
| DartType visitInterfaceType(InterfaceType node) { |
| Nullability newNullability = visitNullability(node); |
| List<DartType> newTypeArguments = null; |
| for (int i = 0; i < node.typeArguments.length; i++) { |
| DartType substitution = node.typeArguments[i].accept(this); |
| if (substitution != null) { |
| newTypeArguments ??= node.typeArguments.toList(growable: false); |
| newTypeArguments[i] = substitution; |
| } |
| } |
| return createInterfaceType(node, newNullability, newTypeArguments); |
| } |
| |
| DartType createInterfaceType(InterfaceType node, Nullability newNullability, |
| List<DartType> newTypeArguments) { |
| if (newNullability == null && newTypeArguments == null) { |
| // No nullability or type arguments needed to be substituted. |
| return null; |
| } else { |
| if (node.classNode.name == 'Null' && |
| node.classNode.enclosingLibrary.importUri.scheme == 'dart' && |
| node.classNode.enclosingLibrary.importUri.path == 'core') { |
| return null; |
| } |
| return new InterfaceType( |
| node.classNode, |
| newNullability ?? node.nullability, |
| newTypeArguments ?? node.typeArguments); |
| } |
| } |
| |
| @override |
| DartType visitFutureOrType(FutureOrType node) { |
| Nullability newNullability = visitNullability(node); |
| DartType newTypeArgument = node.typeArgument.accept(this); |
| return createFutureOrType(node, newNullability, newTypeArgument); |
| } |
| |
| DartType createFutureOrType( |
| FutureOrType node, Nullability newNullability, DartType newTypeArgument) { |
| if (newNullability == null && newTypeArgument == null) { |
| // No nullability or type arguments needed to be substituted. |
| return null; |
| } else { |
| return new FutureOrType(newTypeArgument ?? node.typeArgument, |
| newNullability ?? node.declaredNullability); |
| } |
| } |
| |
| @override |
| DartType visitDynamicType(DynamicType node) => null; |
| |
| @override |
| DartType visitNeverType(NeverType node) { |
| Nullability newNullability = visitNullability(node); |
| return createNeverType(node, newNullability); |
| } |
| |
| DartType createNeverType(NeverType node, Nullability newNullability) { |
| if (newNullability == null) { |
| // No nullability needed to be substituted. |
| return null; |
| } else { |
| return new NeverType(newNullability); |
| } |
| } |
| |
| @override |
| DartType visitInvalidType(InvalidType node) => null; |
| |
| @override |
| DartType visitBottomType(BottomType node) => null; |
| |
| @override |
| DartType visitVoidType(VoidType node) => null; |
| |
| @override |
| DartType visitTypeParameterType(TypeParameterType node) { |
| Nullability newNullability = visitNullability(node); |
| if (node.promotedBound != null) { |
| DartType newPromotedBound = node.promotedBound.accept(this); |
| return createPromotedTypeParameterType( |
| node, newNullability, newPromotedBound); |
| } |
| return createTypeParameterType(node, newNullability); |
| } |
| |
| DartType createTypeParameterType( |
| TypeParameterType node, Nullability newNullability) { |
| if (newNullability == null) { |
| // No nullability needed to be substituted. |
| return null; |
| } else { |
| return new TypeParameterType(node.parameter, newNullability); |
| } |
| } |
| |
| DartType createPromotedTypeParameterType(TypeParameterType node, |
| Nullability newNullability, DartType newPromotedBound) { |
| if (newNullability == null && newPromotedBound == null) { |
| // No nullability or bound needed to be substituted. |
| return null; |
| } else { |
| return new TypeParameterType( |
| node.parameter, |
| newNullability ?? node.declaredNullability, |
| newPromotedBound ?? node.promotedBound); |
| } |
| } |
| |
| @override |
| DartType visitTypedefType(TypedefType node) { |
| Nullability newNullability = visitNullability(node); |
| List<DartType> newTypeArguments = null; |
| for (int i = 0; i < node.typeArguments.length; i++) { |
| DartType substitution = node.typeArguments[i].accept(this); |
| if (substitution != null) { |
| newTypeArguments ??= node.typeArguments.toList(growable: false); |
| newTypeArguments[i] = substitution; |
| } |
| } |
| return createTypedef(node, newNullability, newTypeArguments); |
| } |
| |
| DartType createTypedef(TypedefType node, Nullability newNullability, |
| List<DartType> newTypeArguments) { |
| if (newNullability == null && newTypeArguments == null) { |
| // No nullability or type arguments needed to be substituted. |
| return null; |
| } else { |
| return new TypedefType( |
| node.typedefNode, |
| newNullability ?? node.nullability, |
| newTypeArguments ?? node.typeArguments); |
| } |
| } |
| |
| @override |
| DartType defaultDartType(DartType node) => null; |
| } |