| // Copyright (c) 2019, 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/element.dart'; |
| import 'package:analyzer/dart/element/type.dart'; |
| import 'package:analyzer/src/dart/element/handle.dart'; |
| import 'package:nnbd_migration/src/decorated_type.dart'; |
| import 'package:nnbd_migration/src/node_builder.dart'; |
| import 'package:nnbd_migration/src/nullability_node.dart'; |
| |
| /// Responsible for building and maintaining information about nullability |
| /// decorations related to the class hierarchy. |
| /// |
| /// For instance, if one class is a subclass of the other, we record the |
| /// nullabilities of all the types involved in the subclass relationship. |
| class DecoratedClassHierarchy { |
| final VariableRepository _variables; |
| |
| final NullabilityGraph _graph; |
| |
| /// Cache for speeding up the computation of |
| /// [_getGenericSupertypeDecorations]. |
| final Map<ClassElement, Map<ClassElement, DecoratedType>> |
| _genericSupertypeDecorations = {}; |
| |
| DecoratedClassHierarchy(this._variables, this._graph); |
| |
| /// Retrieves a [DecoratedType] describing how [class_] implements |
| /// [superclass]. |
| /// |
| /// If [class_] does not implement [superclass], raises an exception. |
| /// |
| /// Note that the returned [DecoratedType] will have a node of `never`, |
| /// because the relationship between a class and its superclass is not |
| /// nullable. |
| DecoratedType getDecoratedSupertype( |
| ClassElement class_, ClassElement superclass) { |
| assert(class_ is! ClassElementHandle); |
| assert(superclass is! ClassElementHandle); |
| if (superclass.typeParameters.isEmpty) { |
| return DecoratedType(superclass.type, _graph.never); |
| } |
| return _getGenericSupertypeDecorations(class_)[superclass] ?? |
| (throw StateError('Unrelated types')); |
| } |
| |
| /// Computes a map whose keys are all the superclasses of [class_], and whose |
| /// values indicate how [class_] implements each superclass. |
| Map<ClassElement, DecoratedType> _getGenericSupertypeDecorations( |
| ClassElement class_) { |
| var decorations = _genericSupertypeDecorations[class_]; |
| if (decorations == null) { |
| // Call ourselves recursively to compute how each of [class_]'s direct |
| // superclasses relates to all of its transitive superclasses. |
| decorations = _genericSupertypeDecorations[class_] = {}; |
| var decoratedDirectSupertypes = |
| _variables.decoratedDirectSupertypes(class_); |
| for (var entry in decoratedDirectSupertypes.entries) { |
| var superclass = entry.key; |
| var decoratedSupertype = entry.value; |
| var supertype = decoratedSupertype.type as InterfaceType; |
| // Compute a type substitution to determine how [class_] relates to |
| // this specific [superclass]. |
| Map<TypeParameterElement, DecoratedType> substitution = {}; |
| for (int i = 0; i < supertype.typeArguments.length; i++) { |
| substitution[supertype.typeParameters[i]] = |
| decoratedSupertype.typeArguments[i]; |
| } |
| // Apply that substitution to the relation between [superclass] and |
| // each of its transitive superclasses, to determine the relation |
| // between [class_] and the transitive superclass. |
| var recursiveSupertypeDecorations = |
| _getGenericSupertypeDecorations(superclass); |
| for (var entry in recursiveSupertypeDecorations.entries) { |
| decorations[entry.key] ??= entry.value.substitute(substitution); |
| } |
| // Also record the relation between [class_] and its direct |
| // superclass. |
| decorations[superclass] ??= decoratedSupertype; |
| } |
| } |
| return decorations; |
| } |
| } |