blob: 72ddff657faa57e253813d69d15aebf59672dd37 [file] [log] [blame]
// 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;
}