OLD | NEW |
1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 // These tests are for an experimental feature that treats Dart primitive types | 5 // These tests are for an experimental feature that treats Dart primitive types |
6 // (int, bool, double, etc.) as non-nullable. This file is not evidence for an | 6 // (int, bool, double, etc.) as non-nullable. This file is not evidence for an |
7 // intention to officially support non-nullable primitives in Dart (or general | 7 // intention to officially support non-nullable primitives in Dart (or general |
8 // NNBD, for that matter) so don't get too crazy about it. | 8 // NNBD, for that matter) so don't get too crazy about it. |
9 | 9 |
10 library analyzer.test.src.task.non_null_primitives.checker_test; | 10 library analyzer.test.src.task.non_null_primitives.checker_test; |
11 | 11 |
12 import '../../../reflective_tests.dart'; | 12 import 'package:test_reflective_loader/test_reflective_loader.dart'; |
| 13 |
13 import '../strong/strong_test_helper.dart'; | 14 import '../strong/strong_test_helper.dart'; |
14 | 15 |
15 void main() { | 16 void main() { |
16 initStrongModeTests(); | 17 initStrongModeTests(); |
17 runReflectiveTests(NonNullCheckerTest); | 18 defineReflectiveTests(NonNullCheckerTest); |
| 19 } |
| 20 |
| 21 String _withError(String file, String error) { |
| 22 return ("" + file).replaceFirst("boom", error); |
18 } | 23 } |
19 | 24 |
20 @reflectiveTest | 25 @reflectiveTest |
21 class NonNullCheckerTest { | 26 class NonNullCheckerTest { |
22 // Tests simple usage of ints as iterators for a loop. Not directly related to | 27 // Tests simple usage of ints as iterators for a loop. Not directly related to |
23 // non-nullability, but if it is implemented this should be more efficient, | 28 // non-nullability, but if it is implemented this should be more efficient, |
24 // since languages.length will not be null-checked on every iteration. | 29 // since languages.length will not be null-checked on every iteration. |
| 30 final String defaultNnbdExample = ''' |
| 31 class Point { |
| 32 final int x, y; |
| 33 Point(this.x, this.y); |
| 34 Point operator +(Point other) => new Point(x + other.x, y + other.y); |
| 35 String toString() => "x: \$x, y: \$y"; |
| 36 } |
| 37 |
| 38 void main() { |
| 39 Point p1 = new Point(0, 0); |
| 40 Point p2 = new Point(10, 10); |
| 41 print("p1 + p2 = \${p1 + p2}"); |
| 42 } |
| 43 '''; |
| 44 |
| 45 final String defaultNnbdExampleMod1 = ''' |
| 46 class Point { |
| 47 final int x, y; |
| 48 Point(this.x, this.y); |
| 49 Point operator +(Point other) => new Point(x + other.x, y + other.y); |
| 50 String toString() => "x: \$x, y: \$y"; |
| 51 } |
| 52 |
| 53 void main() { |
| 54 Point p1 = new Point(0, 0); |
| 55 Point p2 = new Point(10, /*boom*/null); // Change here. |
| 56 print("p1 + p2 = \${p1 + p2}"); |
| 57 } |
| 58 '''; |
| 59 |
| 60 final String defaultNnbdExampleMod2 = ''' |
| 61 class Point { |
| 62 final int x, y; |
| 63 Point(this.x, this.y); |
| 64 Point operator +(Point other) => new Point(x + other.x, y + other.y); |
| 65 String toString() => "x: \$x, y: \$y"; |
| 66 } |
| 67 |
| 68 void main() { |
| 69 bool f = false; // Necessary, because dead code is otherwise detected. |
| 70 Point p1 = new Point(0, 0); |
| 71 Point p2 = new Point(10, /*boom*/f ? 10 : null); // Change here. |
| 72 print("p1 + p2 = \${p1 + p2}"); |
| 73 } |
| 74 '''; |
| 75 |
| 76 void test_assign_null_to_nonnullable() { |
| 77 addFile(''' |
| 78 int x = 0; |
| 79 |
| 80 main() { |
| 81 x = 1; |
| 82 x = /*error:INVALID_ASSIGNMENT*/null; |
| 83 } |
| 84 '''); |
| 85 check(nonnullableTypes: <String>['dart:core,int']); |
| 86 } |
| 87 |
25 void test_forLoop() { | 88 void test_forLoop() { |
26 checkFile(''' | 89 checkFile(''' |
27 class MyList { | 90 class MyList { |
28 int length; | 91 int length; |
29 MyList() { | 92 MyList() { |
30 length = 6; | 93 length = 6; |
31 } | 94 } |
32 String operator [](int i) { | 95 String operator [](int i) { |
33 return <String>["Dart", "Java", "JS", "C", "C++", "C#"][i]; | 96 return <String>["Dart", "Java", "JS", "C", "C++", "C#"][i]; |
34 } | 97 } |
35 } | 98 } |
36 | 99 |
37 main() { | 100 main() { |
38 var languages = new MyList(); | 101 var languages = new MyList(); |
39 for (int i = 0; i < languages.length; ++i) { | 102 for (int i = 0; i < languages.length; ++i) { |
40 print(languages[i]); | 103 print(languages[i]); |
41 } | 104 } |
42 } | 105 } |
43 '''); | 106 '''); |
44 } | 107 } |
45 | 108 |
46 void test_nullableTypes() { | |
47 // By default x can be set to null. | |
48 checkFile('int x = null;'); | |
49 } | |
50 | |
51 void test_initialize_nonnullable_with_null() { | 109 void test_initialize_nonnullable_with_null() { |
52 addFile('int x = /*error:INVALID_ASSIGNMENT*/null;'); | 110 addFile('int x = /*error:INVALID_ASSIGNMENT*/null;'); |
53 check(nonnullableTypes: <String>['dart:core,int']); | 111 check(nonnullableTypes: <String>['dart:core,int']); |
54 } | 112 } |
55 | 113 |
56 void test_initialize_nonnullable_with_valid_value() { | 114 void test_initialize_nonnullable_with_valid_value() { |
57 addFile('int x = 0;'); | 115 addFile('int x = 0;'); |
58 check(nonnullableTypes: <String>['dart:core,int']); | 116 check(nonnullableTypes: <String>['dart:core,int']); |
59 } | 117 } |
60 | 118 |
61 void test_assign_null_to_nonnullable() { | 119 void test_nonnullable_fields() { |
62 addFile(''' | 120 addFile(defaultNnbdExample); |
63 int x = 0; | 121 // `null` can be passed as an argument to `Point` in default mode. |
64 | 122 addFile(_withError(defaultNnbdExampleMod1, "error:INVALID_ASSIGNMENT")); |
65 main() { | 123 // A nullable expression can be passed as an argument to `Point` in default |
66 x = 1; | 124 // mode. |
67 x = /*error:INVALID_ASSIGNMENT*/null; | 125 addFile(_withError(defaultNnbdExampleMod2, "error:INVALID_ASSIGNMENT")); |
68 } | |
69 '''); | |
70 check(nonnullableTypes: <String>['dart:core,int']); | 126 check(nonnullableTypes: <String>['dart:core,int']); |
71 } | 127 } |
72 | 128 |
73 void test_uninitialized_nonnullable_local_variable() { | 129 void test_nullable_fields() { |
74 // Ideally, we will do flow analysis and throw an error only if a variable | 130 addFile(defaultNnbdExample); |
75 // is used before it has been initialized. | 131 // `null` can be passed as an argument to `Point` in default mode. |
76 addFile('main() { int /*error:NON_NULLABLE_FIELD_NOT_INITIALIZED*/x; }'); | 132 addFile(defaultNnbdExampleMod1); |
77 check(nonnullableTypes: <String>['dart:core,int']); | 133 // A nullable expression can be passed as an argument to `Point` in default |
| 134 // mode. |
| 135 addFile(defaultNnbdExampleMod2); |
| 136 check(); |
78 } | 137 } |
79 | 138 |
80 void test_uninitialized_nonnullable_top_level_variable_declaration() { | 139 // Default example from NNBD document. |
81 // If `int`s are non-nullable, then this code should throw an error. | 140 void test_nullableTypes() { |
82 addFile('int /*error:NON_NULLABLE_FIELD_NOT_INITIALIZED*/x;'); | 141 // By default x can be set to null. |
| 142 checkFile('int x = null;'); |
| 143 } |
| 144 |
| 145 void test_prefer_final_to_non_nullable_error() { |
| 146 addFile('main() { final int /*error:FINAL_NOT_INITIALIZED*/x; }'); |
| 147 addFile('final int /*error:FINAL_NOT_INITIALIZED*/x;'); |
| 148 addFile(''' |
| 149 void foo() {} |
| 150 |
| 151 class A { |
| 152 final int x; |
| 153 |
| 154 /*warning:FINAL_NOT_INITIALIZED_CONSTRUCTOR_1*/A(); |
| 155 } |
| 156 '''); |
83 check(nonnullableTypes: <String>['dart:core,int']); | 157 check(nonnullableTypes: <String>['dart:core,int']); |
84 } | 158 } |
85 | 159 |
86 void test_uninitialized_nonnullable_field_declaration() { | 160 void test_uninitialized_nonnullable_field_declaration() { |
87 addFile(''' | 161 addFile(''' |
88 void foo() {} | 162 void foo() {} |
89 | 163 |
90 class A { | 164 class A { |
91 // Ideally, we should allow x to be init in the constructor, but that requires | 165 // Ideally, we should allow x to be init in the constructor, but that requires |
92 // too much complication in the checker, so for now we throw a static error at | 166 // too much complication in the checker, so for now we throw a static error at |
93 // the declaration site. | 167 // the declaration site. |
94 int /*error:NON_NULLABLE_FIELD_NOT_INITIALIZED*/x; | 168 int /*error:NON_NULLABLE_FIELD_NOT_INITIALIZED*/x; |
95 | 169 |
96 A(); | 170 A(); |
97 } | 171 } |
98 '''); | 172 '''); |
99 check(nonnullableTypes: <String>['dart:core,int']); | 173 check(nonnullableTypes: <String>['dart:core,int']); |
100 } | 174 } |
101 | 175 |
102 void test_prefer_final_to_non_nullable_error() { | 176 void test_uninitialized_nonnullable_local_variable() { |
103 addFile('main() { final int /*error:FINAL_NOT_INITIALIZED*/x; }'); | 177 // Ideally, we will do flow analysis and throw an error only if a variable |
104 addFile('final int /*error:FINAL_NOT_INITIALIZED*/x;'); | 178 // is used before it has been initialized. |
105 addFile(''' | 179 addFile('main() { int /*error:NON_NULLABLE_FIELD_NOT_INITIALIZED*/x; }'); |
106 void foo() {} | |
107 | |
108 class A { | |
109 final int x; | |
110 | |
111 /*warning:FINAL_NOT_INITIALIZED_CONSTRUCTOR_1*/A(); | |
112 } | |
113 '''); | |
114 check(nonnullableTypes: <String>['dart:core,int']); | 180 check(nonnullableTypes: <String>['dart:core,int']); |
115 } | 181 } |
116 | 182 |
117 // Default example from NNBD document. | 183 void test_uninitialized_nonnullable_top_level_variable_declaration() { |
118 final String defaultNnbdExample = ''' | 184 // If `int`s are non-nullable, then this code should throw an error. |
119 class Point { | 185 addFile('int /*error:NON_NULLABLE_FIELD_NOT_INITIALIZED*/x;'); |
120 final int x, y; | |
121 Point(this.x, this.y); | |
122 Point operator +(Point other) => new Point(x + other.x, y + other.y); | |
123 String toString() => "x: \$x, y: \$y"; | |
124 } | |
125 | |
126 void main() { | |
127 Point p1 = new Point(0, 0); | |
128 Point p2 = new Point(10, 10); | |
129 print("p1 + p2 = \${p1 + p2}"); | |
130 } | |
131 '''; | |
132 | |
133 final String defaultNnbdExampleMod1 = ''' | |
134 class Point { | |
135 final int x, y; | |
136 Point(this.x, this.y); | |
137 Point operator +(Point other) => new Point(x + other.x, y + other.y); | |
138 String toString() => "x: \$x, y: \$y"; | |
139 } | |
140 | |
141 void main() { | |
142 Point p1 = new Point(0, 0); | |
143 Point p2 = new Point(10, /*boom*/null); // Change here. | |
144 print("p1 + p2 = \${p1 + p2}"); | |
145 } | |
146 '''; | |
147 | |
148 final String defaultNnbdExampleMod2 = ''' | |
149 class Point { | |
150 final int x, y; | |
151 Point(this.x, this.y); | |
152 Point operator +(Point other) => new Point(x + other.x, y + other.y); | |
153 String toString() => "x: \$x, y: \$y"; | |
154 } | |
155 | |
156 void main() { | |
157 bool f = false; // Necessary, because dead code is otherwise detected. | |
158 Point p1 = new Point(0, 0); | |
159 Point p2 = new Point(10, /*boom*/f ? 10 : null); // Change here. | |
160 print("p1 + p2 = \${p1 + p2}"); | |
161 } | |
162 '''; | |
163 | |
164 void test_nullable_fields() { | |
165 addFile(defaultNnbdExample); | |
166 // `null` can be passed as an argument to `Point` in default mode. | |
167 addFile(defaultNnbdExampleMod1); | |
168 // A nullable expression can be passed as an argument to `Point` in default | |
169 // mode. | |
170 addFile(defaultNnbdExampleMod2); | |
171 check(); | |
172 } | |
173 | |
174 void test_nonnullable_fields() { | |
175 addFile(defaultNnbdExample); | |
176 // `null` can be passed as an argument to `Point` in default mode. | |
177 addFile(_withError(defaultNnbdExampleMod1, "error:INVALID_ASSIGNMENT")); | |
178 // A nullable expression can be passed as an argument to `Point` in default | |
179 // mode. | |
180 addFile(_withError(defaultNnbdExampleMod2, "error:INVALID_ASSIGNMENT")); | |
181 check(nonnullableTypes: <String>['dart:core,int']); | 186 check(nonnullableTypes: <String>['dart:core,int']); |
182 } | 187 } |
183 } | 188 } |
184 | |
185 String _withError(String file, String error) { | |
186 return ("" + file).replaceFirst("boom", error); | |
187 } | |
OLD | NEW |