| // 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 file. | |
| import '../ast.dart'; | |
| import '../type_algebra.dart'; | |
| /// Helper visitor that clones a type if a nested type is replaced, and | |
| /// otherwise returns `null`. | |
| class ReplacementVisitor implements DartTypeVisitor1<DartType?, Variance> { | |
| const new(); | |
| Nullability? visitNullability(DartType node) => null; | |
| @override | |
| DartType? visitFunctionType(FunctionType node, Variance variance) { | |
| Nullability? newNullability = visitNullability(node); | |
| StructuralParameterList? newTypeParameters; | |
| for (int i = 0; i < node.typeParameters.length; i++) { | |
| StructuralParameter typeParameter = node.typeParameters[i]; | |
| // TODO(johnniwinther): Bounds should not be null, even in case of | |
| // cyclic typedefs. Currently | |
| // instantiate_to_bound/non_simple_class_parametrized_typedef_cycle | |
| // fails with this. | |
| DartType? newBound = typeParameter.bound.accept1( | |
| this, | |
| variance.combine(Variance.invariant), | |
| ); | |
| DartType? newDefaultType = typeParameter.defaultType.accept1( | |
| this, | |
| variance.combine(Variance.invariant), | |
| ); | |
| if (newBound != null || newDefaultType != null) { | |
| newTypeParameters ??= StructuralParameterList.from(node.typeParameters); | |
| newTypeParameters[i] = new StructuralParameter( | |
| typeParameter.name, | |
| newBound ?? typeParameter.bound, | |
| newDefaultType ?? typeParameter.defaultType, | |
| ); | |
| } | |
| } | |
| FunctionTypeInstantiator? instantiator; | |
| if (newTypeParameters != null) { | |
| List<DartType> typeParameterTypes = [ | |
| for (StructuralParameter parameter in newTypeParameters) | |
| new StructuralParameterType.withDefaultNullability(parameter), | |
| ]; | |
| instantiator = FunctionTypeInstantiator.fromInstantiation( | |
| node, | |
| typeParameterTypes, | |
| ); | |
| for (int i = 0; i < newTypeParameters.length; i++) { | |
| newTypeParameters[i].bound = instantiator.substitute( | |
| newTypeParameters[i].bound, | |
| ); | |
| } | |
| } | |
| DartType? visitType(DartType? type, Variance variance) { | |
| if (type == null) return null; | |
| DartType? result = type.accept1(this, variance); | |
| if (instantiator != null) { | |
| result = instantiator.substitute(result ?? type); | |
| } | |
| return result; | |
| } | |
| DartType? newReturnType = visitType(node.returnType, variance); | |
| DartTypeList? newPositionalParameters = null; | |
| for (int i = 0; i < node.positionalParameters.length; i++) { | |
| DartType? newType = visitType( | |
| node.positionalParameters[i], | |
| variance.combine(Variance.contravariant), | |
| ); | |
| if (newType != null) { | |
| newPositionalParameters ??= DartTypeList.from( | |
| node.positionalParameters, | |
| ); | |
| newPositionalParameters[i] = newType; | |
| } | |
| } | |
| NamedDartTypeList? newNamedParameters = null; | |
| for (int i = 0; i < node.namedParameters.length; i++) { | |
| DartType? newType = visitType( | |
| node.namedParameters[i].type, | |
| variance.combine(Variance.contravariant), | |
| ); | |
| NamedType? newNamedType = createNamedType( | |
| node.namedParameters[i], | |
| newType, | |
| ); | |
| if (newNamedType != null) { | |
| newNamedParameters ??= NamedDartTypeList.from(node.namedParameters); | |
| newNamedParameters[i] = newNamedType; | |
| } | |
| } | |
| return createFunctionType( | |
| node, | |
| newNullability, | |
| newTypeParameters, | |
| newReturnType, | |
| newPositionalParameters, | |
| newNamedParameters, | |
| ); | |
| } | |
| @override | |
| DartType? visitRecordType(RecordType node, Variance variance) { | |
| Nullability? newNullability = visitNullability(node); | |
| DartType? visitType(DartType? type, Variance variance) { | |
| return type?.accept1(this, variance); | |
| } | |
| DartTypeList? newPositional = null; | |
| for (int i = 0; i < node.positional.length; i++) { | |
| DartType? newType = visitType(node.positional[i], variance); | |
| if (newType != null) { | |
| newPositional ??= DartTypeList.from(node.positional); | |
| newPositional[i] = newType; | |
| } | |
| } | |
| NamedDartTypeList? newNamed = null; | |
| for (int i = 0; i < node.named.length; i++) { | |
| DartType? newType = visitType(node.named[i].type, variance); | |
| NamedType? newNamedType = createNamedType(node.named[i], newType); | |
| if (newNamedType != null) { | |
| newNamed ??= NamedDartTypeList.from(node.named); | |
| newNamed[i] = newNamedType; | |
| } | |
| } | |
| return createRecordType(node, newNullability, newPositional, newNamed); | |
| } | |
| 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, | |
| StructuralParameterList? newTypeParameters, | |
| DartType? newReturnType, | |
| DartTypeList? newPositionalParameters, | |
| NamedDartTypeList? newNamedParameters, | |
| ) { | |
| if (newNullability == null && | |
| newReturnType == null && | |
| newPositionalParameters == null && | |
| newNamedParameters == 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, | |
| ); | |
| } | |
| } | |
| DartType? createRecordType( | |
| RecordType node, | |
| Nullability? newNullability, | |
| DartTypeList? newPositional, | |
| NamedDartTypeList? newNamed, | |
| ) { | |
| if (newNullability == null && newPositional == null && newNamed == null) { | |
| // No nullability or types had to be substituted. | |
| return null; | |
| } else { | |
| return new RecordType( | |
| newPositional ?? node.positional, | |
| newNamed ?? node.named, | |
| newNullability ?? node.nullability, | |
| ); | |
| } | |
| } | |
| @override | |
| DartType? visitInterfaceType(InterfaceType node, Variance variance) { | |
| Nullability? newNullability = visitNullability(node); | |
| DartTypeList? newTypeArguments = null; | |
| for (int i = 0; i < node.typeArguments.length; i++) { | |
| DartType? substitution = node.typeArguments[i].accept1(this, variance); | |
| if (substitution != null) { | |
| newTypeArguments ??= DartTypeList.from(node.typeArguments); | |
| newTypeArguments[i] = substitution; | |
| } | |
| } | |
| return createInterfaceType(node, newNullability, newTypeArguments); | |
| } | |
| DartType? createInterfaceType( | |
| InterfaceType node, | |
| Nullability? newNullability, | |
| DartTypeList? newTypeArguments, | |
| ) { | |
| if (newNullability == null && newTypeArguments == null) { | |
| // No nullability or type arguments needed to be substituted. | |
| return null; | |
| } else { | |
| return new InterfaceType.byReference( | |
| node.classReference, | |
| newNullability ?? node.nullability, | |
| newTypeArguments ?? node.typeArguments, | |
| ); | |
| } | |
| } | |
| @override | |
| DartType? visitFutureOrType(FutureOrType node, Variance variance) { | |
| Nullability? newNullability = visitNullability(node); | |
| DartType? newTypeArgument = node.typeArgument.accept1(this, variance); | |
| 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 { | |
| newTypeArgument ??= node.typeArgument; | |
| newNullability ??= node.declaredNullability; | |
| // The top-level nullability of a FutureOr should remain the same, with | |
| // the exception of the case of [Nullability.undetermined]. In that case | |
| // it remains undetermined if the nullability of [typeArgument] is | |
| // undetermined, and otherwise it should become | |
| // [Nullability.nonNullable]. | |
| Nullability adjustedNullability; | |
| if (newNullability == Nullability.undetermined) { | |
| if (newTypeArgument.nullability == Nullability.undetermined) { | |
| adjustedNullability = Nullability.undetermined; | |
| } else { | |
| adjustedNullability = Nullability.nonNullable; | |
| } | |
| } else { | |
| adjustedNullability = newNullability; | |
| } | |
| return new FutureOrType(newTypeArgument, adjustedNullability); | |
| } | |
| } | |
| @override | |
| DartType? visitDynamicType(DynamicType node, Variance variance) => null; | |
| @override | |
| DartType? visitNeverType(NeverType node, Variance variance) { | |
| 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 NeverType.fromNullability(newNullability); | |
| } | |
| } | |
| @override | |
| DartType? visitNullType(NullType node, Variance variance) => null; | |
| @override | |
| DartType? visitInvalidType(InvalidType node, Variance variance) => null; | |
| @override | |
| DartType? visitVoidType(VoidType node, Variance variance) => null; | |
| @override | |
| DartType? visitTypeParameterType(TypeParameterType node, Variance variance) { | |
| Nullability? newNullability = visitNullability(node); | |
| return createTypeParameterType(node, newNullability); | |
| } | |
| @override | |
| DartType? visitStructuralParameterType( | |
| StructuralParameterType node, | |
| Variance variance, | |
| ) { | |
| Nullability? newNullability = visitNullability(node); | |
| return createStructuralParameterType(node, newNullability); | |
| } | |
| @override | |
| DartType? visitIntersectionType(IntersectionType node, Variance variance) { | |
| DartType? newLeft = node.left.accept1(this, variance); | |
| DartType? newRight = node.right.accept1(this, variance); | |
| return createIntersectionType( | |
| node, | |
| newLeft as TypeParameterType?, | |
| newRight, | |
| ); | |
| } | |
| 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? createStructuralParameterType( | |
| StructuralParameterType node, | |
| Nullability? newNullability, | |
| ) { | |
| if (newNullability == null) { | |
| // No nullability needed to be substituted. | |
| return null; | |
| } else { | |
| return new StructuralParameterType(node.parameter, newNullability); | |
| } | |
| } | |
| DartType? createIntersectionType( | |
| IntersectionType node, | |
| TypeParameterType? left, | |
| DartType? right, | |
| ) { | |
| if (left == null && right == null) { | |
| return null; | |
| } else { | |
| return new IntersectionType(left ?? node.left, right ?? node.right); | |
| } | |
| } | |
| @override | |
| DartType? visitTypedefType(TypedefType node, Variance variance) { | |
| Nullability? newNullability = visitNullability(node); | |
| DartTypeList? newTypeArguments = null; | |
| for (int i = 0; i < node.typeArguments.length; i++) { | |
| DartType? substitution = node.typeArguments[i].accept1( | |
| this, | |
| variance.combine(node.typedefNode.typeParameters[i].variance), | |
| ); | |
| if (substitution != null) { | |
| newTypeArguments ??= DartTypeList.from(node.typeArguments); | |
| newTypeArguments[i] = substitution; | |
| } | |
| } | |
| return createTypedef(node, newNullability, newTypeArguments); | |
| } | |
| DartType? createTypedef( | |
| TypedefType node, | |
| Nullability? newNullability, | |
| DartTypeList? 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? visitExtensionType(ExtensionType node, Variance variance) { | |
| Nullability? newNullability = visitNullability(node); | |
| DartTypeList? newTypeArguments = null; | |
| for (int i = 0; i < node.typeArguments.length; i++) { | |
| DartType? substitution = node.typeArguments[i].accept1( | |
| this, | |
| variance.combine( | |
| node.extensionTypeDeclaration.typeParameters[i].variance, | |
| ), | |
| ); | |
| if (substitution != null) { | |
| newTypeArguments ??= DartTypeList.from(node.typeArguments); | |
| newTypeArguments[i] = substitution; | |
| } | |
| } | |
| return createExtensionType(node, newNullability, newTypeArguments); | |
| } | |
| DartType? createExtensionType( | |
| ExtensionType node, | |
| Nullability? newNullability, | |
| DartTypeList? newTypeArguments, | |
| ) { | |
| if (newNullability == null && newTypeArguments == null) { | |
| // No nullability or type arguments needed to be substituted. | |
| return null; | |
| } else { | |
| return new ExtensionType( | |
| node.extensionTypeDeclaration, | |
| newNullability ?? node.declaredNullability, | |
| newTypeArguments ?? node.typeArguments, | |
| ); | |
| } | |
| } | |
| @override | |
| DartType? visitAuxiliaryType(AuxiliaryType node, Variance variance) { | |
| // TODO(johnniwinther): Use [DartTypeVisitor1AuxiliaryFunction] to handle | |
| // [AuxiliaryType]s. | |
| return null; | |
| } | |
| @override | |
| DartType? visitFunctionTypeParameterType( | |
| FunctionTypeParameterType node, | |
| Variance arg, | |
| ) { | |
| // TODO(cstefantsova): Implement visitFunctionTypeParameterType. | |
| throw UnimplementedError( | |
| "Unimplemented support for $node (${node.runtimeType}).", | |
| ); | |
| } | |
| @override | |
| DartType? visitClassTypeParameterType( | |
| ClassTypeParameterType node, | |
| Variance arg, | |
| ) { | |
| // TODO(cstefantsova): Implement visitClassTypeParameterType. | |
| throw UnimplementedError( | |
| "Unimplemented support for $node (${node.runtimeType}).", | |
| ); | |
| } | |
| } |