Migration: Allow edges to be queried to see if they're satisfied.
Change-Id: I9da0233dfbac0c7aee168622c554d003c2c5e16c
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/106382
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
diff --git a/pkg/nnbd_migration/lib/src/nullability_node.dart b/pkg/nnbd_migration/lib/src/nullability_node.dart
index a580f92..d71920e 100644
--- a/pkg/nnbd_migration/lib/src/nullability_node.dart
+++ b/pkg/nnbd_migration/lib/src/nullability_node.dart
@@ -33,10 +33,27 @@
bool get hard => _kind != _NullabilityEdgeKind.soft;
+ /// Indicates whether nullability was successfully propagated through this
+ /// edge.
+ bool get isSatisfied {
+ if (!_isTriggered) return true;
+ return destinationNode.isNullable;
+ }
+
bool get isUnion => _kind == _NullabilityEdgeKind.union;
NullabilityNode get primarySource => sources.first;
+ /// Indicates whether all the sources of this edge are nullable (and thus
+ /// downstream nullability propagation should try to make the destination node
+ /// nullable, if possible).
+ bool get _isTriggered {
+ for (var source in sources) {
+ if (!source.isNullable) return false;
+ }
+ return true;
+ }
+
@override
String toString() {
var edgeDecorations = <Object>[];
@@ -183,16 +200,10 @@
}
var pendingSubstitutions = <NullabilityNodeForSubstitution>[];
while (true) {
- nextEdge:
while (pendingEdges.isNotEmpty) {
var edge = pendingEdges.removeLast();
+ if (!edge._isTriggered) continue;
var node = edge.destinationNode;
- for (var source in edge.sources) {
- if (!source.isNullable) {
- // Not all sources are nullable, so this edge doesn't apply yet.
- continue nextEdge;
- }
- }
if (node._state == _NullabilityState.nonNullable) {
// The node has already been marked as non-nullable, so the edge can't
// be satisfied.
diff --git a/pkg/nnbd_migration/test/nullability_node_test.dart b/pkg/nnbd_migration/test/nullability_node_test.dart
index 3687874..9cc8d36 100644
--- a/pkg/nnbd_migration/test/nullability_node_test.dart
+++ b/pkg/nnbd_migration/test/nullability_node_test.dart
@@ -481,6 +481,43 @@
assertUnsatisfied([edge_always_1, edge_always_2, edge_always_3]);
}
+ test_satisfied_edge_destination_nullable() {
+ var n1 = newNode(1);
+ var edge = connect(always, n1);
+ propagate();
+ assertUnsatisfied([]);
+ expect(edge.isSatisfied, true);
+ }
+
+ test_satisfied_edge_source_non_nullable() {
+ var n1 = newNode(1);
+ var n2 = newNode(1);
+ var edge = connect(n1, n2);
+ propagate();
+ assertUnsatisfied([]);
+ expect(edge.isSatisfied, true);
+ }
+
+ test_satisfied_edge_two_sources_first_non_nullable() {
+ var n1 = newNode(1);
+ var n2 = newNode(1);
+ connect(always, n2);
+ var edge = connect(n1, never, guards: [n2]);
+ propagate();
+ assertUnsatisfied([]);
+ expect(edge.isSatisfied, true);
+ }
+
+ test_satisfied_edge_two_sources_second_non_nullable() {
+ var n1 = newNode(1);
+ var n2 = newNode(1);
+ connect(always, n1);
+ var edge = connect(n1, never, guards: [n2]);
+ propagate();
+ assertUnsatisfied([]);
+ expect(edge.isSatisfied, true);
+ }
+
test_unconstrainted_node_non_nullable() {
var n1 = newNode(1);
propagate();
@@ -488,6 +525,22 @@
assertUnsatisfied([]);
}
+ test_unsatisfied_edge_multiple_sources() {
+ var n1 = newNode(1);
+ connect(always, n1);
+ var edge = connect(always, never, guards: [n1]);
+ propagate();
+ assertUnsatisfied([edge]);
+ expect(edge.isSatisfied, false);
+ }
+
+ test_unsatisfied_edge_single_source() {
+ var edge = connect(always, never);
+ propagate();
+ assertUnsatisfied([edge]);
+ expect(edge.isSatisfied, false);
+ }
+
void union(NullabilityNode x, NullabilityNode y) {
graph.union(x, y, _TestEdgeOrigin());
}