Migration: Make always/never instance fields of the nullability graph.

Previously, they were static, which made them easier to access, but
made it difficult to track their edges (because we couldn't safely
mutate them, so we had to store their edges in special fields of
NullabilityGraph).  Now that we're passing NullabilityGraph all over
the place anyhow, there's no benefit to making them static anymore.

Change-Id: Ia3e7d32ae479f40f505621e5d6df04060e39b1c7
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/105723
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
diff --git a/pkg/nnbd_migration/lib/src/decorated_type.dart b/pkg/nnbd_migration/lib/src/decorated_type.dart
index a740c1c..e3785f0 100644
--- a/pkg/nnbd_migration/lib/src/decorated_type.dart
+++ b/pkg/nnbd_migration/lib/src/decorated_type.dart
@@ -45,12 +45,12 @@
 
   /// Creates a [DecoratedType] corresponding to the given [element], which is
   /// presumed to have come from code that is already migrated.
-  factory DecoratedType.forElement(Element element) {
+  factory DecoratedType.forElement(Element element, NullabilityGraph graph) {
     DecoratedType decorate(DartType type) {
       assert((type as TypeImpl).nullabilitySuffix ==
           NullabilitySuffix.star); // TODO(paulberry)
       if (type is FunctionType) {
-        var decoratedType = DecoratedType(type, NullabilityNode.never,
+        var decoratedType = DecoratedType(type, graph.never,
             returnType: decorate(type.returnType), positionalParameters: []);
         for (var parameter in type.parameters) {
           assert(parameter.isPositional); // TODO(paulberry)
@@ -59,7 +59,7 @@
         return decoratedType;
       } else if (type is InterfaceType) {
         assert(type.typeParameters.isEmpty); // TODO(paulberry)
-        return DecoratedType(type, NullabilityNode.never);
+        return DecoratedType(type, graph.never);
       } else {
         throw type.runtimeType; // TODO(paulberry)
       }
diff --git a/pkg/nnbd_migration/lib/src/graph_builder.dart b/pkg/nnbd_migration/lib/src/graph_builder.dart
index cf3c46d..c7f7b14 100644
--- a/pkg/nnbd_migration/lib/src/graph_builder.dart
+++ b/pkg/nnbd_migration/lib/src/graph_builder.dart
@@ -78,14 +78,12 @@
 
   GraphBuilder(TypeProvider typeProvider, this._variables, this._graph,
       this._source, this.listener)
-      : _notNullType =
-            DecoratedType(typeProvider.objectType, NullabilityNode.never),
+      : _notNullType = DecoratedType(typeProvider.objectType, _graph.never),
         _nonNullableBoolType =
-            DecoratedType(typeProvider.boolType, NullabilityNode.never),
+            DecoratedType(typeProvider.boolType, _graph.never),
         _nonNullableTypeType =
-            DecoratedType(typeProvider.typeType, NullabilityNode.never),
-        _nullType =
-            DecoratedType(typeProvider.nullType, NullabilityNode.always);
+            DecoratedType(typeProvider.typeType, _graph.never),
+        _nullType = DecoratedType(typeProvider.nullType, _graph.always);
 
   /// Gets the decorated type of [element] from [_variables], performing any
   /// necessary substitutions.
@@ -120,13 +118,11 @@
       var decoratedElementType =
           _variables.decoratedElementType(variable, create: true);
       if (baseElement.isGetter) {
-        decoratedBaseType = DecoratedType(
-            baseElement.type, NullabilityNode.never,
+        decoratedBaseType = DecoratedType(baseElement.type, _graph.never,
             returnType: decoratedElementType);
       } else {
         assert(baseElement.isSetter);
-        decoratedBaseType = DecoratedType(
-            baseElement.type, NullabilityNode.never,
+        decoratedBaseType = DecoratedType(baseElement.type, _graph.never,
             positionalParameters: [decoratedElementType]);
       }
     } else {
@@ -220,7 +216,7 @@
 
   @override
   DecoratedType visitBooleanLiteral(BooleanLiteral node) {
-    return DecoratedType(node.staticType, NullabilityNode.never);
+    return DecoratedType(node.staticType, _graph.never);
   }
 
   @override
@@ -258,7 +254,7 @@
         // Nothing to do; the implicit default value of `null` will never be
         // reached.
       } else {
-        NullabilityNode.recordAssignment(NullabilityNode.always,
+        NullabilityNode.recordAssignment(_graph.always,
             getOrComputeElementType(node.declaredElement).node, _guards, _graph,
             hard: false);
       }
@@ -351,7 +347,7 @@
 
   @override
   DecoratedType visitIntegerLiteral(IntegerLiteral node) {
-    return DecoratedType(node.staticType, NullabilityNode.never);
+    return DecoratedType(node.staticType, _graph.never);
   }
 
   @override
@@ -484,19 +480,19 @@
 
   @override
   DecoratedType visitStringLiteral(StringLiteral node) {
-    return DecoratedType(node.staticType, NullabilityNode.never);
+    return DecoratedType(node.staticType, _graph.never);
   }
 
   @override
   DecoratedType visitThisExpression(ThisExpression node) {
-    return DecoratedType(node.staticType, NullabilityNode.never);
+    return DecoratedType(node.staticType, _graph.never);
   }
 
   @override
   DecoratedType visitThrowExpression(ThrowExpression node) {
     node.expression.accept(this);
     // TODO(paulberry): do we need to check the expression type?  I think not.
-    return DecoratedType(node.staticType, NullabilityNode.never);
+    return DecoratedType(node.staticType, _graph.never);
   }
 
   @override
@@ -517,7 +513,7 @@
             hard: true);
       }
     }
-    return DecoratedType(typeName.type, NullabilityNode.never);
+    return DecoratedType(typeName.type, _graph.never);
   }
 
   @override
diff --git a/pkg/nnbd_migration/lib/src/node_builder.dart b/pkg/nnbd_migration/lib/src/node_builder.dart
index 44a7475..88991e5 100644
--- a/pkg/nnbd_migration/lib/src/node_builder.dart
+++ b/pkg/nnbd_migration/lib/src/node_builder.dart
@@ -140,7 +140,7 @@
     var type = node.type;
     if (type.isVoid || type.isDynamic) {
       var nullabilityNode = NullabilityNode.forTypeAnnotation(node.end);
-      _graph.connect(NullabilityNode.always, nullabilityNode);
+      _graph.connect(_graph.always, nullabilityNode);
       var decoratedType =
           DecoratedTypeAnnotation(type, nullabilityNode, node.offset);
       _variables.recordDecoratedTypeAnnotation(_source, node, decoratedType,
@@ -186,10 +186,10 @@
     }
     switch (_classifyComment(node.endToken.next.precedingComments)) {
       case _NullabilityComment.bang:
-        _graph.connect(decoratedType.node, NullabilityNode.never, hard: true);
+        _graph.connect(decoratedType.node, _graph.never, hard: true);
         break;
       case _NullabilityComment.question:
-        _graph.connect(NullabilityNode.always, decoratedType.node);
+        _graph.connect(_graph.always, decoratedType.node);
         break;
       case _NullabilityComment.none:
         break;
@@ -238,8 +238,7 @@
     var previousFunctionType = _currentFunctionType;
     // TODO(paulberry): test that it's correct to use `null` for the nullability
     // of the function type
-    var functionType = DecoratedType(
-        declaredElement.type, NullabilityNode.never,
+    var functionType = DecoratedType(declaredElement.type, _graph.never,
         returnType: decoratedReturnType,
         positionalParameters: [],
         namedParameters: {});
diff --git a/pkg/nnbd_migration/lib/src/nullability_migration_impl.dart b/pkg/nnbd_migration/lib/src/nullability_migration_impl.dart
index a94f87b..3d405a3 100644
--- a/pkg/nnbd_migration/lib/src/nullability_migration_impl.dart
+++ b/pkg/nnbd_migration/lib/src/nullability_migration_impl.dart
@@ -18,9 +18,9 @@
 class NullabilityMigrationImpl implements NullabilityMigration {
   final NullabilityMigrationListener listener;
 
-  final _variables = Variables();
+  final Variables _variables;
 
-  final _graph = NullabilityGraph();
+  final NullabilityGraph _graph;
 
   final bool _permissive;
 
@@ -30,8 +30,12 @@
   /// as far as possible even though the migration algorithm is not yet
   /// complete.  TODO(paulberry): remove this mode once the migration algorithm
   /// is fully implemented.
-  NullabilityMigrationImpl(this.listener, {bool permissive: false})
-      : _permissive = permissive;
+  NullabilityMigrationImpl(NullabilityMigrationListener listener,
+      {bool permissive: false})
+      : this._(listener, NullabilityGraph(), permissive);
+
+  NullabilityMigrationImpl._(this.listener, this._graph, this._permissive)
+      : _variables = Variables(_graph);
 
   void finish() {
     _graph.propagate();
diff --git a/pkg/nnbd_migration/lib/src/nullability_node.dart b/pkg/nnbd_migration/lib/src/nullability_node.dart
index b18fd8e..e1a68f8 100644
--- a/pkg/nnbd_migration/lib/src/nullability_node.dart
+++ b/pkg/nnbd_migration/lib/src/nullability_node.dart
@@ -37,22 +37,24 @@
   /// `sourceNode` argument to [connect].
   final _allSourceNodes = Set<NullabilityNode>.identity();
 
-  /// List of [NullabilityEdge] objects that are downstream from
-  /// [NullabilityNode.always].  (They can't be stored in
-  /// [NullabilityNode.always] directly because it is immutable).
-  final _downstreamFromAlways = <NullabilityEdge>[];
-
-  /// List of [NullabilityEdge] objects that are upstream from
-  /// [NullabilityNode.never] due to unconditional control flow.  (They can't be
-  /// stored in [NullabilityNode.never] directly because it is immutable).
-  final _upstreamFromNever = <NullabilityEdge>[];
-
   /// List of [NullabilityNodeMutable] objects that were set into the nullable
   /// state by a process other than nullability propagation.  The next time
   /// nullability is propagated, the propagation algorithm will ensure that
   /// edges originating at these nodes are examined.
   final _pendingDownstreamNodes = <NullabilityNodeMutable>[];
 
+  /// Returns a [NullabilityNode] that is a priori nullable.
+  ///
+  /// Propagation of nullability always proceeds downstream starting at this
+  /// node.
+  final NullabilityNode always = _NullabilityNodeImmutable('always', true);
+
+  /// Returns a [NullabilityNode] that is a priori non-nullable.
+  ///
+  /// Propagation of nullability always proceeds upstream starting at this
+  /// node.
+  final NullabilityNode never = _NullabilityNodeImmutable('never', false);
+
   /// Records that [sourceNode] is immediately upstream from [destinationNode].
   void connect(NullabilityNode sourceNode, NullabilityNode destinationNode,
       {bool hard: false, List<NullabilityNode> guards: const []}) {
@@ -61,15 +63,7 @@
     for (var source in sources) {
       _connectDownstream(source, edge);
     }
-    if (destinationNode is NullabilityNodeMutable) {
-      destinationNode._upstreamEdges.add(edge);
-    } else if (destinationNode == NullabilityNode.never) {
-      _upstreamFromNever.add(edge);
-    } else {
-      // We don't need to track nodes that are upstream from `always` because
-      // `always` will never have non-null intent.
-      assert(destinationNode == NullabilityNode.always);
-    }
+    destinationNode._upstreamEdges.add(edge);
   }
 
   /// Determines the nullability of each node in the graph by propagating
@@ -82,25 +76,17 @@
 
   void _connectDownstream(NullabilityNode source, NullabilityEdge edge) {
     _allSourceNodes.add(source);
-    if (source is NullabilityNodeMutable) {
-      source._downstreamEdges.add(edge);
-      if (source is _NullabilityNodeCompound) {
-        for (var component in source._components) {
-          _connectDownstream(component, edge);
-        }
+    source._downstreamEdges.add(edge);
+    if (source is _NullabilityNodeCompound) {
+      for (var component in source._components) {
+        _connectDownstream(component, edge);
       }
-    } else if (source == NullabilityNode.always) {
-      _downstreamFromAlways.add(edge);
-    } else {
-      // We don't need to track nodes that are downstream from `never` because
-      // `never` will never be nullable.
-      assert(source == NullabilityNode.never);
     }
   }
 
   void _debugDump() {
     for (var source in _allSourceNodes) {
-      var edges = _getDownstreamEdges(source);
+      var edges = source._downstreamEdges;
       var destinations =
           edges.where((edge) => edge.primarySource == source).map((edge) {
         var suffixes = <Object>[];
@@ -116,21 +102,9 @@
     }
   }
 
-  Iterable<NullabilityEdge> _getDownstreamEdges(NullabilityNode node) {
-    if (node is NullabilityNodeMutable) {
-      return node._downstreamEdges;
-    } else if (node == NullabilityNode.always) {
-      return _downstreamFromAlways;
-    } else {
-      // No nodes are downstream from `never`.
-      assert(node == NullabilityNode.never);
-      return const [];
-    }
-  }
-
   /// Propagates nullability downstream.
   void _propagateDownstream() {
-    var pendingEdges = <NullabilityEdge>[]..addAll(_downstreamFromAlways);
+    var pendingEdges = <NullabilityEdge>[]..addAll(always._downstreamEdges);
     for (var node in _pendingDownstreamNodes) {
       pendingEdges.addAll(node._downstreamEdges);
     }
@@ -177,7 +151,7 @@
   /// Propagates non-null intent upstream along unconditional control flow
   /// lines.
   void _propagateUpstream() {
-    var pendingEdges = <NullabilityEdge>[]..addAll(_upstreamFromNever);
+    var pendingEdges = <NullabilityEdge>[]..addAll(never._upstreamEdges);
     while (pendingEdges.isNotEmpty) {
       var edge = pendingEdges.removeLast();
       if (!edge.hard) continue;
@@ -203,15 +177,7 @@
   /// There is no guarantee of uniqueness of the iterated nodes.
   @visibleForTesting
   Iterable<NullabilityEdge> getUpstreamEdges(NullabilityNode node) {
-    if (node is NullabilityNodeMutable) {
-      return node._upstreamEdges;
-    } else if (node == NullabilityNode.never) {
-      return _upstreamFromNever;
-    } else {
-      // No nodes are upstream from `always`.
-      assert(node == NullabilityNode.always);
-      return const [];
-    }
+    return node._upstreamEdges;
   }
 
   /// Iterates through all nodes that are "upstream" of [node] (i.e. if
@@ -225,7 +191,7 @@
   @visibleForTesting
   Iterable<NullabilityNode> getUpstreamNodes(NullabilityNode node) sync* {
     for (var source in _allSourceNodes) {
-      for (var edge in _getDownstreamEdges(source)) {
+      for (var edge in source._downstreamEdges) {
         if (edge.destinationNode == node) {
           yield source;
         }
@@ -241,22 +207,24 @@
 /// variables.  Over time this will be replaced by a first class representation
 /// of the nullability inference graph.
 abstract class NullabilityNode {
-  /// [NullabilityNode] used for types that are known a priori to be nullable
-  /// (e.g. the type of the `null` literal).
-  static final NullabilityNode always =
-      _NullabilityNodeImmutable('always', true);
-
-  /// [NullabilityNode] used for types that are known a priori to be
-  /// non-nullable (e.g. the type of an integer literal).
-  static final NullabilityNode never =
-      _NullabilityNodeImmutable('never', false);
-
   static final _debugNamesInUse = Set<String>();
 
   bool _isPossiblyOptional = false;
 
   String _debugName;
 
+  /// List of [NullabilityEdge] objects describing this node's relationship to
+  /// other nodes that are "downstream" from it (meaning that if a key node is
+  /// nullable, then all the nodes in the corresponding value will either have
+  /// to be nullable, or null checks will have to be added).
+  final _downstreamEdges = <NullabilityEdge>[];
+
+  /// List of nodes that are "upstream" from this node via unconditional control
+  /// flow (meaning that if a node in the list is nullable, then there exists
+  /// code that is unguarded by an "if" statement that indicates that this node
+  /// will have to be nullable, or null checks will have to be added).
+  final _upstreamEdges = <NullabilityEdge>[];
+
   /// Creates a [NullabilityNode] representing the nullability of a variable
   /// whose type is `dynamic` due to type inference.
   ///
@@ -304,8 +272,7 @@
 
   /// Gets a string that can be appended to a type name during debugging to help
   /// annotate the nullability of that type.
-  String get debugSuffix =>
-      this == always ? '?' : this == never ? '' : '?($this)';
+  String get debugSuffix => '?($this)';
 
   /// After nullability propagation, this getter can be used to query whether
   /// the type associated with this node should be considered nullable.
@@ -325,13 +292,13 @@
   void recordNamedParameterNotSupplied(
       List<NullabilityNode> guards, NullabilityGraph graph) {
     if (isPossiblyOptional) {
-      graph.connect(NullabilityNode.always, this, guards: guards);
+      graph.connect(graph.always, this, guards: guards);
     }
   }
 
   void recordNonNullIntent(
       List<NullabilityNode> guards, NullabilityGraph graph) {
-    graph.connect(this, NullabilityNode.never, hard: true);
+    graph.connect(this, graph.never, hard: true);
   }
 
   String toString() {
@@ -424,22 +391,10 @@
 /// Base class for nullability nodes whose state can be mutated safely.
 ///
 /// Nearly all nullability nodes derive from this class; the only exceptions are
-/// the fixed nodes [NullabilityNode.always] and [NullabilityNode.never].
+/// the fixed nodes "always "never".
 abstract class NullabilityNodeMutable extends NullabilityNode {
   _NullabilityState _state;
 
-  /// List of [NullabilityEdge] objects describing this node's relationship to
-  /// other nodes that are "downstream" from it (meaning that if a key node is
-  /// nullable, then all the nodes in the corresponding value will either have
-  /// to be nullable, or null checks will have to be added).
-  final _downstreamEdges = <NullabilityEdge>[];
-
-  /// List of nodes that are "upstream" from this node via unconditional control
-  /// flow (meaning that if a node in the list is nullable, then there exists
-  /// code that is unguarded by an "if" statement that indicates that this node
-  /// will have to be nullable, or null checks will have to be added).
-  final _upstreamEdges = <NullabilityEdge>[];
-
   NullabilityNodeMutable._(
       {_NullabilityState initialState: _NullabilityState.undetermined})
       : _state = initialState,
@@ -468,6 +423,9 @@
   _NullabilityNodeImmutable(this._debugPrefix, this.isNullable) : super._();
 
   @override
+  String get debugSuffix => isNullable ? '?' : '';
+
+  @override
   _NullabilityState get _state => isNullable
       ? _NullabilityState.ordinaryNullable
       : _NullabilityState.nonNullable;
diff --git a/pkg/nnbd_migration/lib/src/variables.dart b/pkg/nnbd_migration/lib/src/variables.dart
index df66005..9a8e653 100644
--- a/pkg/nnbd_migration/lib/src/variables.dart
+++ b/pkg/nnbd_migration/lib/src/variables.dart
@@ -13,6 +13,8 @@
 import 'package:nnbd_migration/src/potential_modification.dart';
 
 class Variables implements VariableRecorder, VariableRepository {
+  final NullabilityGraph _graph;
+
   final _decoratedElementTypes = <Element, DecoratedType>{};
 
   final _decoratedTypeAnnotations =
@@ -20,10 +22,12 @@
 
   final _potentialModifications = <Source, List<PotentialModification>>{};
 
+  Variables(this._graph);
+
   @override
   DecoratedType decoratedElementType(Element element, {bool create: false}) =>
       _decoratedElementTypes[element] ??= create
-          ? DecoratedType.forElement(element)
+          ? DecoratedType.forElement(element, _graph)
           : throw StateError('No element found');
 
   @override
diff --git a/pkg/nnbd_migration/test/migration_visitor_test.dart b/pkg/nnbd_migration/test/migration_visitor_test.dart
index f34c48c..5e0d298 100644
--- a/pkg/nnbd_migration/test/migration_visitor_test.dart
+++ b/pkg/nnbd_migration/test/migration_visitor_test.dart
@@ -47,12 +47,12 @@
   /// Checks that there are no nullability nodes upstream from [node] that could
   /// cause it to become nullable.
   void assertNoUpstreamNullability(NullabilityNode node) {
-    // NullabilityNode.never can never become nullable, even if it has nodes
+    // never can never become nullable, even if it has nodes
     // upstream from it.
-    if (node == NullabilityNode.never) return;
+    if (node == never) return;
 
     for (var upstreamNode in graph.getUpstreamNodes(node)) {
-      expect(upstreamNode, NullabilityNode.never);
+      expect(upstreamNode, never);
     }
   }
 
@@ -71,7 +71,7 @@
       {NullabilityNode contextNode, List<NullabilityNode> guards = const []}) {
     expect(expressionChecks.valueNode, same(valueNode));
     if (contextNode == null) {
-      expect(expressionChecks.contextNode, same(NullabilityNode.never));
+      expect(expressionChecks.contextNode, same(never));
     } else {
       expect(expressionChecks.contextNode, same(contextNode));
     }
@@ -417,7 +417,7 @@
 
     var nullable_i = decoratedTypeAnnotation('int i').node;
     var nullable_conditional = decoratedExpressionType('(b ?').node;
-    assertConditional(nullable_conditional, NullabilityNode.always, nullable_i);
+    assertConditional(nullable_conditional, always, nullable_i);
   }
 
   test_conditionalExpression_right_non_null() async {
@@ -444,7 +444,7 @@
 
     var nullable_i = decoratedTypeAnnotation('int i').node;
     var nullable_conditional = decoratedExpressionType('(b ?').node;
-    assertConditional(nullable_conditional, nullable_i, NullabilityNode.always);
+    assertConditional(nullable_conditional, nullable_i, always);
   }
 
   test_functionDeclaration_expression_body() async {
@@ -470,8 +470,7 @@
 void f({int i = null}) {}
 ''');
 
-    assertEdge(NullabilityNode.always, decoratedTypeAnnotation('int').node,
-        hard: false);
+    assertEdge(always, decoratedTypeAnnotation('int').node, hard: false);
   }
 
   test_functionDeclaration_parameter_named_no_default() async {
@@ -479,8 +478,7 @@
 void f({int i}) {}
 ''');
 
-    assertEdge(NullabilityNode.always, decoratedTypeAnnotation('int').node,
-        hard: false);
+    assertEdge(always, decoratedTypeAnnotation('int').node, hard: false);
   }
 
   test_functionDeclaration_parameter_named_no_default_required() async {
@@ -506,8 +504,7 @@
 void f([int i = null]) {}
 ''');
 
-    assertEdge(NullabilityNode.always, decoratedTypeAnnotation('int').node,
-        hard: false);
+    assertEdge(always, decoratedTypeAnnotation('int').node, hard: false);
   }
 
   test_functionDeclaration_parameter_positionalOptional_no_default() async {
@@ -515,8 +512,7 @@
 void f([int i]) {}
 ''');
 
-    assertEdge(NullabilityNode.always, decoratedTypeAnnotation('int').node,
-        hard: false);
+    assertEdge(always, decoratedTypeAnnotation('int').node, hard: false);
   }
 
   test_functionDeclaration_resets_unconditional_control_flow() async {
@@ -571,7 +567,7 @@
 }
 ''');
     var optional_i = possiblyOptionalParameter('int i');
-    expect(getEdges(NullabilityNode.always, optional_i), isNotEmpty);
+    expect(getEdges(always, optional_i), isNotEmpty);
   }
 
   test_functionInvocation_parameter_named_missing_required() async {
@@ -599,7 +595,7 @@
 }
 ''');
 
-    assertNullCheck(checkExpression('null'), NullabilityNode.always,
+    assertNullCheck(checkExpression('null'), always,
         contextNode: decoratedTypeAnnotation('int').node);
   }
 
@@ -956,7 +952,7 @@
   test_never() async {
     await analyze('');
 
-    expect(NullabilityNode.never.isNullable, isFalse);
+    expect(never.isNullable, isFalse);
   }
 
   test_parenthesizedExpression() async {
@@ -966,7 +962,7 @@
 }
 ''');
 
-    assertNullCheck(checkExpression('(null)'), NullabilityNode.always,
+    assertNullCheck(checkExpression('(null)'), always,
         contextNode: decoratedTypeAnnotation('int').node);
   }
 
@@ -1069,8 +1065,7 @@
 }
 ''');
 
-    assertEdge(NullabilityNode.always, decoratedTypeAnnotation('int').node,
-        hard: false);
+    assertEdge(always, decoratedTypeAnnotation('int').node, hard: false);
   }
 
   test_return_null() async {
@@ -1080,7 +1075,7 @@
 }
 ''');
 
-    assertNullCheck(checkExpression('null'), NullabilityNode.always,
+    assertNullCheck(checkExpression('null'), always,
         contextNode: decoratedTypeAnnotation('int').node);
   }
 
@@ -1186,11 +1181,11 @@
 
   MigrationVisitorTestBase() : this._(NullabilityGraphForTesting());
 
-  MigrationVisitorTestBase._(this.graph) : _variables = _Variables();
+  MigrationVisitorTestBase._(this.graph) : _variables = _Variables(graph);
 
-  NullabilityNode get always => NullabilityNode.always;
+  NullabilityNode get always => graph.always;
 
-  NullabilityNode get never => NullabilityNode.never;
+  NullabilityNode get never => graph.never;
 
   TypeProvider get typeProvider => testAnalysisResult.typeProvider;
 
@@ -1300,7 +1295,7 @@
     var decoratedIntType = decoratedTypeAnnotation('int');
     expect(decoratedType.namedParameters['y'], same(decoratedIntType));
     expect(decoratedIntType.node, isNotNull);
-    expect(decoratedIntType.node, isNot(NullabilityNode.never));
+    expect(decoratedIntType.node, isNot(never));
   }
 
   test_genericFunctionType_returnType() async {
@@ -1315,7 +1310,7 @@
     var decoratedIntType = decoratedTypeAnnotation('int');
     expect(decoratedType.returnType, same(decoratedIntType));
     expect(decoratedIntType.node, isNotNull);
-    expect(decoratedIntType.node, isNot(NullabilityNode.never));
+    expect(decoratedIntType.node, isNot(never));
   }
 
   test_genericFunctionType_unnamedParameterType() async {
@@ -1330,7 +1325,7 @@
     var decoratedIntType = decoratedTypeAnnotation('int');
     expect(decoratedType.positionalParameters[0], same(decoratedIntType));
     expect(decoratedIntType.node, isNotNull);
-    expect(decoratedIntType.node, isNot(NullabilityNode.never));
+    expect(decoratedIntType.node, isNot(never));
   }
 
   test_interfaceType_typeParameter() async {
@@ -1341,11 +1336,11 @@
     expect(decoratedFunctionType('f').positionalParameters[0],
         same(decoratedListType));
     expect(decoratedListType.node, isNotNull);
-    expect(decoratedListType.node, isNot(NullabilityNode.never));
+    expect(decoratedListType.node, isNot(never));
     var decoratedIntType = decoratedTypeAnnotation('int');
     expect(decoratedListType.typeArguments[0], same(decoratedIntType));
     expect(decoratedIntType.node, isNotNull);
-    expect(decoratedIntType.node, isNot(NullabilityNode.never));
+    expect(decoratedIntType.node, isNot(never));
   }
 
   test_topLevelFunction_parameterType_implicit_dynamic() async {
@@ -1368,8 +1363,8 @@
     var functionType = decoratedFunctionType('f');
     expect(functionType.namedParameters['s'], same(decoratedType));
     expect(decoratedType.node, isNotNull);
-    expect(decoratedType.node, isNot(NullabilityNode.never));
-    expect(decoratedType.node, isNot(NullabilityNode.always));
+    expect(decoratedType.node, isNot(never));
+    expect(decoratedType.node, isNot(always));
     expect(functionType.namedParameters['s'].node.isPossiblyOptional, true);
   }
 
@@ -1383,8 +1378,8 @@
     var functionType = decoratedFunctionType('f');
     expect(functionType.namedParameters['s'], same(decoratedType));
     expect(decoratedType.node, isNotNull);
-    expect(decoratedType.node, isNot(NullabilityNode.never));
-    expect(decoratedType.node, isNot(NullabilityNode.always));
+    expect(decoratedType.node, isNot(never));
+    expect(decoratedType.node, isNot(always));
     expect(functionType.namedParameters['s'].node.isPossiblyOptional, false);
   }
 
@@ -1396,7 +1391,7 @@
     var functionType = decoratedFunctionType('f');
     expect(functionType.namedParameters['s'], same(decoratedType));
     expect(decoratedType.node, isNotNull);
-    expect(decoratedType.node, isNot(NullabilityNode.never));
+    expect(decoratedType.node, isNot(never));
     expect(functionType.namedParameters['s'].node.isPossiblyOptional, false);
   }
 
@@ -1408,7 +1403,7 @@
     expect(decoratedFunctionType('f').positionalParameters[0],
         same(decoratedType));
     expect(decoratedType.node, isNotNull);
-    expect(decoratedType.node, isNot(NullabilityNode.never));
+    expect(decoratedType.node, isNot(never));
   }
 
   test_topLevelFunction_parameterType_simple() async {
@@ -1419,7 +1414,7 @@
     expect(decoratedFunctionType('f').positionalParameters[0],
         same(decoratedType));
     expect(decoratedType.node, isNotNull);
-    expect(decoratedType.node, isNot(NullabilityNode.never));
+    expect(decoratedType.node, isNot(never));
   }
 
   test_topLevelFunction_returnType_implicit_dynamic() async {
@@ -1438,7 +1433,7 @@
     var decoratedType = decoratedTypeAnnotation('int');
     expect(decoratedFunctionType('f').returnType, same(decoratedType));
     expect(decoratedType.node, isNotNull);
-    expect(decoratedType.node, isNot(NullabilityNode.never));
+    expect(decoratedType.node, isNot(never));
   }
 
   test_type_comment_bang() async {
@@ -1461,7 +1456,7 @@
 ''');
     var bound = decoratedTypeParameterBound('T');
     expect(decoratedTypeAnnotation('Object'), same(bound));
-    expect(bound.node, isNot(NullabilityNode.always));
+    expect(bound.node, isNot(always));
     expect(bound.type, typeProvider.objectType);
   }
 
@@ -1507,6 +1502,8 @@
 
   final _possiblyOptional = <DefaultFormalParameter, NullabilityNode>{};
 
+  _Variables(NullabilityGraph graph) : super(graph);
+
   /// Gets the [ExpressionChecks] associated with the given [expression].
   ExpressionChecks checkExpression(Expression expression) =>
       _expressionChecks[_normalizeExpression(expression)];
diff --git a/pkg/nnbd_migration/test/nullability_node_test.dart b/pkg/nnbd_migration/test/nullability_node_test.dart
index 57f07e6..e3ce617 100644
--- a/pkg/nnbd_migration/test/nullability_node_test.dart
+++ b/pkg/nnbd_migration/test/nullability_node_test.dart
@@ -16,9 +16,9 @@
 class NullabilityNodeTest {
   final graph = NullabilityGraph();
 
-  NullabilityNode get always => NullabilityNode.always;
+  NullabilityNode get always => graph.always;
 
-  NullabilityNode get never => NullabilityNode.never;
+  NullabilityNode get never => graph.never;
 
   void connect(NullabilityNode source, NullabilityNode destination,
       {bool hard = false, List<NullabilityNode> guards = const []}) {