Files
sdk/pkg/nnbd_migration/test/nullability_node_test.dart
T
Paul Berry 3f83aeb493 Migration: add the ability to "union" nullability nodes.
This will simplify some situations where nullability migration would
otherwise have to visit the source code in a very careful order.  For
example, when analyzing top level fields undergoing type inference, we
can simply create a node for the nullability of each top level field,
and then later use the "union" operation to hook up those nodes to the
nodes resulting from analyzing their initializers.  Without the union
operation, we would have to be careful to visit the top level fields
in dependency order.

Change-Id: I3b07e1abccc5b1c9f1c7c9fa7b13fb6af60c07d6
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/105800
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
2019-06-12 20:10:24 +00:00

435 lines
11 KiB
Dart

// 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:nnbd_migration/src/nullability_node.dart';
import 'package:test/test.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
main() {
defineReflectiveSuite(() {
defineReflectiveTests(NullabilityNodeTest);
});
}
@reflectiveTest
class NullabilityNodeTest {
final graph = NullabilityGraph();
NullabilityNode get always => graph.always;
NullabilityNode get never => graph.never;
void connect(NullabilityNode source, NullabilityNode destination,
{bool hard = false, List<NullabilityNode> guards = const []}) {
graph.connect(source, destination, hard: hard, guards: guards);
}
NullabilityNode lub(NullabilityNode left, NullabilityNode right) {
return NullabilityNode.forLUB(left, right);
}
NullabilityNode newNode(int offset) =>
NullabilityNode.forTypeAnnotation(offset);
NullabilityNode subst(NullabilityNode inner, NullabilityNode outer) {
return NullabilityNode.forSubstitution(inner, outer);
}
test_always_and_never_state() {
graph.propagate();
expect(always.isNullable, isTrue);
expect(never.isNullable, isFalse);
}
test_always_and_never_unaffected_by_hard_edges() {
connect(always, never, hard: true);
graph.propagate();
expect(always.isNullable, isTrue);
expect(never.isNullable, isFalse);
}
test_always_and_never_unaffected_by_soft_edges() {
connect(always, never);
graph.propagate();
expect(always.isNullable, isTrue);
expect(never.isNullable, isFalse);
}
test_always_destination() {
// always -> 1 -(hard)-> always
var n1 = newNode(1);
connect(always, n1);
connect(n1, always, hard: true);
graph.propagate();
// Upstream propagation of non-nullability ignores edges terminating at
// `always`, so n1 should be nullable.
expect(n1.isNullable, true);
}
test_never_source() {
// never -> 1
var n1 = newNode(1);
connect(never, n1);
graph.propagate();
// Downstream propagation of nullability ignores edges originating at
// `never`, so n1 should be non-nullable.
expect(n1.isNullable, false);
}
test_propagation_always_union() {
// always == 1
// 1 -(hard)-> never
// 1 -> 2
// 1 -> 3
// 3 -(hard)-> never
var n1 = newNode(1);
var n2 = newNode(2);
var n3 = newNode(3);
union(always, n1);
connect(n1, never, hard: true);
connect(n1, n2);
connect(n1, n3);
connect(n3, never, hard: true);
graph.propagate();
// Union edges take precedence over hard ones, so n1 should be nullable.
expect(n1.isNullable, true);
// And nullability should be propagated to n2.
expect(n2.isNullable, true);
// But it should not be propagated to n3 because non-nullability propagation
// takes precedence over ordinary nullability propagation.
expect(n3.isNullable, false);
}
test_propagation_always_union_reversed() {
// always == 1
// 1 -(hard)-> never
// 1 -> 2
// 1 -> 3
// 3 -(hard)-> never
var n1 = newNode(1);
var n2 = newNode(2);
var n3 = newNode(3);
union(n1, always);
connect(n1, never, hard: true);
connect(n1, n2);
connect(n1, n3);
connect(n3, never, hard: true);
graph.propagate();
// Union edges take precedence over hard ones, so n1 should be nullable.
expect(n1.isNullable, true);
// And nullability should be propagated to n2.
expect(n2.isNullable, true);
// But it should not be propagated to n3 because non-nullability propagation
// takes precedence over ordinary nullability propagation.
expect(n3.isNullable, false);
}
test_propagation_downstream_guarded_multiple_guards_all_satisfied() {
// always -> 1
// always -> 2
// always -> 3
// 1 -(2,3)-> 4
var n1 = newNode(1);
var n2 = newNode(2);
var n3 = newNode(3);
var n4 = newNode(3);
connect(always, n1);
connect(always, n2);
connect(always, n3);
connect(n1, n4, guards: [n2, n3]);
graph.propagate();
expect(n4.isNullable, true);
}
test_propagation_downstream_guarded_multiple_guards_not_all_satisfied() {
// always -> 1
// always -> 2
// 1 -(2,3)-> 4
var n1 = newNode(1);
var n2 = newNode(2);
var n3 = newNode(3);
var n4 = newNode(3);
connect(always, n1);
connect(always, n2);
connect(n1, n4, guards: [n2, n3]);
graph.propagate();
expect(n4.isNullable, false);
}
test_propagation_downstream_guarded_satisfy_guard_first() {
// always -> 1
// always -> 2
// 2 -(1)-> 3
var n1 = newNode(1);
var n2 = newNode(2);
var n3 = newNode(3);
connect(always, n1);
connect(always, n2);
connect(n2, n3, guards: [n1]);
graph.propagate();
expect(n3.isNullable, true);
}
test_propagation_downstream_guarded_satisfy_source_first() {
// always -> 1
// always -> 2
// 1 -(2)-> 3
var n1 = newNode(1);
var n2 = newNode(2);
var n3 = newNode(3);
connect(always, n1);
connect(always, n2);
connect(n1, n3, guards: [n2]);
graph.propagate();
expect(n3.isNullable, true);
}
test_propagation_downstream_guarded_unsatisfied_guard() {
// always -> 1
// 1 -(2)-> 3
var n1 = newNode(1);
var n2 = newNode(2);
var n3 = newNode(3);
connect(always, n1);
connect(n1, n3, guards: [n2]);
graph.propagate();
expect(n3.isNullable, false);
}
test_propagation_downstream_guarded_unsatisfied_source() {
// always -> 1
// 2 -(1)-> 3
var n1 = newNode(1);
var n2 = newNode(2);
var n3 = newNode(3);
connect(always, n1);
connect(n2, n3, guards: [n1]);
graph.propagate();
expect(n3.isNullable, false);
}
test_propagation_downstream_through_lub_both() {
// always -> 1
// always -> 2
// LUB(1, 2) -> 3
var n1 = newNode(1);
var n2 = newNode(2);
var n3 = newNode(3);
connect(always, n1);
connect(always, n2);
connect(lub(n1, n2), n3);
graph.propagate();
expect(n3.isNullable, true);
}
test_propagation_downstream_through_lub_cascaded() {
// always -> 1
// LUB(LUB(1, 2), 3) -> 4
var n1 = newNode(1);
var n2 = newNode(2);
var n3 = newNode(3);
var n4 = newNode(3);
connect(always, n1);
connect(lub(lub(n1, n2), n3), n4);
graph.propagate();
expect(n4.isNullable, true);
}
test_propagation_downstream_through_lub_left() {
// always -> 1
// LUB(1, 2) -> 3
var n1 = newNode(1);
var n2 = newNode(2);
var n3 = newNode(3);
connect(always, n1);
connect(lub(n1, n2), n3);
graph.propagate();
expect(n3.isNullable, true);
}
test_propagation_downstream_through_lub_neither() {
// LUB(1, 2) -> 3
var n1 = newNode(1);
var n2 = newNode(2);
var n3 = newNode(3);
connect(lub(n1, n2), n3);
graph.propagate();
expect(n3.isNullable, false);
}
test_propagation_downstream_through_lub_right() {
// always -> 2
// LUB(1, 2) -> 3
var n1 = newNode(1);
var n2 = newNode(2);
var n3 = newNode(3);
connect(always, n2);
connect(lub(n1, n2), n3);
graph.propagate();
expect(n3.isNullable, true);
}
test_propagation_downstream_through_substitution_both() {
// always -> 1
// always -> 2
// subst(1, 2) -> 3
var n1 = newNode(1);
var n2 = newNode(2);
var n3 = newNode(3);
connect(always, n1);
connect(always, n2);
connect(subst(n1, n2), n3);
graph.propagate();
expect(n3.isNullable, true);
}
test_propagation_downstream_through_substitution_cascaded() {
// always -> 1
// LUB(LUB(1, 2), 3) -> 4
var n1 = newNode(1);
var n2 = newNode(2);
var n3 = newNode(3);
var n4 = newNode(3);
connect(always, n1);
connect(subst(subst(n1, n2), n3), n4);
graph.propagate();
expect(n4.isNullable, true);
}
test_propagation_downstream_through_substitution_inner() {
// always -> 1
// LUB(1, 2) -> 3
var n1 = newNode(1);
var n2 = newNode(2);
var n3 = newNode(3);
connect(always, n1);
connect(subst(n1, n2), n3);
graph.propagate();
expect(n3.isNullable, true);
}
test_propagation_downstream_through_substitution_neither() {
// LUB(1, 2) -> 3
var n1 = newNode(1);
var n2 = newNode(2);
var n3 = newNode(3);
connect(subst(n1, n2), n3);
graph.propagate();
expect(n3.isNullable, false);
}
test_propagation_downstream_through_substitution_outer() {
// always -> 2
// LUB(1, 2) -> 3
var n1 = newNode(1);
var n2 = newNode(2);
var n3 = newNode(3);
connect(always, n2);
connect(subst(n1, n2), n3);
graph.propagate();
expect(n3.isNullable, true);
}
test_propagation_downstream_through_union() {
// always -> 1
// 1 == 2
// 2 -> 3
var n1 = newNode(1);
var n2 = newNode(2);
var n3 = newNode(3);
connect(always, n1);
union(n1, n2);
connect(n2, n3);
graph.propagate();
expect(n1.isNullable, true);
expect(n2.isNullable, true);
expect(n3.isNullable, true);
}
test_propagation_downstream_through_union_reversed() {
// always -> 1
// 2 == 1
// 2 -> 3
var n1 = newNode(1);
var n2 = newNode(2);
var n3 = newNode(3);
connect(always, n1);
union(n2, n1);
connect(n2, n3);
graph.propagate();
expect(n1.isNullable, true);
expect(n2.isNullable, true);
expect(n3.isNullable, true);
}
test_propagation_simple() {
// always -(soft)-> 1 -(soft)-> 2 -(hard) -> 3 -(hard)-> never
var n1 = newNode(1);
var n2 = newNode(2);
var n3 = newNode(3);
connect(always, n1);
connect(n1, n2);
connect(n2, n3, hard: true);
connect(n3, never, hard: true);
graph.propagate();
expect(n1.isNullable, true);
expect(n2.isNullable, false);
expect(n3.isNullable, false);
}
test_propagation_upstream_through_union() {
// always -> 1
// always -> 2
// always -> 3
// 1 -(hard)-> 2
// 2 == 3
// 3 -(hard)-> never
var n1 = newNode(1);
var n2 = newNode(2);
var n3 = newNode(3);
connect(always, n1);
connect(always, n2);
connect(always, n3);
connect(n1, n2, hard: true);
union(n2, n3);
connect(n3, never, hard: true);
graph.propagate();
expect(n1.isNullable, false);
expect(n2.isNullable, false);
expect(n3.isNullable, false);
}
test_propagation_upstream_through_union_reversed() {
// always -> 1
// always -> 2
// always -> 3
// 1 -(hard)-> 2
// 3 == 2
// 3 -(hard)-> never
var n1 = newNode(1);
var n2 = newNode(2);
var n3 = newNode(3);
connect(always, n1);
connect(always, n2);
connect(always, n3);
connect(n1, n2, hard: true);
union(n3, n2);
connect(n3, never, hard: true);
graph.propagate();
expect(n1.isNullable, false);
expect(n2.isNullable, false);
expect(n3.isNullable, false);
}
test_unconstrainted_node_non_nullable() {
var n1 = newNode(1);
graph.propagate();
expect(n1.isNullable, false);
}
void union(NullabilityNode x, NullabilityNode y) {
graph.union(x, y);
}
}