blob: 3935f4d4cb9947d3e418000d089e1baccfe7270d [file] [log] [blame]
// 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/nullability_suffix.dart';
import 'package:analyzer/dart/element/type.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 [type] implements [superclass].
///
/// If [type] is not an interface type, or it does not implement [superclass],
/// raises an exception.
DecoratedType asInstanceOf(DecoratedType type, ClassElement superclass) {
var typeType = type.type as InterfaceType;
var class_ = typeType.element;
if (class_ == superclass) return type;
var result = getDecoratedSupertype(class_, superclass);
if (result.typeArguments.isNotEmpty && type.typeArguments.isNotEmpty) {
// TODO(paulberry): test
result = result.substitute(type.asSubstitution);
}
return result.withNode(type.node);
}
/// 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_.library.isDartCore && class_.name == 'Null'));
if (superclass.typeParameters.isEmpty) {
return DecoratedType(
superclass.instantiate(
typeArguments: const [],
nullabilitySuffix: NullabilitySuffix.none,
),
_graph.never,
);
}
return _getGenericSupertypeDecorations(class_)[superclass] ??
(throw StateError('Unrelated types: $class_ and $superclass'));
}
/// 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 = {};
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;
}
_genericSupertypeDecorations[class_] = decorations;
}
return decorations;
}
}