OLD | NEW |
1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2015, 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 /// Tests for type inference. | 5 /// Tests for type inference. |
6 library dev_compiler.test.inferred_type_test; | 6 library dev_compiler.test.inferred_type_test; |
7 | 7 |
8 import 'package:unittest/unittest.dart'; | 8 import 'package:unittest/unittest.dart'; |
9 | 9 |
10 import 'package:dev_compiler/src/testing.dart'; | 10 import 'package:dev_compiler/src/testing.dart'; |
(...skipping 119 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
130 c = /*severe:StaticTypeError*/"hi"; | 130 c = /*severe:StaticTypeError*/"hi"; |
131 c = 4; | 131 c = 4; |
132 } | 132 } |
133 | 133 |
134 int y = 0; // field def after use | 134 int y = 0; // field def after use |
135 final z = 42; // should infer `int` | 135 final z = 42; // should infer `int` |
136 ''' | 136 ''' |
137 }); | 137 }); |
138 }); | 138 }); |
139 | 139 |
| 140 test('do not infer field type when initializer is null', () { |
| 141 testChecker({ |
| 142 '/main.dart': ''' |
| 143 var x = null; |
| 144 var y = 3; |
| 145 class A { |
| 146 static var x = null; |
| 147 static var y = 3; |
| 148 |
| 149 var x2 = null; |
| 150 var y2 = 3; |
| 151 } |
| 152 |
| 153 test() { |
| 154 x = "hi"; |
| 155 y = /*severe:StaticTypeError*/"hi"; |
| 156 A.x = "hi"; |
| 157 A.y = /*severe:StaticTypeError*/"hi"; |
| 158 new A().x2 = "hi"; |
| 159 new A().y2 = /*severe:StaticTypeError*/"hi"; |
| 160 } |
| 161 ''' |
| 162 }); |
| 163 }); |
| 164 |
140 test('do not infer from variables in same lib (order independence)', () { | 165 test('do not infer from variables in same lib (order independence)', () { |
141 testChecker({ | 166 testChecker({ |
142 '/main.dart': ''' | 167 '/main.dart': ''' |
143 var x = 2; | 168 var x = 2; |
144 var y = x; | |
145 | |
146 test1() { | |
147 x = /*severe:StaticTypeError*/"hi"; | |
148 y = "hi"; | |
149 } | |
150 ''' | |
151 }); | |
152 | |
153 testChecker({ | |
154 '/main.dart': ''' | |
155 class A { | |
156 static var x = 2; | |
157 static var y = A.x; | |
158 } | |
159 | |
160 test1() { | |
161 A.x = /*severe:StaticTypeError*/"hi"; | |
162 A.y = "hi"; | |
163 } | |
164 ''' | |
165 }); | |
166 | |
167 // Allowed with special flag. Note, while the flag is generally not stable, | |
168 // we can use it in this test because it is stable within a library (order | |
169 // matches program order). | |
170 testChecker({ | |
171 '/main.dart': ''' | |
172 var x = 2; | |
173 var y = x; | 169 var y = x; |
174 | 170 |
175 test1() { | 171 test1() { |
176 x = /*severe:StaticTypeError*/"hi"; | 172 x = /*severe:StaticTypeError*/"hi"; |
177 y = /*severe:StaticTypeError*/"hi"; | 173 y = "hi"; |
178 } | 174 } |
179 ''' | 175 ''' |
180 }, inferInNonStableOrder: true); | 176 }); |
181 | 177 |
182 testChecker({ | 178 testChecker({ |
183 '/main.dart': ''' | 179 '/main.dart': ''' |
184 class A { | 180 class A { |
185 static var x = 2; | 181 static var x = 2; |
186 static var y = A.x; | 182 static var y = A.x; |
187 } | 183 } |
188 | 184 |
189 test1() { | 185 test1() { |
190 A.x = /*severe:StaticTypeError*/"hi"; | 186 A.x = /*severe:StaticTypeError*/"hi"; |
191 A.y = /*severe:StaticTypeError*/"hi"; | 187 A.y = "hi"; |
192 } | 188 } |
193 ''' | 189 ''' |
194 }, inferInNonStableOrder: true); | 190 }); |
195 }); | 191 }); |
196 | 192 |
197 test('not ok to infer from variables in non-cycle libs', () { | 193 test('not ok to infer from variables in non-cycle libs', () { |
198 testChecker({ | 194 testChecker({ |
199 '/a.dart': ''' | 195 '/a.dart': ''' |
200 var x = 2; | 196 var x = 2; |
201 ''', | 197 ''', |
202 '/main.dart': ''' | 198 '/main.dart': ''' |
203 import 'a.dart'; | 199 import 'a.dart'; |
204 var y = x; | 200 var y = x; |
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
255 B.y = /*severe:StaticTypeError*/"hi"; | 251 B.y = /*severe:StaticTypeError*/"hi"; |
256 } | 252 } |
257 ''' | 253 ''' |
258 }, inferStaticsFromIdentifiers: true); | 254 }, inferStaticsFromIdentifiers: true); |
259 }); | 255 }); |
260 | 256 |
261 test('do not infer from variables in cycle libs', () { | 257 test('do not infer from variables in cycle libs', () { |
262 testChecker({ | 258 testChecker({ |
263 '/a.dart': ''' | 259 '/a.dart': ''' |
264 import 'main.dart'; | 260 import 'main.dart'; |
265 var x = 2; | 261 var x = 2; // ok to infer |
266 ''', | 262 ''', |
267 '/main.dart': ''' | 263 '/main.dart': ''' |
268 import 'a.dart'; | 264 import 'a.dart'; |
269 var y = x; | 265 var y = x; // not ok to infer yet |
270 | 266 |
271 test1() { | 267 test1() { |
272 int t = 3; | 268 int t = 3; |
273 t = /*info:DownCast*/x; | 269 t = x; |
274 t = /*info:DownCast*/y; | 270 t = /*info:DownCast*/y; |
275 } | 271 } |
276 ''' | 272 ''' |
277 }, inferStaticsFromIdentifiers: true); | 273 }, inferStaticsFromIdentifiers: true); |
278 | 274 |
279 testChecker({ | 275 testChecker({ |
280 '/a.dart': ''' | 276 '/a.dart': ''' |
281 import 'main.dart'; | 277 import 'main.dart'; |
282 class A { static var x = 2; } | 278 class A { static var x = 2; } |
283 ''', | 279 ''', |
284 '/main.dart': ''' | 280 '/main.dart': ''' |
285 import 'a.dart'; | 281 import 'a.dart'; |
286 class B { static var y = A.x; } | 282 class B { static var y = A.x; } |
287 | 283 |
288 test1() { | 284 test1() { |
289 int t = 3; | 285 int t = 3; |
290 t = /*info:DownCast*/A.x; | 286 t = A.x; |
291 t = /*info:DownCast*/A.y; | 287 t = /*info:DownCast*/A.y; |
292 } | 288 } |
293 ''' | 289 ''' |
294 }, inferStaticsFromIdentifiers: true); | 290 }, inferStaticsFromIdentifiers: true); |
295 }); | 291 }); |
296 | 292 |
297 test('do not infer from static and instance fields', () { | 293 test('do not infer from static and instance fields', () { |
298 testChecker({ | 294 testChecker({ |
299 '/a.dart': ''' | 295 '/a.dart': ''' |
300 import 'b.dart'; | 296 import 'b.dart'; |
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
343 test1() { | 339 test1() { |
344 int x = 0; | 340 int x = 0; |
345 // inference in A now works. | 341 // inference in A now works. |
346 x = A.a1; | 342 x = A.a1; |
347 x = new A().a2; | 343 x = new A().a2; |
348 } | 344 } |
349 ''' | 345 ''' |
350 }, inferStaticsFromIdentifiers: true); | 346 }, inferStaticsFromIdentifiers: true); |
351 }); | 347 }); |
352 | 348 |
353 test('don\'t infer on cycles', () { | 349 test('inference uses declared types', () { |
| 350 testChecker({ |
| 351 '/main.dart': ''' |
| 352 int w = 0; |
| 353 var x = 0; |
| 354 |
| 355 var y = w; // y can be inferred because w is typed int. |
| 356 var z = x; // z cannot, because x would be inferred. |
| 357 |
| 358 test1() { |
| 359 int a; |
| 360 a = w; |
| 361 a = x; |
| 362 a = y; |
| 363 a = /*info:DownCast*/z; |
| 364 } |
| 365 ''' |
| 366 }, inferStaticsFromIdentifiers: true); |
| 367 }); |
| 368 |
| 369 test('inference in cycles is deterministic', () { |
354 testChecker({ | 370 testChecker({ |
355 '/a.dart': ''' | 371 '/a.dart': ''' |
356 import 'b.dart'; | 372 import 'b.dart'; |
357 class A { | 373 class A { |
358 static final a1 = B.b1; | 374 static final a1 = B.b1; |
359 final a2 = new B().b2; | 375 final a2 = new B().b2; |
360 } | 376 } |
361 ''', | 377 ''', |
362 '/b.dart': ''' | 378 '/b.dart': ''' |
363 class B { | 379 class B { |
364 static final b1 = 1; | 380 static final b1 = 1; |
365 final b2 = 1; | 381 final b2 = 1; |
366 } | 382 } |
367 ''', | 383 ''', |
368 '/c.dart': ''' | 384 '/c.dart': ''' |
369 import "main.dart"; // creates a cycle | 385 import "main.dart"; // creates a cycle |
370 | 386 |
371 class C { | 387 class C { |
372 static final c1 = 1; | 388 static final c1 = 1; |
373 final c2 = 1; | 389 final c2 = 1; |
374 } | 390 } |
375 ''', | 391 ''', |
376 '/e.dart': ''' | 392 '/e.dart': ''' |
377 part "e2.dart"; | 393 import 'a.dart'; |
| 394 part 'e2.dart'; |
378 | 395 |
379 class E { | 396 class E { |
380 static final e1 = 1; | 397 static final e1 = 1; |
381 final e2 = 1; | 398 static final e2 = F.f1; |
| 399 static final e3 = A.a1; |
| 400 final e4 = 1; |
| 401 final e5 = new F().f2; |
| 402 final e6 = new A().a2; |
382 } | 403 } |
383 ''', | 404 ''', |
384 '/f.dart': ''' | 405 '/f.dart': ''' |
385 part "f2.dart"; | 406 part 'f2.dart'; |
386 ''', | 407 ''', |
387 '/e2.dart': ''' | 408 '/e2.dart': ''' |
388 class F { | 409 class F { |
389 static final f1 = 1; | 410 static final f1 = 1; |
390 final f2 = 1; | 411 final f2 = 1; |
391 } | 412 } |
392 ''', | 413 ''', |
393 '/main.dart': ''' | 414 '/main.dart': ''' |
394 import "a.dart"; | 415 import "a.dart"; |
395 import "c.dart"; | 416 import "c.dart"; |
396 import "e.dart"; | 417 import "e.dart"; |
397 | 418 |
398 class D { | 419 class D { |
399 static final d1 = A.a1 + 1; | 420 static final d1 = A.a1 + 1; |
400 static final d2 = C.c1 + 1; | 421 static final d2 = C.c1 + 1; |
401 final d3 = new A().a2; | 422 final d3 = new A().a2; |
402 final d4 = new C().c2; | 423 final d4 = new C().c2; |
403 } | 424 } |
404 | 425 |
405 test1() { | 426 test1() { |
406 int x = 0; | 427 int x = 0; |
407 // inference in A works, it's not in a cycle | 428 // inference in A works, it's not in a cycle |
408 x = A.a1; | 429 x = A.a1; |
409 x = new A().a2; | 430 x = new A().a2; |
410 | 431 |
411 // inference here or in c.dart is disabled because of the cycle | 432 // Within a cycle we allow inference when the RHS is well known, but |
412 x = /*info:DownCast*/C.c1; | 433 // not when it depends on other fields within the cycle |
413 x = /*info:DownCast*/D.d1; | 434 x = C.c1; |
| 435 x = D.d1; |
414 x = /*info:DownCast*/D.d2; | 436 x = /*info:DownCast*/D.d2; |
415 x = /*info:DownCast*/new C().c2; | 437 x = new C().c2; |
416 x = /*info:DownCast*/new D().d3; | 438 x = new D().d3; |
417 x = /*info:DownCast*/new D().d4; | 439 x = /*info:DownCast*/new D().d4; |
418 | 440 |
419 | 441 |
420 // inference in e.dart and f.dart is disabled because they contains | 442 // Similarly if the library contains parts. |
421 // parts | 443 x = E.e1; |
422 x = /*info:DownCast*/E.e1; | 444 x = /*info:DownCast*/E.e2; |
423 x = /*info:DownCast*/new E().e2; | 445 x = E.e3; |
424 x = /*info:DownCast*/F.f1; | 446 x = new E().e4; |
425 x = /*info:DownCast*/new F().f2; | 447 x = /*info:DownCast*/new E().e5; |
| 448 x = new E().e6; |
| 449 x = F.f1; |
| 450 x = new F().f2; |
426 } | 451 } |
427 ''' | 452 ''' |
428 }, inferStaticsFromIdentifiers: true); | 453 }, inferStaticsFromIdentifiers: true); |
429 }); | 454 }); |
430 | 455 |
431 test('infer from complex expressions if the outer-most value is precise', () { | 456 test('infer from complex expressions if the outer-most value is precise', () { |
432 testChecker({ | 457 testChecker({ |
433 '/main.dart': ''' | 458 '/main.dart': ''' |
434 class A { int x; B operator+(other) {} } | 459 class A { int x; B operator+(other) {} } |
435 class B extends A { B(ignore); } | 460 class B extends A { B(ignore); } |
436 var a = new A(); | 461 var a = new A(); |
437 // Note: it doesn't matter that some of these refer to 'x'. | 462 // Note: it doesn't matter that some of these refer to 'x'. |
438 var b = new B(x); // allocations | 463 var b = new B(x); // allocations |
439 var c1 = [x]; // list literals | 464 var c1 = [x]; // list literals |
440 var c2 = const []; | 465 var c2 = const []; |
441 var d = {'a': 'b'}; // map literals | 466 var d = {'a': 'b'}; // map literals |
442 var e = new A()..x = 3; // cascades | 467 var e = new A()..x = 3; // cascades |
443 var f = 2 + 3; // binary expressions are OK if the left operand | 468 var f = 2 + 3; // binary expressions are OK if the left operand |
444 // is from a library in a different strongest | 469 // is from a library in a different strongest |
445 // conected component. | 470 // conected component. |
446 var g = -3; | 471 var g = -3; |
447 var h = new A() + 3; | 472 var h = new A() + 3; |
448 var i = - new A(); | 473 var i = - new A(); |
| 474 var j = null as B; |
449 | 475 |
450 test1() { | 476 test1() { |
451 a = /*severe:StaticTypeError*/"hi"; | 477 a = /*severe:StaticTypeError*/"hi"; |
452 a = new B(3); | 478 a = new B(3); |
453 b = /*severe:StaticTypeError*/"hi"; | 479 b = /*severe:StaticTypeError*/"hi"; |
454 b = new B(3); | 480 b = new B(3); |
455 c1 = []; | 481 c1 = []; |
456 c1 = /*severe:StaticTypeError*/{}; | 482 c1 = /*severe:StaticTypeError*/{}; |
457 c2 = []; | 483 c2 = []; |
458 c2 = /*severe:StaticTypeError*/{}; | 484 c2 = /*severe:StaticTypeError*/{}; |
459 d = {}; | 485 d = {}; |
460 d = /*severe:StaticTypeError*/3; | 486 d = /*severe:StaticTypeError*/3; |
461 e = new A(); | 487 e = new A(); |
462 e = /*severe:StaticTypeError*/{}; | 488 e = /*severe:StaticTypeError*/{}; |
463 f = 3; | 489 f = 3; |
464 f = /*severe:StaticTypeError*/false; | 490 f = /*severe:StaticTypeError*/false; |
465 g = 1; | 491 g = 1; |
466 g = /*severe:StaticTypeError*/false; | 492 g = /*severe:StaticTypeError*/false; |
467 h = /*severe:StaticTypeError*/false; | 493 h = /*severe:StaticTypeError*/false; |
468 h = new B(); | 494 h = new B(); |
469 i = false; | 495 i = false; |
| 496 j = new B(); |
| 497 j = /*severe:StaticTypeError*/false; |
| 498 j = /*severe:StaticTypeError*/[]; |
470 } | 499 } |
471 ''' | 500 ''' |
472 }); | 501 }); |
473 }); | 502 }); |
474 | 503 |
475 test('do not infer if complex expressions read possibly inferred field', () { | 504 test('do not infer if complex expressions read possibly inferred field', () { |
476 testChecker({ | 505 testChecker({ |
477 '/a.dart': ''' | 506 '/a.dart': ''' |
478 class A { | 507 class A { |
479 var x = 3; | 508 var x = 3; |
(...skipping 327 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
807 } | 836 } |
808 | 837 |
809 foo () { | 838 foo () { |
810 int y = /*severe:StaticTypeError*/new B().m(null, null); | 839 int y = /*severe:StaticTypeError*/new B().m(null, null); |
811 String z = new B().m(null, null); | 840 String z = new B().m(null, null); |
812 } | 841 } |
813 ''' | 842 ''' |
814 }, inferFromOverrides: true); | 843 }, inferFromOverrides: true); |
815 }); | 844 }); |
816 | 845 |
817 // TODO(sigmund): enable this test, it's currently flaky (see issue #48). | 846 test('infer type regardless of declaration order or cycles', () { |
818 skip_test('infer types on generic instantiations in library cycle', () { | 847 testChecker({ |
| 848 '/b.dart': ''' |
| 849 import 'main.dart'; |
| 850 |
| 851 class B extends A { } |
| 852 ''', |
| 853 '/main.dart': ''' |
| 854 import 'b.dart'; |
| 855 class C extends B { |
| 856 get x; |
| 857 } |
| 858 class A { |
| 859 int get x; |
| 860 } |
| 861 foo () { |
| 862 int y = new C().x; |
| 863 String y = /*severe:StaticTypeError*/new C().x; |
| 864 } |
| 865 ''' |
| 866 }, inferFromOverrides: true); |
| 867 }); |
| 868 |
| 869 // Note: this is a regression test for a non-deterministic behavior we used to |
| 870 // have with inference in library cycles. If you see this test flake out, |
| 871 // change `test` to `skip_test` and reopen bug #48. |
| 872 test('infer types on generic instantiations in library cycle', () { |
819 testChecker({ | 873 testChecker({ |
820 '/a.dart': ''' | 874 '/a.dart': ''' |
821 import 'main.dart'; | 875 import 'main.dart'; |
822 abstract class I<E> { | 876 abstract class I<E> { |
823 A<E> m(a, String f(v, T e)); | 877 A<E> m(a, String f(v, T e)); |
824 } | 878 } |
825 ''', | 879 ''', |
826 '/main.dart': ''' | 880 '/main.dart': ''' |
827 import 'a.dart'; | 881 import 'a.dart'; |
828 | 882 |
(...skipping 97 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
926 class C1 extends A implements B { | 980 class C1 extends A implements B { |
927 I3 get a => null; | 981 I3 get a => null; |
928 } | 982 } |
929 | 983 |
930 class C2 extends A implements B { | 984 class C2 extends A implements B { |
931 /*severe:InvalidMethodOverride*/get a => null; | 985 /*severe:InvalidMethodOverride*/get a => null; |
932 } | 986 } |
933 ''' | 987 ''' |
934 }, inferFromOverrides: true); | 988 }, inferFromOverrides: true); |
935 }); | 989 }); |
| 990 |
| 991 test('infer from RHS only if it wont conflict with overridden fields', () { |
| 992 testChecker({ |
| 993 '/main.dart': ''' |
| 994 class A { |
| 995 var x; |
| 996 } |
| 997 |
| 998 class B extends A { |
| 999 var x = 2; |
| 1000 } |
| 1001 |
| 1002 foo() { |
| 1003 String y = /*info:DownCast*/new B().x; |
| 1004 int z = /*info:DownCast*/new B().x; |
| 1005 } |
| 1006 ''' |
| 1007 }, inferFromOverrides: true); |
| 1008 |
| 1009 testChecker({ |
| 1010 '/main.dart': ''' |
| 1011 class A { |
| 1012 final x; |
| 1013 } |
| 1014 |
| 1015 class B extends A { |
| 1016 final x = 2; |
| 1017 } |
| 1018 |
| 1019 foo() { |
| 1020 String y = /*severe:StaticTypeError*/new B().x; |
| 1021 int z = new B().x; |
| 1022 } |
| 1023 ''' |
| 1024 }, inferFromOverrides: true); |
| 1025 }); |
| 1026 |
| 1027 test('infer correctly on multiple variables declared together', () { |
| 1028 testChecker({ |
| 1029 '/main.dart': ''' |
| 1030 class A { |
| 1031 var x, y = 2, z = "hi"; |
| 1032 } |
| 1033 |
| 1034 class B extends A { |
| 1035 var x = 2, y = 3, z, w = 2; |
| 1036 } |
| 1037 |
| 1038 foo() { |
| 1039 String s; |
| 1040 int i; |
| 1041 |
| 1042 s = /*info:DownCast*/new B().x; |
| 1043 s = /*severe:StaticTypeError*/new B().y; |
| 1044 s = new B().z; |
| 1045 s = /*severe:StaticTypeError*/new B().w; |
| 1046 |
| 1047 i = /*info:DownCast*/new B().x; |
| 1048 i = new B().y; |
| 1049 i = /*severe:StaticTypeError*/new B().z; |
| 1050 i = new B().w; |
| 1051 } |
| 1052 ''' |
| 1053 }, inferFromOverrides: true); |
| 1054 }); |
936 } | 1055 } |
OLD | NEW |