| // 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.md file. |
| |
| import 'package:analyzer/dart/element/element.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/replacement_visitor.dart'; |
| import 'package:analyzer/src/dart/element/type.dart'; |
| |
| /// Returns [type] in which all promoted type variables have been replace with |
| /// their unpromoted equivalents, and, if [library] is non-nullable by default, |
| /// replaces all legacy types with their non-nullable equivalents. |
| DartType demoteType(LibraryElement library, DartType type) { |
| if (library.isNonNullableByDefault) { |
| var visitor = const DemotionNonNullificationVisitor(); |
| return type.accept(visitor) ?? type; |
| } else { |
| var visitor = const DemotionNonNullificationVisitor(nonNullifyTypes: false); |
| return type.accept(visitor) ?? type; |
| } |
| } |
| |
| /// Returns `true` if type contains a promoted type variable. |
| bool hasPromotedTypeVariable(DartType type) { |
| return type.accept( |
| const _HasPromotedTypeVariableVisitor(), |
| ); |
| } |
| |
| /// Visitor that replaces all promoted type variables the type variable itself |
| /// and/or replaces all legacy types with non-nullable types. |
| /// |
| /// The visitor returns `null` if the type wasn't changed. |
| class DemotionNonNullificationVisitor extends ReplacementVisitor { |
| final bool demoteTypeVariables; |
| final bool nonNullifyTypes; |
| |
| const DemotionNonNullificationVisitor({ |
| this.demoteTypeVariables = true, |
| this.nonNullifyTypes = true, |
| }) : assert(demoteTypeVariables || nonNullifyTypes); |
| |
| @override |
| NullabilitySuffix visitNullability(DartType type) { |
| if (nonNullifyTypes && type.nullabilitySuffix == NullabilitySuffix.star) { |
| return NullabilitySuffix.none; |
| } |
| return null; |
| } |
| |
| @override |
| DartType visitTypeParameterType(TypeParameterType type) { |
| var newNullability = visitNullability(type); |
| |
| if (demoteTypeVariables) { |
| var typeImpl = type as TypeParameterTypeImpl; |
| if (typeImpl.promotedBound != null) { |
| return TypeParameterTypeImpl( |
| element: type.element, |
| nullabilitySuffix: newNullability ?? type.nullabilitySuffix, |
| ); |
| } |
| } |
| |
| return createTypeParameterType( |
| type: type, |
| newNullability: newNullability, |
| ); |
| } |
| } |
| |
| /// Visitor that returns `true` if a type contains a promoted type variable. |
| class _HasPromotedTypeVariableVisitor extends UnifyingTypeVisitor<bool> { |
| const _HasPromotedTypeVariableVisitor(); |
| |
| @override |
| bool visitDartType(DartType type) => false; |
| |
| @override |
| bool visitFunctionType(FunctionType type) { |
| if (type.returnType.accept(this)) { |
| return true; |
| } |
| |
| for (var parameter in type.parameters) { |
| if (parameter.type.accept(this)) { |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| @override |
| bool visitInterfaceType(InterfaceType type) { |
| for (var typeArgument in type.typeArguments) { |
| if (typeArgument.accept(this)) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| @override |
| bool visitTypeParameterType(TypeParameterType type) { |
| return (type as TypeParameterTypeImpl).promotedBound != null; |
| } |
| } |