blob: df1b6c62a89084eacccbb5353ac9ab90fc5826cd [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:test_reflective_loader/test_reflective_loader.dart';
import 'migration_visitor_test_base.dart';
main() {
defineReflectiveSuite(() {
defineReflectiveTests(EdgeBuilderFlowAnalysisTest);
});
}
@reflectiveTest
class EdgeBuilderFlowAnalysisTest extends EdgeBuilderTestBase {
test_assignmentExpression() async {
await analyze('''
void f(int i, int j) {
if (i != null) {
g(i);
i = j;
h(i);
}
}
void g(int k) {}
void h(int l) {}
''');
var iNode = decoratedTypeAnnotation('int i').node;
var jNode = decoratedTypeAnnotation('int j').node;
var kNode = decoratedTypeAnnotation('int k').node;
var lNode = decoratedTypeAnnotation('int l').node;
// No edge from i to k because i's type is promoted to non-nullable
assertNoEdge(iNode, kNode);
// But there is an edge from i to l, because it is after the assignment
assertEdge(iNode, lNode, hard: false);
// And there is an edge from j to i, because a null value of j would lead to
// a null value for i.
assertEdge(jNode, iNode, hard: false);
}
test_assignmentExpression_lhs_before_rhs() async {
await analyze('''
void f(int i, List<int> l) {
if (i != null) {
l[i = g(i)] = h(i);
}
}
int g(int j) => 1;
int h(int k) => 1;
''');
var iNode = decoratedTypeAnnotation('int i').node;
var jNode = decoratedTypeAnnotation('int j').node;
var kNode = decoratedTypeAnnotation('int k').node;
var gReturnNode = decoratedTypeAnnotation('int g').node;
// No edge from i to j, because i's type is promoted before the call to g.
assertNoEdge(iNode, jNode);
// But there is an edge from i to k, because the call to h happens after the
// assignment.
assertEdge(iNode, kNode, hard: false);
// And there is an edge from g's return type to i, due to the assignment.
assertEdge(gReturnNode, iNode, hard: false);
}
test_assignmentExpression_write_after_rhs() async {
await analyze('''
void f(int i) {
if (i != null) {
i = g(i);
h(i);
}
}
int g(int j) => 1;
void h(int k) {}
''');
var iNode = decoratedTypeAnnotation('int i').node;
var jNode = decoratedTypeAnnotation('int j').node;
var kNode = decoratedTypeAnnotation('int k').node;
var gReturnNode = decoratedTypeAnnotation('int g').node;
// No edge from i to j because i's type is promoted before the call to g.
assertNoEdge(iNode, jNode);
// But there is an edge from i to k, because the call to h happens after the
// assignment.
assertEdge(iNode, kNode, hard: false);
// And there is an edge from g's return type to i, due to the assignment.
assertEdge(gReturnNode, iNode, hard: false);
}
test_binaryExpression_ampersandAmpersand_left() async {
await analyze('''
bool f(int i) => i != null && i.isEven;
bool g(int j) => j.isEven;
''');
var iNode = decoratedTypeAnnotation('int i').node;
var jNode = decoratedTypeAnnotation('int j').node;
// No edge from i to `never` because i's type is promoted to non-nullable
assertNoEdge(iNode, never);
// But there is an edge from j to `never`.
assertEdge(jNode, never, hard: true);
}
test_binaryExpression_ampersandAmpersand_right() async {
await analyze('''
void f(bool b, int i, int j) {
if (b && i != null) {
print(i.isEven);
print(j.isEven);
}
}
''');
var iNode = decoratedTypeAnnotation('int i').node;
var jNode = decoratedTypeAnnotation('int j').node;
// No edge from i to `never` because i's type is promoted to non-nullable
assertNoEdge(iNode, never);
// But there is an edge from j to `never`.
assertEdge(jNode, never, hard: false);
}
test_binaryExpression_barBar_left() async {
await analyze('''
bool f(int i) => i == null || i.isEven;
bool g(int j) => j.isEven;
''');
var iNode = decoratedTypeAnnotation('int i').node;
var jNode = decoratedTypeAnnotation('int j').node;
// No edge from i to `never` because i's type is promoted to non-nullable
assertNoEdge(iNode, never);
// But there is an edge from j to `never`.
assertEdge(jNode, never, hard: true);
}
test_binaryExpression_barBar_right() async {
await analyze('''
void f(bool b, int i, int j) {
if (b || i == null) {} else {
print(i.isEven);
print(j.isEven);
}
}
''');
var iNode = decoratedTypeAnnotation('int i').node;
var jNode = decoratedTypeAnnotation('int j').node;
// No edge from i to `never` because i's type is promoted to non-nullable
assertNoEdge(iNode, never);
// But there is an edge from j to `never`.
assertEdge(jNode, never, hard: false);
}
test_booleanLiteral_false() async {
await analyze('''
void f(int i, int j) {
if (i != null || false) {} else return;
if (j != null || true) {} else return;
i.isEven;
j.isEven;
}
''');
var iNode = decoratedTypeAnnotation('int i').node;
var jNode = decoratedTypeAnnotation('int j').node;
// No edge from i to never i is known to be non-nullable at the site of
// the call to i.isEven
assertNoEdge(iNode, never);
// But there is an edge from j to never
assertEdge(jNode, never, hard: false);
}
test_booleanLiteral_true() async {
await analyze('''
void f(int i, int j) {
if (i == null && true) return;
if (j == null && false) return;
i.isEven;
j.isEven;
}
''');
var iNode = decoratedTypeAnnotation('int i').node;
var jNode = decoratedTypeAnnotation('int j').node;
// No edge from i to never i is known to be non-nullable at the site of
// the call to i.isEven
assertNoEdge(iNode, never);
// But there is an edge from j to never
assertEdge(jNode, never, hard: false);
}
test_break_labeled() async {
await analyze('''
void f(int i) {
L: while(true) {
while (b()) {
if (i != null) break L;
}
g(i);
}
h(i);
}
bool b() => true;
void g(int j) {}
void h(int k) {}
''');
var iNode = decoratedTypeAnnotation('int i').node;
var jNode = decoratedTypeAnnotation('int j').node;
var kNode = decoratedTypeAnnotation('int k').node;
// No edge from i to k because i is promoted at the time of the call to h.
assertNoEdge(iNode, kNode);
// But there is an edge from i to j, because i is not promoted at the time
// of the call to g.
assertEdge(iNode, jNode, hard: false);
}
test_break_unlabeled() async {
await analyze('''
void f(int i) {
while (true) {
if (i != null) break;
g(i);
}
h(i);
}
bool b() => true;
void g(int j) {}
void h(int k) {}
''');
var iNode = decoratedTypeAnnotation('int i').node;
var jNode = decoratedTypeAnnotation('int j').node;
var kNode = decoratedTypeAnnotation('int k').node;
// No edge from i to k because i is promoted at the time of the call to h.
assertNoEdge(iNode, kNode);
// But there is an edge from i to j, because i is not promoted at the time
// of the call to g.
assertEdge(iNode, jNode, hard: false);
}
test_conditionalExpression() async {
await analyze('''
int f(int i) => i == null ? g(i) : h(i);
int g(int j) => 1;
int h(int k) => 1;
''');
var iNode = decoratedTypeAnnotation('int i').node;
var jNode = decoratedTypeAnnotation('int j').node;
var kNode = decoratedTypeAnnotation('int k').node;
// No edge from i to k because i is known to be non-nullable at the site of
// the call to h()
assertNoEdge(iNode, kNode);
// But there is an edge from i to j
// TODO(paulberry): there should be a guard on this edge.
assertEdge(iNode, jNode, hard: false);
}
test_conditionalExpression_propagates_promotions() async {
await analyze('''
void f(bool b, int i, int j, int k) {
if (b ? (i != null && j != null) : (i != null && k != null)) {
i.isEven;
j.isEven;
k.isEven;
}
}
''');
var iNode = decoratedTypeAnnotation('int i').node;
var jNode = decoratedTypeAnnotation('int j').node;
var kNode = decoratedTypeAnnotation('int k').node;
// No edge from i to never because i is promoted.
assertNoEdge(iNode, never);
// But there are edges from j and k to never.
assertEdge(jNode, never, hard: false);
assertEdge(kNode, never, hard: false);
}
test_constructorDeclaration_assert() async {
await analyze('''
class C {
C(int i, int j) : assert(i == null || i.isEven, j.isEven);
}
''');
var iNode = decoratedTypeAnnotation('int i').node;
var jNode = decoratedTypeAnnotation('int j').node;
// No edge from i to `never` because i's type is promoted to non-nullable
assertNoEdge(iNode, never);
// But there is an edge from j to `never`.
assertEdge(jNode, never, hard: true);
}
test_constructorDeclaration_initializer() async {
await analyze('''
class C {
bool b1;
bool b2;
C(int i, int j) : b1 = i == null || i.isEven, b2 = j.isEven;
}
''');
var iNode = decoratedTypeAnnotation('int i').node;
var jNode = decoratedTypeAnnotation('int j').node;
// No edge from i to `never` because i's type is promoted to non-nullable
assertNoEdge(iNode, never);
// But there is an edge from j to `never`.
assertEdge(jNode, never, hard: true);
}
test_constructorDeclaration_redirection() async {
await analyze('''
class C {
C(bool b1, bool b2);
C.redirect(int i, int j) : this(i == null || i.isEven, j.isEven);
}
''');
var iNode = decoratedTypeAnnotation('int i').node;
var jNode = decoratedTypeAnnotation('int j').node;
// No edge from i to `never` because i's type is promoted to non-nullable
assertNoEdge(iNode, never);
// But there is an edge from j to `never`.
assertEdge(jNode, never, hard: true);
}
test_continue_labeled() async {
await analyze('''
void f(int i) {
L: do {
do {
if (i != null) continue L;
} while (g(i));
break;
} while (h(i));
}
bool g(int j) => true;
bool h(int k) => true;
''');
var iNode = decoratedTypeAnnotation('int i').node;
var jNode = decoratedTypeAnnotation('int j').node;
var kNode = decoratedTypeAnnotation('int k').node;
// No edge from i to k because i is promoted at the time of the call to h.
assertNoEdge(iNode, kNode);
// But there is an edge from i to j, because i is not promoted at the time
// of the call to g.
assertEdge(iNode, jNode, hard: false);
}
test_continue_unlabeled() async {
await analyze('''
void f(int i) {
do {
if (i != null) continue;
h(i);
break;
} while (g(i));
}
bool g(int j) => true;
void h(int k) {}
''');
var iNode = decoratedTypeAnnotation('int i').node;
var jNode = decoratedTypeAnnotation('int j').node;
var kNode = decoratedTypeAnnotation('int k').node;
// No edge from i to j because i is promoted at the time of the call to g.
assertNoEdge(iNode, jNode);
// But there is an edge from i to j, because i is not promoted at the time
// of the call to h.
assertEdge(iNode, kNode, hard: false);
}
test_do_break_target() async {
await analyze('''
void f(int i) {
L: do {
do {
if (i != null) break L;
if (b()) break;
} while (true);
g(i);
} while (true);
h(i);
}
bool b() => true;
void g(int j) {}
void h(int k) {}
''');
var iNode = decoratedTypeAnnotation('int i').node;
var jNode = decoratedTypeAnnotation('int j').node;
var kNode = decoratedTypeAnnotation('int k').node;
// No edge from i to k because i is promoted at the time of the call to h.
assertNoEdge(iNode, kNode);
// But there is an edge from i to j, because i is not promoted at the time
// of the call to g.
assertEdge(iNode, jNode, hard: false);
}
test_do_cancels_promotions_for_assignments_in_body() async {
await analyze('''
void f(int i, int j) {
if (i == null) return;
if (j == null) return;
do {
i.isEven;
j.isEven;
j = null;
} while (true);
}
''');
var iNode = decoratedTypeAnnotation('int i').node;
var jNode = decoratedTypeAnnotation('int j').node;
// No edge from i to never because is is promoted.
assertNoEdge(iNode, never);
// But there is an edge from j to never because its promotion was cancelled.
assertEdge(jNode, never, hard: false);
}
test_do_cancels_promotions_for_assignments_in_condition() async {
await analyze('''
void f(int i, int j) {
if (i == null) return;
if (j == null) return;
do {} while (i.isEven && j.isEven && g(j = null));
}
bool g(int k) => true;
''');
var iNode = decoratedTypeAnnotation('int i').node;
var jNode = decoratedTypeAnnotation('int j').node;
// No edge from i to never because is is promoted.
assertNoEdge(iNode, never);
// But there is an edge from j to never because its promotion was cancelled.
assertEdge(jNode, never, hard: false);
}
test_do_continue_target() async {
await analyze('''
void f(int i) {
L: do {
do {
if (i != null) continue L;
g(i);
} while (true);
} while (h(i));
}
void g(int j) {}
bool h(int k) => true;
''');
var iNode = decoratedTypeAnnotation('int i').node;
var jNode = decoratedTypeAnnotation('int j').node;
var kNode = decoratedTypeAnnotation('int k').node;
// No edge from i to k because i is promoted at the time of the call to h.
assertNoEdge(iNode, kNode);
// But there is an edge from i to j, because i is not promoted at the time
// of the call to g.
assertEdge(iNode, jNode, hard: false);
}
test_field_initializer() async {
await analyze('''
bool b1 = true;
bool b2 = true;
class C {
bool b = b1 || b2;
}
''');
// No assertions; we just want to verify that the presence of `||` inside a
// field doesn't cause flow analysis to crash.
}
test_functionDeclaration() async {
await analyze('''
void f(int i, int j) {
if (i == null) return;
print(i.isEven);
print(j.isEven);
}
''');
var iNode = decoratedTypeAnnotation('int i').node;
var jNode = decoratedTypeAnnotation('int j').node;
// No edge from i to `never` because i's type is promoted to non-nullable
assertNoEdge(iNode, never);
// But there is an edge from j to `never`.
assertEdge(jNode, never, hard: false);
}
test_functionDeclaration_expression_body() async {
await analyze('''
bool f(int i) => i == null || i.isEven;
bool g(int j) => j.isEven;
''');
var iNode = decoratedTypeAnnotation('int i').node;
var jNode = decoratedTypeAnnotation('int j').node;
// No edge from i to `never` because i's type is promoted to non-nullable
assertNoEdge(iNode, never);
// But there is an edge from j to `never`.
assertEdge(jNode, never, hard: true);
}
test_functionDeclaration_resets_unconditional_control_flow() async {
await analyze('''
void f(bool b, int i, int j) {
assert(i != null);
if (b) return;
assert(j != null);
}
void g(int k) {
assert(k != null);
}
''');
assertEdge(decoratedTypeAnnotation('int i').node, never, hard: true);
assertNoEdge(always, decoratedTypeAnnotation('int j').node);
assertEdge(decoratedTypeAnnotation('int k').node, never, hard: true);
}
test_functionExpression_parameters() async {
await analyze('''
void f() {
var g = (int i, int j) {
if (i == null) return;
print(i.isEven);
print(j.isEven);
};
}
''');
var iNode = decoratedTypeAnnotation('int i').node;
var jNode = decoratedTypeAnnotation('int j').node;
// No edge from i to `never` because i's type is promoted to non-nullable
assertNoEdge(iNode, never);
// But there is an edge from j to `never`.
assertEdge(jNode, never, hard: false);
}
test_if() async {
await analyze('''
void f(int i) {
if (i == null) {
g(i);
} else {
h(i);
}
}
void g(int j) {}
void h(int k) {}
''');
var iNode = decoratedTypeAnnotation('int i').node;
var jNode = decoratedTypeAnnotation('int j').node;
var kNode = decoratedTypeAnnotation('int k').node;
// No edge from i to k because i is known to be non-nullable at the site of
// the call to h()
assertNoEdge(iNode, kNode);
// But there is an edge from i to j
assertEdge(iNode, jNode, hard: false, guards: [iNode]);
}
test_if_without_else() async {
await analyze('''
void f(int i) {
if (i == null) {
g(i);
return;
}
h(i);
}
void g(int j) {}
void h(int k) {}
''');
var iNode = decoratedTypeAnnotation('int i').node;
var jNode = decoratedTypeAnnotation('int j').node;
var kNode = decoratedTypeAnnotation('int k').node;
// No edge from i to k because i is known to be non-nullable at the site of
// the call to h()
assertNoEdge(iNode, kNode);
// But there is an edge from i to j
assertEdge(iNode, jNode, hard: false, guards: [iNode]);
}
test_local_function_parameters() async {
await analyze('''
void f() {
void g(int i, int j) {
if (i == null) return;
print(i.isEven);
print(j.isEven);
}
}
''');
var iNode = decoratedTypeAnnotation('int i').node;
var jNode = decoratedTypeAnnotation('int j').node;
// No edge from i to `never` because i's type is promoted to non-nullable
assertNoEdge(iNode, never);
// But there is an edge from j to `never`.
assertEdge(jNode, never, hard: false);
}
test_return() async {
await analyze('''
void f(int i, int j) {
if (i == null) return;
print(i.isEven);
print(j.isEven);
}
''');
var iNode = decoratedTypeAnnotation('int i').node;
var jNode = decoratedTypeAnnotation('int j').node;
// No edge from i to `never` because i's type is promoted to non-nullable
assertNoEdge(iNode, never);
// But there is an edge from j to `never`.
assertEdge(jNode, never, hard: false);
}
test_topLevelVar_initializer() async {
await analyze('''
bool b1 = true;
bool b2 = true;
bool b3 = b1 || b2;
''');
// No assertions; we just want to verify that the presence of `||` inside a
// top level variable doesn't cause flow analysis to crash.
}
test_while_break_target() async {
await analyze('''
void f(int i) {
L: while (true) {
while (true) {
if (i != null) break L;
if (b()) break;
}
g(i);
}
h(i);
}
bool b() => true;
void g(int j) {}
void h(int k) {}
''');
var iNode = decoratedTypeAnnotation('int i').node;
var jNode = decoratedTypeAnnotation('int j').node;
var kNode = decoratedTypeAnnotation('int k').node;
// No edge from i to k because i is promoted at the time of the call to h.
assertNoEdge(iNode, kNode);
// But there is an edge from i to j, because i is not promoted at the time
// of the call to g.
assertEdge(iNode, jNode, hard: false);
}
test_while_cancels_promotions_for_assignments_in_body() async {
await analyze('''
void f(int i, int j) {
if (i == null) return;
if (j == null) return;
while (true) {
i.isEven;
j.isEven;
j = null;
}
}
''');
var iNode = decoratedTypeAnnotation('int i').node;
var jNode = decoratedTypeAnnotation('int j').node;
// No edge from i to never because is is promoted.
assertNoEdge(iNode, never);
// But there is an edge from j to never because its promotion was cancelled.
assertEdge(jNode, never, hard: false);
}
test_while_cancels_promotions_for_assignments_in_condition() async {
await analyze('''
void f(int i, int j) {
if (i == null) return;
if (j == null) return;
while (i.isEven && j.isEven && g(j = null)) {}
}
bool g(int k) => true;
''');
var iNode = decoratedTypeAnnotation('int i').node;
var jNode = decoratedTypeAnnotation('int j').node;
// No edge from i to never because is is promoted.
assertNoEdge(iNode, never);
// But there is an edge from j to never because its promotion was cancelled.
assertEdge(jNode, never, hard: false);
}
test_while_promotes() async {
await analyze('''
void f(int i, int j) {
while (i != null) {
i.isEven;
j.isEven;
}
}
''');
var iNode = decoratedTypeAnnotation('int i').node;
var jNode = decoratedTypeAnnotation('int j').node;
// No edge from i to never because is is promoted.
assertNoEdge(iNode, never);
// But there is an edge from j to never.
assertEdge(jNode, never, hard: false);
}
}