blob: 35b8c5e1c2934316ca74568c936feec76d87ec06 [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/ast/ast.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/type.dart';
import 'package:analyzer/src/generated/source.dart';
import 'package:nnbd_migration/nnbd_migration.dart';
import 'package:nnbd_migration/nullability_state.dart';
/// Information exposed to the migration client about the set of nullability
/// nodes decorating a type in the program being migrated.
abstract class DecoratedTypeInfo {
/// Information about the graph node associated with the decision of whether
/// or not to make this type into a nullable type.
NullabilityNodeInfo get node;
/// If [type] is a function type, information about the set of nullability
/// nodes decorating the type's return type.
DecoratedTypeInfo get returnType;
/// The original (pre-migration) type that is being migrated.
DartType get type;
/// If [type] is a function type, looks up information about the set of
/// nullability nodes decorating one of the type's named parameter types.
DecoratedTypeInfo namedParameter(String name);
/// If [type] is a function type, looks up information about the set of
/// nullability nodes decorating one of the type's positional parameter types.
/// (This could be an optional or a required positional parameter).
DecoratedTypeInfo positionalParameter(int i);
/// If [type] is an interface type, looks up information about the set of
/// nullability nodes decorating one of the type's type arguments.
DecoratedTypeInfo typeArgument(int i);
}
/// Information exposed to the migration client about an edge in the nullability
/// graph.
///
/// A graph edge represents a dependency relationship between two types being
/// migrated, suggesting that if one type (the source) is made nullable, it may
/// be desirable to make the other type (the destination) nullable as well.
abstract class EdgeInfo implements FixReasonInfo {
/// Information about the graph node that this edge "points to".
NullabilityNodeInfo get destinationNode;
/// The set of "guard nodes" for this edge. Guard nodes are graph nodes whose
/// nullability determines whether it is important to satisfy a graph edge.
/// If at least one of an edge's guards is non-nullable, then it is not
/// important to satisfy the graph edge. (Typically this is because the code
/// that led to the graph edge being created is only reachable if the guards
/// are all nullable).
Iterable<NullabilityNodeInfo> get guards;
/// A boolean indicating whether the graph edge is a "hard" edge. Hard edges
/// are associated with unconditional control flow, and thus allow information
/// about non-nullability to be propagated "upstream" through the nullability
/// graph.
bool get isHard;
/// A boolean indicating whether the graph edge is "satisfied". At its heart,
/// the nullability propagation algorithm is an effort to satisfy graph edges
/// in a way that corresponds to the user's intent. A graph edge is
/// considered satisfied if any of the following is true:
/// - Its [sourceNode] is non-nullable.
/// - One of its [guards] is non-nullable.
/// - Its [destinationNode] is nullable.
bool get isSatisfied;
/// Indicates whether all the upstream nodes of this edge are nullable (and
/// thus downstream nullability propagation should try to make the destination
/// node nullable, if possible).
bool get isTriggered;
/// A boolean indicating whether the graph edge is a "union" edge. Union
/// edges are edges for which the nullability propagation algorithm tries to
/// ensure that both the [sourceNode] and the [destinationNode] have the
/// same nullability. Typically these are associated with situations where
/// Dart language semantics require two types to be the same type (e.g. a type
/// formal bound on a generic function type in a base class, and the
/// corresponding type formal bound on a generic function type in an
/// overriding class).
///
/// The [isHard] property is always true for union edges.
bool get isUnion;
/// Indicates whether the downstream node of this edge is non-nullable and the
/// edge is hard (and thus upstream nullability propagation should try to make
/// the source node non-nullable, if possible).
bool get isUpstreamTriggered;
/// Information about the graph node that this edge "points away from".
NullabilityNodeInfo get sourceNode;
}
/// Information exposed to the migration client about the location in source
/// code that led an edge to be introduced into the nullability graph.
abstract class EdgeOriginInfo {
/// The kind of origin represented by this info.
EdgeOriginKind get kind;
/// The AST node that led the edge to be introduced into the nullability
/// graph.
AstNode get node;
/// The source file that [node] appears in.
Source get source;
}
/// An enumeration of the various kinds of edge origins created by the migration
/// engine.
enum EdgeOriginKind {
alwaysNullableType,
compoundAssignment,
defaultValue,
expressionChecks,
fieldFormalParameter,
forEachVariable,
greatestLowerBound,
ifNull,
implicitMixinSuperCall,
inheritance,
initializerInference,
instantiateToBounds,
isCheckComponentType,
isCheckMainType,
namedParameterNotSupplied,
nonNullAssertion,
nullabilityComment,
optionalFormalParameter,
}
/// Interface used by the migration engine to expose information to its client
/// about a reason for a modification to the source file.
abstract class FixReasonInfo {}
/// Interface used by the migration engine to expose information to its client
/// about the decisions made during migration, and how those decisions relate to
/// the input source code.
abstract class NullabilityMigrationInstrumentation {
/// Called whenever an explicit [typeAnnotation] is found in the source code,
/// to report the nullability [node] that was associated with this type. If
/// the migration engine determines that the [node] should be nullable, a `?`
/// will be inserted after the type annotation.
void explicitTypeNullability(
Source source, TypeAnnotation typeAnnotation, NullabilityNodeInfo node);
/// Called whenever reference is made to an [element] outside of the code
/// being migrated, to report the nullability nodes associated with the type
/// of the element.
void externalDecoratedType(Element element, DecoratedTypeInfo decoratedType);
/// Called whenever a fix is decided upon, to report the reasons for the fix.
void fix(SingleNullabilityFix fix, Iterable<FixReasonInfo> reasons);
/// Called whenever the migration engine creates a graph edge between
/// nullability nodes, to report information about the edge that was created,
/// and why it was created.
void graphEdge(EdgeInfo edge, EdgeOriginInfo originInfo);
/// Called when the migration engine start up, to report information about the
/// immutable migration nodes [never] and [always] that are used as the
/// starting point for nullability propagation.
void immutableNodes(NullabilityNodeInfo never, NullabilityNodeInfo always);
/// Called whenever the migration engine encounters an implicit return type
/// associated with an AST node, to report the nullability nodes associated
/// with the implicit return type of the AST node.
///
/// [node] is the AST node having an implicit return type; it may be an
/// executable declaration, function-typed formal parameter declaration,
/// function type alias declaration, GenericFunctionType, or a function
/// expression.
void implicitReturnType(
Source source, AstNode node, DecoratedTypeInfo decoratedReturnType);
/// Called whenever the migration engine encounters an implicit type
/// associated with an AST node, to report the nullability nodes associated
/// with the implicit type of the AST node.
///
/// [node] is the AST node having an implicit type; it may be a formal
/// parameter, a declared identifier, or a variable in a variable declaration
/// list.
void implicitType(
Source source, AstNode node, DecoratedTypeInfo decoratedType);
/// Called whenever the migration engine encounters an AST node with implicit
/// type arguments, to report the nullability nodes associated with the
/// implicit type arguments of the AST node.
///
/// [node] is the AST node having implicit type arguments; it may be a
/// constructor redirection, function expression invocation, method
/// invocation, instance creation expression, list/map/set literal, or type
/// annotation.
void implicitTypeArguments(
Source source, AstNode node, Iterable<DecoratedTypeInfo> types);
/// Called whenever the migration engine performs a step in the propagation of
/// nullability information through the nullability graph, to report details
/// of the step that was performed and why.
void propagationStep(PropagationInfo info);
}
/// Information exposed to the migration client about a single node in the
/// nullability graph.
abstract class NullabilityNodeInfo implements FixReasonInfo {
/// Indicates whether the node is immutable. The only immutable nodes in the
/// nullability graph are the nodes `never` and `always` that are used as the
/// starting points for nullability propagation.
bool get isImmutable;
/// After migration is complete, this getter can be used to query whether
/// the type associated with this node was determined to be nullable.
bool get isNullable;
/// The edges that caused this node to have the nullability that it has.
Iterable<EdgeInfo> get upstreamEdges;
}
/// Information exposed to the migration client about a single step in the
/// nullability propagation algorithm, in which the nullability state of a
/// single node was changed.
abstract class PropagationInfo {
/// The edge that caused the nullability state of [node] to be set to
/// [newState], or `null` if the nullability state was changed for reasons
/// not associated with an edge. Will be `null` when [reason] is
/// [StateChangeReason.substituteInner] or
/// [StateChangeReason.substituteOuter], non-null otherwise.
EdgeInfo get edge;
/// The new state that [node] was placed into.
NullabilityState get newState;
/// The nullability node whose state was changed.
NullabilityNodeInfo get node;
/// The reason the nullability node's state was changed.
StateChangeReason get reason;
/// The substitution node that caused the nullability state of [node] to be
/// set to [newState], or `null` if the nullability state was changed for
/// reasons not associated with a substitution node. Will be non-null when
/// [reason] is [StateChangeReason.substituteInner] or
/// [StateChangeReason.substituteOuter], `null` otherwise.
SubstitutionNodeInfo get substitutionNode;
}
/// Enum representing the various reasons why a nullability node might change
/// state during nullability propagation.
enum StateChangeReason {
/// A union edge exists between this node and a node that is known a priori to
/// be nullable, so this node is being made nullable as well.
union,
/// A hard or union edge exists whose source is this node, and whose
/// destination is non-nullable, so this node is being made non-nullable as
/// well.
upstream,
/// An edge exists whose destination is this node, and whose source is
/// nullable, so this node is being made nullable as well.
downstream,
/// An edge exists whose source is this node, and whose destination is exact
/// nullable, so this node is being made exact nullable as well.
exactUpstream,
/// A substitution node exists whose inner node points to this node, and the
/// substitution node is nullable, so this node is being made nullable as
/// well.
substituteInner,
/// A substitution node exists whose outer node points to this node, and the
/// substitution node is nullable, so this node is being made nullable as
/// well.
substituteOuter,
}
/// Information exposed to the migration client about a node in the nullability
/// graph resulting from a type substitution.
abstract class SubstitutionNodeInfo extends NullabilityNodeInfo {
/// Nullability node representing the inner type of the substitution.
///
/// For example, if this NullabilityNode arose from substituting `int*` for
/// `T` in the type `T*`, [innerNode] is the nullability corresponding to the
/// `*` in `int*`.
NullabilityNodeInfo get innerNode;
/// Nullability node representing the outer type of the substitution.
///
/// For example, if this NullabilityNode arose from substituting `int*` for
/// `T` in the type `T*`, [innerNode] is the nullability corresponding to the
/// `*` in `T*`.
NullabilityNodeInfo get outerNode;
}