Rework some migration tests to make use of the nullability graph.
This required tracking the map from a node to the nodes that are
upstream from it.
Change-Id: I079fac2196f3928b521b0866fd6f1790e6f24573
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/101489
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
diff --git a/pkg/analysis_server/lib/src/nullability/nullability_graph.dart b/pkg/analysis_server/lib/src/nullability/nullability_graph.dart
index f1c9d45..bcc2300 100644
--- a/pkg/analysis_server/lib/src/nullability/nullability_graph.dart
+++ b/pkg/analysis_server/lib/src/nullability/nullability_graph.dart
@@ -13,9 +13,15 @@
/// have to be added).
final _downstream = Map<NullabilityNode, List<NullabilityNode>>.identity();
+ /// Map from a nullability node to those nodes that are "upstream" from it
+ /// (meaning that if a node in the value is nullable, then the corresponding
+ /// key node will have to be nullable, or null checks will have to be added).
+ final _upstream = Map<NullabilityNode, List<NullabilityNode>>.identity();
+
/// Records that [sourceNode] is immediately upstream from [destinationNode].
void connect(NullabilityNode sourceNode, NullabilityNode destinationNode) {
(_downstream[sourceNode] ??= []).add(destinationNode);
+ (_upstream[destinationNode] ??= []).add(sourceNode);
}
/// Iterates through all nodes that are "downstream" of [node] (i.e. if
@@ -25,4 +31,12 @@
/// There is no guarantee of uniqueness of the iterated nodes.
Iterable<NullabilityNode> getDownstreamNodes(NullabilityNode node) =>
_downstream[node] ?? const [];
+
+ /// Iterates through all nodes that are "upstream" of [node] (i.e. if
+ /// any of the iterated nodes are nullable, then [node] will either have to be
+ /// nullable, or null checks will have to be added).
+ // ///
+ // /// There is no guarantee of uniqueness of the iterated nodes.
+ Iterable<NullabilityNode> getUpstreamNodes(NullabilityNode node) =>
+ _upstream[node] ?? const [];
}
diff --git a/pkg/analysis_server/test/src/nullability/migration_visitor_test.dart b/pkg/analysis_server/test/src/nullability/migration_visitor_test.dart
index 5501916..61c133d 100644
--- a/pkg/analysis_server/test/src/nullability/migration_visitor_test.dart
+++ b/pkg/analysis_server/test/src/nullability/migration_visitor_test.dart
@@ -81,6 +81,14 @@
}
}
+ /// Checks that there are no nullability nodes upstream from [node] that could
+ /// cause it to become nullable.
+ void assertNoUpstreamNullability(NullabilityNode node) {
+ for (var upstreamNode in graph.getUpstreamNodes(node)) {
+ expect(upstreamNode, NullabilityNode.never);
+ }
+ }
+
/// Verifies that a null check will occur under the proper circumstances.
///
/// [expressionChecks] is the object tracking whether or not a null check is
@@ -175,7 +183,7 @@
int f(int i, int j) => i + j;
''');
- assertNoConstraints(decoratedTypeAnnotation('int f').node.nullable);
+ assertNoUpstreamNullability(decoratedTypeAnnotation('int f').node);
}
test_binaryExpression_add_right_check() async {
@@ -205,7 +213,7 @@
bool f(int i, int j) => i == j;
''');
- assertNoConstraints(decoratedTypeAnnotation('bool f').node.nullable);
+ assertNoUpstreamNullability(decoratedTypeAnnotation('bool f').node);
}
test_boolLiteral() async {
@@ -214,7 +222,7 @@
return true;
}
''');
- assertNoConstraints(decoratedTypeAnnotation('bool').node.nullable);
+ assertNoUpstreamNullability(decoratedTypeAnnotation('bool').node);
}
test_conditionalExpression_condition_check() async {
@@ -312,7 +320,7 @@
void f({int i = 1}) {}
''');
- assertNoConstraints(decoratedTypeAnnotation('int').node.nullable);
+ assertNoUpstreamNullability(decoratedTypeAnnotation('int').node);
}
test_functionDeclaration_parameter_named_default_null() async {
@@ -344,7 +352,7 @@
namedNoDefaultParameterHeuristic:
NamedNoDefaultParameterHeuristic.assumeRequired));
- assertNoConstraints(decoratedTypeAnnotation('int').node.nullable);
+ assertNoUpstreamNullability(decoratedTypeAnnotation('int').node);
}
test_functionDeclaration_parameter_named_no_default_required_assume_nullable() async {
@@ -357,7 +365,7 @@
namedNoDefaultParameterHeuristic:
NamedNoDefaultParameterHeuristic.assumeNullable));
- assertNoConstraints(decoratedTypeAnnotation('int').node.nullable);
+ assertNoUpstreamNullability(decoratedTypeAnnotation('int').node);
}
test_functionDeclaration_parameter_named_no_default_required_assume_required() async {
@@ -370,7 +378,7 @@
namedNoDefaultParameterHeuristic:
NamedNoDefaultParameterHeuristic.assumeRequired));
- assertNoConstraints(decoratedTypeAnnotation('int').node.nullable);
+ assertNoUpstreamNullability(decoratedTypeAnnotation('int').node);
}
test_functionDeclaration_parameter_positionalOptional_default_notNull() async {
@@ -378,7 +386,7 @@
void f([int i = 1]) {}
''');
- assertNoConstraints(decoratedTypeAnnotation('int').node.nullable);
+ assertNoUpstreamNullability(decoratedTypeAnnotation('int').node);
}
test_functionDeclaration_parameter_positionalOptional_default_null() async {
@@ -481,8 +489,8 @@
// The call at `f()` is presumed to be in error; no constraint is recorded.
var optional_i = possiblyOptionalParameter('int i');
expect(optional_i, isNull);
- var nullable_i = decoratedTypeAnnotation('int i').node.nullable;
- assertNoConstraints(nullable_i);
+ var nullable_i = decoratedTypeAnnotation('int i').node;
+ assertNoUpstreamNullability(nullable_i);
}
test_functionInvocation_parameter_null() async {
@@ -615,7 +623,7 @@
return 0;
}
''');
- assertNoConstraints(decoratedTypeAnnotation('int').node.nullable);
+ assertNoUpstreamNullability(decoratedTypeAnnotation('int').node);
}
test_methodDeclaration_resets_unconditional_control_flow() async {
@@ -757,7 +765,7 @@
return 'x';
}
''');
- assertNoConstraints(decoratedTypeAnnotation('String').node.nullable);
+ assertNoUpstreamNullability(decoratedTypeAnnotation('String').node);
}
test_thisExpression() async {
@@ -767,7 +775,7 @@
}
''');
- assertNoConstraints(decoratedTypeAnnotation('C f').node.nullable);
+ assertNoUpstreamNullability(decoratedTypeAnnotation('C f').node);
}
test_throwExpression() async {
@@ -776,7 +784,7 @@
return throw null;
}
''');
- assertNoConstraints(decoratedTypeAnnotation('int').node.nullable);
+ assertNoUpstreamNullability(decoratedTypeAnnotation('int').node);
}
test_typeName() async {
@@ -785,7 +793,7 @@
return int;
}
''');
- assertNoConstraints(decoratedTypeAnnotation('Type').node.nullable);
+ assertNoUpstreamNullability(decoratedTypeAnnotation('Type').node);
}
}