diff --git a/tests/language/nnbd/flow_analysis/write_promoted_value_in_closure_error_test.dart b/tests/language/nnbd/flow_analysis/write_promoted_value_in_closure_error_test.dart new file mode 100644 index 000000000000..bd76d000e360 --- /dev/null +++ b/tests/language/nnbd/flow_analysis/write_promoted_value_in_closure_error_test.dart @@ -0,0 +1,44 @@ +// Copyright (c) 2020, 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. + +// Verifies that variables assigned in closures and local functions are +// de-promoted at the top of the closure, since the closure may be invoked +// multiple times. + +void functionExpression(Object x) { + if (x is int) { + print(x.isEven); // Verify that promotion occurred + var f = () { + // The assignment to x does de-promote because it happens after the top of + // the closure, so flow analysis cannot check that the assigned value is + // an int at the time de-promotion occurs. + print(x.isEven); + // ^^^^^^ + // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER + // [cfe] The getter 'isEven' isn't defined for the class 'Object'. + x = 0; + }; + } +} + +void localFunction(Object x) { + if (x is int) { + print(x.isEven); // Verify that promotion occurred + f() { + // The assignment to x does de-promote because it happens after the top of + // the closure, so flow analysis cannot check that the assigned value is + // an int at the time de-promotion occurs. + print(x.isEven); + // ^^^^^^ + // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER + // [cfe] The getter 'isEven' isn't defined for the class 'Object'. + x = 0; + } + } +} + +main() { + functionExpression(0); + localFunction(0); +} diff --git a/tests/language/nnbd/flow_analysis/write_promoted_value_in_loop_error_test.dart b/tests/language/nnbd/flow_analysis/write_promoted_value_in_loop_error_test.dart new file mode 100644 index 000000000000..646649d39a48 --- /dev/null +++ b/tests/language/nnbd/flow_analysis/write_promoted_value_in_loop_error_test.dart @@ -0,0 +1,141 @@ +// Copyright (c) 2020, 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. + +// Verifies that variables assigned in loops are de-promoted at the top of the +// loop body, since the loop body be executed multiple times. + +void forLoopAssignInCondition(Object x) { + if (x is int) { + print(x.isEven); // Verify that promotion occurred + for (; 0 == 0 ? (x = 0) == 0 : true;) { + // The assignment to x does de-promote because it happens after the top of + // the loop, so flow analysis cannot check that the assigned value is an + // int at the time de-promotion occurs. + print(x.isEven); + // ^^^^^^ + // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER + // [cfe] The getter 'isEven' isn't defined for the class 'Object'. + } + } +} + +void forLoopAssignInUpdater(Object x) { + if (x is int) { + print(x.isEven); // Verify that promotion occurred + for (;; x = 0) { + // The assignment to x does de-promote because it happens after the top of + // the loop, so flow analysis cannot check that the assigned value is an + // int at the time de-promotion occurs. + print(x.isEven); + // ^^^^^^ + // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER + // [cfe] The getter 'isEven' isn't defined for the class 'Object'. + } + } +} + +void forLoopAssignInBody(Object x) { + if (x is int) { + print(x.isEven); // Verify that promotion occurred + for (;;) { + // The assignment to x does de-promote because it happens after the top of + // the loop, so flow analysis cannot check that the assigned value is an + // int at the time de-promotion occurs. + print(x.isEven); + // ^^^^^^ + // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER + // [cfe] The getter 'isEven' isn't defined for the class 'Object'. + x = 0; + } + } +} + +void forEachAssignInBody(Object x) { + if (x is int) { + print(x.isEven); // Verify that promotion occurred + for (var y in [0]) { + // The assignment to x does de-promote because it happens after the top of + // the loop, so flow analysis cannot check that the assigned value is an + // int at the time de-promotion occurs. + print(x.isEven); + // ^^^^^^ + // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER + // [cfe] The getter 'isEven' isn't defined for the class 'Object'. + x = 0; + } + } +} + +void whileAssignInCondition(Object x) { + if (x is int) { + print(x.isEven); // Verify that promotion occurred + while (0 == 0 ? (x = 0) == 0 : true) { + // The assignment to x does de-promote because it happens after the top of + // the loop, so flow analysis cannot check that the assigned value is an + // int at the time de-promotion occurs. + print(x.isEven); + // ^^^^^^ + // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER + // [cfe] The getter 'isEven' isn't defined for the class 'Object'. + } + } +} + +void whileAssignInBody(Object x) { + if (x is int) { + print(x.isEven); // Verify that promotion occurred + while (true) { + // The assignment to x does de-promote because it happens after the top of + // the loop, so flow analysis cannot check that the assigned value is an + // int at the time de-promotion occurs. + print(x.isEven); + // ^^^^^^ + // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER + // [cfe] The getter 'isEven' isn't defined for the class 'Object'. + x = 0; + } + } +} + +void doAssignInCondition(Object x) { + if (x is int) { + print(x.isEven); // Verify that promotion occurred + do { + // The assignment to x does de-promote because it happens after the top of + // the loop, so flow analysis cannot check that the assigned value is an + // int at the time de-promotion occurs. + print(x.isEven); + // ^^^^^^ + // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER + // [cfe] The getter 'isEven' isn't defined for the class 'Object'. + } while ((x = 0) == 0); + } +} + +void doAssignInBody(Object x) { + if (x is int) { + print(x.isEven); // Verify that promotion occurred + do { + // The assignment to x does de-promote because it happens after the top of + // the loop, so flow analysis cannot check that the assigned value is an + // int at the time de-promotion occurs. + print(x.isEven); + // ^^^^^^ + // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER + // [cfe] The getter 'isEven' isn't defined for the class 'Object'. + x = 0; + } while (true); + } +} + +main() { + forLoopAssignInCondition(0); + forLoopAssignInUpdater(0); + forLoopAssignInBody(0); + forEachAssignInBody(0); + whileAssignInCondition(0); + whileAssignInBody(0); + doAssignInCondition(0); + doAssignInBody(0); +} diff --git a/tests/language/nnbd/flow_analysis/write_promoted_value_in_loop_test.dart b/tests/language/nnbd/flow_analysis/write_promoted_value_in_loop_test.dart new file mode 100644 index 000000000000..a3857153ffa4 --- /dev/null +++ b/tests/language/nnbd/flow_analysis/write_promoted_value_in_loop_test.dart @@ -0,0 +1,76 @@ +// Copyright (c) 2020, 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. + +// Verifies that variables assigned in initialization parts of loops are not +// de-promoted, since loop initialization only executes once. + +void forLoopWithoutDecl(Object x) { + if (x is int) { + for (x = 0;;) { + // The assignment to x does not de-promote x because it happens before the + // top of the loop, and it assigns an int (which is compatible with the + // promoted type). + x.isEven; + break; + } + } +} + +void forLoopWithoutDeclAssignInRHS(Object x) { + if (x is int) { + int y; + for (y = (x = 0);;) { + // The assignment to x does not de-promote x because it happens before the + // top of the loop, and it assigns an int (which is compatible with the + // promoted type). + x.isEven; + break; + } + } +} + +void forLoopWithDeclAssignInRHS(Object x) { + if (x is int) { + for (int y = (x = 0);;) { + // The assignment to x does not de-promote x because it happens before the + // top of the loop, and it assigns an int (which is compatible with the + // promoted type). + x.isEven; + break; + } + } +} + +void forEachWithoutDecl(Object x) { + if (x is int) { + int y; + for (y in [x = 0]) { + // The assignment to x does not de-promote x because it happens before the + // top of the loop, and it assigns an int (which is compatible with the + // promoted type). + x.isEven; + break; + } + } +} + +void forEachWithDecl(Object x) { + if (x is int) { + for (int y in [x = 0]) { + // The assignment to x does not de-promote x because it happens before the + // top of the loop, and it assigns an int (which is compatible with the + // promoted type). + x.isEven; + break; + } + } +} + +main() { + forLoopWithoutDecl(0); + forLoopWithoutDeclAssignInRHS(0); + forLoopWithDeclAssignInRHS(0); + forEachWithoutDecl(0); + forEachWithDecl(0); +} diff --git a/tests/language/nnbd/flow_analysis/write_promoted_value_in_switch_error_test.dart b/tests/language/nnbd/flow_analysis/write_promoted_value_in_switch_error_test.dart new file mode 100644 index 000000000000..9d96b15de3fa --- /dev/null +++ b/tests/language/nnbd/flow_analysis/write_promoted_value_in_switch_error_test.dart @@ -0,0 +1,54 @@ +// Copyright (c) 2020, 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. + +// Verifies that variables assigned in switch statement bodies are de-promoted +// at the top of labelled case blocks, since the assignment may occur before a +// branch to the labelled case block. + +void switchWithLabelAssignInCase(Object x) { + if (x is int) { + print(x.isEven); // Verify that promotion occurred + switch (x) { + case 1: + continue L; + L: + case 0: + // The assignment to x does de-promote because it happens after the + // label, so flow analysis cannot check that the assigned value is an + // int at the time de-promotion occurs. + print(x.isEven); + // ^^^^^^ + // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER + // [cfe] The getter 'isEven' isn't defined for the class 'Object'. + x = 0; + break; + } + } +} + +void switchWithLabelAssignInDefault(Object x) { + if (x is int) { + print(x.isEven); // Verify that promotion occurred + switch (x) { + case 1: + continue L; + L: + default: + // The assignment to x does de-promote because it happens after the + // label, so flow analysis cannot check that the assigned value is an + // int at the time de-promotion occurs. + print(x.isEven); + // ^^^^^^ + // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER + // [cfe] The getter 'isEven' isn't defined for the class 'Object'. + x = 0; + break; + } + } +} + +main() { + switchWithLabelAssignInCase(0); + switchWithLabelAssignInDefault(0); +} diff --git a/tests/language/nnbd/flow_analysis/write_promoted_value_in_switch_test.dart b/tests/language/nnbd/flow_analysis/write_promoted_value_in_switch_test.dart new file mode 100644 index 000000000000..c8cfc47ef35d --- /dev/null +++ b/tests/language/nnbd/flow_analysis/write_promoted_value_in_switch_test.dart @@ -0,0 +1,99 @@ +// Copyright (c) 2020, 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. + +// Verifies that variables assigned in switch statements are not de-promoted if +// the assignment cannot possibly happen prior to the read. + +void switchDefaultWithoutLabel(Object x) { + if (x is int) { + switch (x = 0) { + case 1: + break; + default: + // The assignment to x does not de-promote x because there is no label, + // and it assigns an int (which is compatible with the promoted type). + x.isEven; + break; + } + } +} + +void switchCaseWithoutLabel(Object x) { + if (x is int) { + switch (x = 0) { + case 1: + break; + case 0: + // The assignment to x does not de-promote x because there is no label, + // and it assigns an int (which is compatible with the promoted type). + x.isEven; + break; + } + } +} + +void switchDefaultWithoutLabelAssignInDefault(Object x) { + if (x is int) { + switch (x) { + default: + // The assignment to x does not de-promote x because there is no label. + x.isEven; + x = 0; + break; + } + } +} + +void switchCaseWithoutLabelAssignInCase(Object x) { + if (x is int) { + switch (x) { + case 0: + // The assignment to x does not de-promote x because there is no label. + x.isEven; + x = 0; + break; + } + } +} + +void switchDefaultWithLabel(Object x) { + if (x is int) { + switch (x = 0) { + case 1: + continue L; + L: + default: + // The assignment to x does not de-promote x because it happens before + // the label, and it assigns an int (which is compatible with the + // promoted type). + x.isEven; + break; + } + } +} + +void switchCaseWithLabel(Object x) { + if (x is int) { + switch (x = 0) { + case 1: + continue L; + L: + case 0: + // The assignment to x does not de-promote x because it happens before + // the label, and it assigns an int (which is compatible with the + // promoted type). + x.isEven; + break; + } + } +} + +main() { + switchDefaultWithoutLabel(0); + switchCaseWithoutLabel(0); + switchDefaultWithoutLabelAssignInDefault(0); + switchCaseWithoutLabelAssignInCase(0); + switchDefaultWithLabel(0); + switchCaseWithLabel(0); +} diff --git a/tests/language/nnbd/flow_analysis/write_promoted_value_in_try_error_test.dart b/tests/language/nnbd/flow_analysis/write_promoted_value_in_try_error_test.dart new file mode 100644 index 000000000000..8f89bdf0d218 --- /dev/null +++ b/tests/language/nnbd/flow_analysis/write_promoted_value_in_try_error_test.dart @@ -0,0 +1,48 @@ +// Copyright (c) 2020, 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. + +// Verifies that variables assigned in try blocks are de-promoted in catch and +// finally blocks, since the catch or finally block may execute after the +// assignment. + +void tryCatchAssignInBody(Object x) { + if (x is int) { + print(x.isEven); // Verify that promotion occurred + try { + x = 0; + } catch (e) { + // The assignment to x does de-promote because flow analysis does a + // conservative estimate of the flow model resulting from a caught + // exception (using the same logic it uses for loops, which doesn't + // account for RHS types) + print(x.isEven); + // ^^^^^^ + // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER + // [cfe] The getter 'isEven' isn't defined for the class 'Object'. + } + } +} + +void tryFinallyAssignInBody(Object x) { + if (x is int) { + print(x.isEven); // Verify that promotion occurred + try { + x = 0; + } finally { + // The assignment to x does de-promote because flow analysis does a + // conservative estimate of the flow model resulting from a caught + // exception (using the same logic it uses for loops, which doesn't + // account for RHS types) + print(x.isEven); + // ^^^^^^ + // [analyzer] STATIC_TYPE_WARNING.UNDEFINED_GETTER + // [cfe] The getter 'isEven' isn't defined for the class 'Object'. + } + } +} + +main() { + tryCatchAssignInBody(0); + tryFinallyAssignInBody(0); +} diff --git a/tests/language/nnbd/flow_analysis/write_promoted_value_in_try_test.dart b/tests/language/nnbd/flow_analysis/write_promoted_value_in_try_test.dart new file mode 100644 index 000000000000..5ddcaf31fb63 --- /dev/null +++ b/tests/language/nnbd/flow_analysis/write_promoted_value_in_try_test.dart @@ -0,0 +1,33 @@ +// Copyright (c) 2020, 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. + +// Verifies that variables assigned in catch and finally are not de-promoted, +// since the catch or finally block only executes once. + +void tryCatchAssignInCatch(Object x) { + if (x is int) { + try {} catch (e) { + // The assignment to x does not de-promote because the assignment is + // outside the scope of the try block + x.isEven; + x = ''; + } + } +} + +void tryFinallyAssignInBody(Object x) { + if (x is int) { + try {} finally { + // The assignment to x does not de-promote because the assignment is + // outside the scope of the try block + x.isEven; + x = 0; + } + } +} + +main() { + tryCatchAssignInCatch(0); + tryFinallyAssignInBody(0); +}