| OLD | NEW |
| (Empty) | |
| 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 |
| 3 // BSD-style license that can be found in the LICENSE file. |
| 4 |
| 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 |
| 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. |
| 9 |
| 10 library analyzer.test.src.task.non_null_primitives.checker_test; |
| 11 |
| 12 import 'package:test_reflective_loader/test_reflective_loader.dart'; |
| 13 |
| 14 import '../strong/strong_test_helper.dart'; |
| 15 |
| 16 void main() { |
| 17 initStrongModeTests(); |
| 18 defineReflectiveTests(NonNullCheckerTest); |
| 19 } |
| 20 |
| 21 String _withError(String file, String error) { |
| 22 return ("" + file).replaceFirst("boom", error); |
| 23 } |
| 24 |
| 25 @reflectiveTest |
| 26 class NonNullCheckerTest { |
| 27 // Tests simple usage of ints as iterators for a loop. Not directly related to |
| 28 // non-nullability, but if it is implemented this should be more efficient, |
| 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 |
| 88 void test_forLoop() { |
| 89 checkFile(''' |
| 90 class MyList { |
| 91 int length; |
| 92 MyList() { |
| 93 length = 6; |
| 94 } |
| 95 String operator [](int i) { |
| 96 return <String>["Dart", "Java", "JS", "C", "C++", "C#"][i]; |
| 97 } |
| 98 } |
| 99 |
| 100 main() { |
| 101 var languages = new MyList(); |
| 102 for (int i = 0; i < languages.length; ++i) { |
| 103 print(languages[i]); |
| 104 } |
| 105 } |
| 106 '''); |
| 107 } |
| 108 |
| 109 void test_compoundAssignment() { |
| 110 addFile(''' |
| 111 void main() { |
| 112 int i = 1; |
| 113 i += 2; |
| 114 /*error:INVALID_ASSIGNMENT*/i += null; |
| 115 print(i); |
| 116 } |
| 117 '''); |
| 118 check(nonnullableTypes: <String>['dart:core,int']); |
| 119 } |
| 120 |
| 121 void test_forEach() { |
| 122 addFile(''' |
| 123 void main() { |
| 124 var ints = <num>[1, 2, 3, null]; |
| 125 for (int /*error:INVALID_ASSIGNMENT*/i in ints) { |
| 126 print(i); |
| 127 } |
| 128 } |
| 129 '''); |
| 130 check(nonnullableTypes: <String>['dart:core,int']); |
| 131 } |
| 132 |
| 133 void test_initialize_nonnullable_with_null() { |
| 134 addFile('int x = /*error:INVALID_ASSIGNMENT*/null;'); |
| 135 check(nonnullableTypes: <String>['dart:core,int']); |
| 136 } |
| 137 |
| 138 void test_initialize_nonnullable_with_valid_value() { |
| 139 addFile('int x = 0;'); |
| 140 check(nonnullableTypes: <String>['dart:core,int']); |
| 141 } |
| 142 |
| 143 void test_nonnullable_fields() { |
| 144 addFile(defaultNnbdExample); |
| 145 // `null` can be passed as an argument to `Point` in default mode. |
| 146 addFile(_withError(defaultNnbdExampleMod1, "error:INVALID_ASSIGNMENT")); |
| 147 // A nullable expression can be passed as an argument to `Point` in default |
| 148 // mode. |
| 149 addFile(_withError(defaultNnbdExampleMod2, "error:INVALID_ASSIGNMENT")); |
| 150 check(nonnullableTypes: <String>['dart:core,int']); |
| 151 } |
| 152 |
| 153 void test_nullable_fields() { |
| 154 addFile(defaultNnbdExample); |
| 155 // `null` can be passed as an argument to `Point` in default mode. |
| 156 addFile(defaultNnbdExampleMod1); |
| 157 // A nullable expression can be passed as an argument to `Point` in default |
| 158 // mode. |
| 159 addFile(defaultNnbdExampleMod2); |
| 160 check(); |
| 161 } |
| 162 |
| 163 // Default example from NNBD document. |
| 164 void test_nullableTypes() { |
| 165 // By default x can be set to null. |
| 166 checkFile('int x = null;'); |
| 167 } |
| 168 |
| 169 void test_prefer_final_to_non_nullable_error() { |
| 170 addFile('main() { final int /*error:FINAL_NOT_INITIALIZED*/x; }'); |
| 171 addFile('final int /*error:FINAL_NOT_INITIALIZED*/x;'); |
| 172 addFile(''' |
| 173 void foo() {} |
| 174 |
| 175 class A { |
| 176 final int x; |
| 177 |
| 178 /*warning:FINAL_NOT_INITIALIZED_CONSTRUCTOR_1*/A(); |
| 179 } |
| 180 '''); |
| 181 check(nonnullableTypes: <String>['dart:core,int']); |
| 182 } |
| 183 |
| 184 void test_uninitialized_nonnullable_field_declaration() { |
| 185 addFile(''' |
| 186 void foo() {} |
| 187 |
| 188 class A { |
| 189 // Ideally, we should allow x to be init in the constructor, but that requires |
| 190 // too much complication in the checker, so for now we throw a static error at |
| 191 // the declaration site. |
| 192 int /*error:NON_NULLABLE_FIELD_NOT_INITIALIZED*/x; |
| 193 |
| 194 A(); |
| 195 } |
| 196 '''); |
| 197 check(nonnullableTypes: <String>['dart:core,int']); |
| 198 } |
| 199 |
| 200 void test_uninitialized_nonnullable_local_variable() { |
| 201 // Ideally, we will do flow analysis and throw an error only if a variable |
| 202 // is used before it has been initialized. |
| 203 addFile('main() { int /*error:NON_NULLABLE_FIELD_NOT_INITIALIZED*/x; }'); |
| 204 check(nonnullableTypes: <String>['dart:core,int']); |
| 205 } |
| 206 |
| 207 void test_uninitialized_nonnullable_top_level_variable_declaration() { |
| 208 // If `int`s are non-nullable, then this code should throw an error. |
| 209 addFile('int /*error:NON_NULLABLE_FIELD_NOT_INITIALIZED*/x;'); |
| 210 check(nonnullableTypes: <String>['dart:core,int']); |
| 211 } |
| 212 |
| 213 void test_method_call() { |
| 214 addFile(''' |
| 215 int s(int x) { |
| 216 return x + 1; |
| 217 } |
| 218 |
| 219 void main() { |
| 220 s(10); |
| 221 s(/*error:INVALID_ASSIGNMENT*/null); |
| 222 } |
| 223 '''); |
| 224 check(nonnullableTypes: <String>['dart:core,int']); |
| 225 } |
| 226 |
| 227 void test_generics() { |
| 228 addFile(''' |
| 229 class Foo<T> { |
| 230 T x; |
| 231 |
| 232 Foo(this.x); |
| 233 } |
| 234 |
| 235 void main() { |
| 236 var f = new Foo<String>("hello"); |
| 237 var g = new Foo<int>(10); |
| 238 var h = new Foo<String>(null); |
| 239 var i = new Foo<int>(/*error:INVALID_ASSIGNMENT*/null); |
| 240 |
| 241 print(f.x); |
| 242 print(g.x); |
| 243 print(h.x); |
| 244 print(i.x); |
| 245 } |
| 246 '''); |
| 247 addFile(''' |
| 248 class Foo<T> { |
| 249 T x; // Should be annotated for a runtime check: x = (null as T) |
| 250 |
| 251 Foo(); |
| 252 } |
| 253 |
| 254 void main() { |
| 255 var f = new Foo<String>(); |
| 256 var g = new Foo<int>(); // Should fail at runtime. |
| 257 } |
| 258 '''); |
| 259 check(nonnullableTypes: <String>['dart:core,int']); |
| 260 } |
| 261 |
| 262 void test_map() { |
| 263 addFile(''' |
| 264 class Pair<K, V> { |
| 265 K first; |
| 266 V second; |
| 267 |
| 268 Pair(this.first, this.second); |
| 269 } |
| 270 |
| 271 class SlowMap<K, V> { |
| 272 List<Pair<K, V>> array; |
| 273 int arrayLength = 0; |
| 274 |
| 275 SlowMap() : array = <Pair<K, V>>[]; |
| 276 |
| 277 void insert(K key, V value) { |
| 278 array.add(new Pair<K, V>(key, value)); |
| 279 ++arrayLength; |
| 280 } |
| 281 |
| 282 bool has(K key) { |
| 283 for (int i = 0; i < arrayLength; ++i) { |
| 284 if (array[i].first == key) { |
| 285 return true; |
| 286 } |
| 287 } |
| 288 return false; |
| 289 } |
| 290 |
| 291 V get(K key) { |
| 292 for (int i = 0; i < arrayLength; ++i) { |
| 293 if (array[i].first == key) { |
| 294 return array[i].second; |
| 295 } |
| 296 } |
| 297 return null; |
| 298 // TODO(stanm): generate explicit cast to V which will produce a runtime |
| 299 // error if V is non-nullable. Optionally, generate a static warning too. |
| 300 } |
| 301 } |
| 302 |
| 303 void main() { |
| 304 var legs = new SlowMap<String, int>(); |
| 305 legs.insert("spider", 8); |
| 306 legs.insert("goat", 4); |
| 307 legs.insert("chicken", 2); |
| 308 |
| 309 int x = legs.get("goat"); // This should not produce an error. |
| 310 int y = legs.get("sheep"); // TODO(stanm): Runtime error here. |
| 311 } |
| 312 '''); |
| 313 check(nonnullableTypes: <String>['dart:core,int']); |
| 314 } |
| 315 } |
| OLD | NEW |