OLD | NEW |
| (Empty) |
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 | |
3 // BSD-style license that can be found in the LICENSE file. | |
4 | |
5 /// Tests for type inference. | |
6 library dev_compiler.test.inferred_type_test; | |
7 | |
8 import 'package:test/test.dart'; | |
9 | |
10 import '../testing.dart'; | |
11 | |
12 void main() { | |
13 // Error also expected when declared type is `int`. | |
14 testChecker('infer type on var', { | |
15 '/main.dart': ''' | |
16 test1() { | |
17 int x = 3; | |
18 x = /*severe:StaticTypeError*/"hi"; | |
19 } | |
20 ''' | |
21 }); | |
22 | |
23 // If inferred type is `int`, error is also reported | |
24 testChecker('infer type on var 2', { | |
25 '/main.dart': ''' | |
26 test2() { | |
27 var x = 3; | |
28 x = /*severe:StaticTypeError*/"hi"; | |
29 } | |
30 ''' | |
31 }); | |
32 | |
33 testChecker('No error when declared type is `num` and assigned null.', { | |
34 '/main.dart': ''' | |
35 test1() { | |
36 num x = 3; | |
37 x = null; | |
38 } | |
39 ''' | |
40 }); | |
41 | |
42 testChecker('do not infer type on dynamic', { | |
43 '/main.dart': ''' | |
44 test() { | |
45 dynamic x = 3; | |
46 x = "hi"; | |
47 } | |
48 ''' | |
49 }); | |
50 | |
51 testChecker('do not infer type when initializer is null', { | |
52 '/main.dart': ''' | |
53 test() { | |
54 var x = null; | |
55 x = "hi"; | |
56 x = 3; | |
57 } | |
58 ''' | |
59 }); | |
60 | |
61 testChecker('infer type on var from field', { | |
62 '/main.dart': ''' | |
63 class A { | |
64 int x = 0; | |
65 | |
66 test1() { | |
67 var a = x; | |
68 a = /*severe:StaticTypeError*/"hi"; | |
69 a = 3; | |
70 var b = y; | |
71 b = /*severe:StaticTypeError*/"hi"; | |
72 b = 4; | |
73 var c = z; | |
74 c = /*severe:StaticTypeError*/"hi"; | |
75 c = 4; | |
76 } | |
77 | |
78 int y; // field def after use | |
79 final z = 42; // should infer `int` | |
80 } | |
81 ''' | |
82 }); | |
83 | |
84 testChecker('infer type on var from top-level', { | |
85 '/main.dart': ''' | |
86 int x = 0; | |
87 | |
88 test1() { | |
89 var a = x; | |
90 a = /*severe:StaticTypeError*/"hi"; | |
91 a = 3; | |
92 var b = y; | |
93 b = /*severe:StaticTypeError*/"hi"; | |
94 b = 4; | |
95 var c = z; | |
96 c = /*severe:StaticTypeError*/"hi"; | |
97 c = 4; | |
98 } | |
99 | |
100 int y = 0; // field def after use | |
101 final z = 42; // should infer `int` | |
102 ''' | |
103 }); | |
104 | |
105 testChecker('do not infer field type when initializer is null', { | |
106 '/main.dart': ''' | |
107 var x = null; | |
108 var y = 3; | |
109 class A { | |
110 static var x = null; | |
111 static var y = 3; | |
112 | |
113 var x2 = null; | |
114 var y2 = 3; | |
115 } | |
116 | |
117 test() { | |
118 x = "hi"; | |
119 y = /*severe:StaticTypeError*/"hi"; | |
120 A.x = "hi"; | |
121 A.y = /*severe:StaticTypeError*/"hi"; | |
122 new A().x2 = "hi"; | |
123 new A().y2 = /*severe:StaticTypeError*/"hi"; | |
124 } | |
125 ''' | |
126 }); | |
127 | |
128 testChecker('infer from variables in non-cycle imports with flag', { | |
129 '/a.dart': ''' | |
130 var x = 2; | |
131 ''', | |
132 '/main.dart': ''' | |
133 import 'a.dart'; | |
134 var y = x; | |
135 | |
136 test1() { | |
137 x = /*severe:StaticTypeError*/"hi"; | |
138 y = /*severe:StaticTypeError*/"hi"; | |
139 } | |
140 ''' | |
141 }); | |
142 | |
143 testChecker('infer from variables in non-cycle imports with flag 2', { | |
144 '/a.dart': ''' | |
145 class A { static var x = 2; } | |
146 ''', | |
147 '/main.dart': ''' | |
148 import 'a.dart'; | |
149 class B { static var y = A.x; } | |
150 | |
151 test1() { | |
152 A.x = /*severe:StaticTypeError*/"hi"; | |
153 B.y = /*severe:StaticTypeError*/"hi"; | |
154 } | |
155 ''' | |
156 }); | |
157 | |
158 testChecker('infer from variables in cycle libs when flag is on', { | |
159 '/a.dart': ''' | |
160 import 'main.dart'; | |
161 var x = 2; // ok to infer | |
162 ''', | |
163 '/main.dart': ''' | |
164 import 'a.dart'; | |
165 var y = x; // now ok :) | |
166 | |
167 test1() { | |
168 int t = 3; | |
169 t = x; | |
170 t = y; | |
171 } | |
172 ''' | |
173 }); | |
174 | |
175 testChecker('infer from variables in cycle libs when flag is on 2', { | |
176 '/a.dart': ''' | |
177 import 'main.dart'; | |
178 class A { static var x = 2; } | |
179 ''', | |
180 '/main.dart': ''' | |
181 import 'a.dart'; | |
182 class B { static var y = A.x; } | |
183 | |
184 test1() { | |
185 int t = 3; | |
186 t = A.x; | |
187 t = B.y; | |
188 } | |
189 ''' | |
190 }); | |
191 | |
192 testChecker('can infer also from static and instance fields (flag on)', { | |
193 '/a.dart': ''' | |
194 import 'b.dart'; | |
195 class A { | |
196 static final a1 = B.b1; | |
197 final a2 = new B().b2; | |
198 } | |
199 ''', | |
200 '/b.dart': ''' | |
201 class B { | |
202 static final b1 = 1; | |
203 final b2 = 1; | |
204 } | |
205 ''', | |
206 '/main.dart': ''' | |
207 import "a.dart"; | |
208 | |
209 test1() { | |
210 int x = 0; | |
211 // inference in A now works. | |
212 x = A.a1; | |
213 x = new A().a2; | |
214 } | |
215 ''' | |
216 }); | |
217 | |
218 testChecker('inference in cycles is deterministic', { | |
219 '/a.dart': ''' | |
220 import 'b.dart'; | |
221 class A { | |
222 static final a1 = B.b1; | |
223 final a2 = new B().b2; | |
224 } | |
225 ''', | |
226 '/b.dart': ''' | |
227 class B { | |
228 static final b1 = 1; | |
229 final b2 = 1; | |
230 } | |
231 ''', | |
232 '/c.dart': ''' | |
233 import "main.dart"; // creates a cycle | |
234 | |
235 class C { | |
236 static final c1 = 1; | |
237 final c2 = 1; | |
238 } | |
239 ''', | |
240 '/e.dart': ''' | |
241 import 'a.dart'; | |
242 part 'e2.dart'; | |
243 | |
244 class E { | |
245 static final e1 = 1; | |
246 static final e2 = F.f1; | |
247 static final e3 = A.a1; | |
248 final e4 = 1; | |
249 final e5 = new F().f2; | |
250 final e6 = new A().a2; | |
251 } | |
252 ''', | |
253 '/f.dart': ''' | |
254 part 'f2.dart'; | |
255 ''', | |
256 '/e2.dart': ''' | |
257 class F { | |
258 static final f1 = 1; | |
259 final f2 = 1; | |
260 } | |
261 ''', | |
262 '/main.dart': ''' | |
263 import "a.dart"; | |
264 import "c.dart"; | |
265 import "e.dart"; | |
266 | |
267 class D { | |
268 static final d1 = A.a1 + 1; | |
269 static final d2 = C.c1 + 1; | |
270 final d3 = new A().a2; | |
271 final d4 = new C().c2; | |
272 } | |
273 | |
274 test1() { | |
275 int x = 0; | |
276 // inference in A works, it's not in a cycle | |
277 x = A.a1; | |
278 x = new A().a2; | |
279 | |
280 // Within a cycle we allow inference when the RHS is well known, but | |
281 // not when it depends on other fields within the cycle | |
282 x = C.c1; | |
283 x = D.d1; | |
284 x = D.d2; | |
285 x = new C().c2; | |
286 x = new D().d3; | |
287 x = /*info:DynamicCast*/new D().d4; | |
288 | |
289 | |
290 // Similarly if the library contains parts. | |
291 x = E.e1; | |
292 x = E.e2; | |
293 x = E.e3; | |
294 x = new E().e4; | |
295 x = /*info:DynamicCast*/new E().e5; | |
296 x = new E().e6; | |
297 x = F.f1; | |
298 x = new F().f2; | |
299 } | |
300 ''' | |
301 }); | |
302 | |
303 testChecker( | |
304 'infer from complex expressions if the outer-most value is precise', { | |
305 '/main.dart': ''' | |
306 class A { int x; B operator+(other) {} } | |
307 class B extends A { B(ignore); } | |
308 var a = new A(); | |
309 // Note: it doesn't matter that some of these refer to 'x'. | |
310 var b = new B(x); // allocations | |
311 var c1 = [x]; // list literals | |
312 var c2 = const []; | |
313 var d = {'a': 'b'}; // map literals | |
314 var e = new A()..x = 3; // cascades | |
315 var f = 2 + 3; // binary expressions are OK if the left operand | |
316 // is from a library in a different strongest | |
317 // conected component. | |
318 var g = -3; | |
319 var h = new A() + 3; | |
320 var i = - new A(); | |
321 var j = null as B; | |
322 | |
323 test1() { | |
324 a = /*severe:StaticTypeError*/"hi"; | |
325 a = new B(3); | |
326 b = /*severe:StaticTypeError*/"hi"; | |
327 b = new B(3); | |
328 c1 = []; | |
329 c1 = /*severe:StaticTypeError*/{}; | |
330 c2 = []; | |
331 c2 = /*severe:StaticTypeError*/{}; | |
332 d = {}; | |
333 d = /*severe:StaticTypeError*/3; | |
334 e = new A(); | |
335 e = /*severe:StaticTypeError*/{}; | |
336 f = 3; | |
337 f = /*severe:StaticTypeError*/false; | |
338 g = 1; | |
339 g = /*severe:StaticTypeError*/false; | |
340 h = /*severe:StaticTypeError*/false; | |
341 h = new B(); | |
342 i = false; | |
343 j = new B(); | |
344 j = /*severe:StaticTypeError*/false; | |
345 j = /*severe:StaticTypeError*/[]; | |
346 } | |
347 ''' | |
348 }); | |
349 | |
350 // but flags can enable this behavior. | |
351 testChecker('infer if complex expressions read possibly inferred field', { | |
352 '/a.dart': ''' | |
353 class A { | |
354 var x = 3; | |
355 } | |
356 ''', | |
357 '/main.dart': ''' | |
358 import 'a.dart'; | |
359 class B { | |
360 var y = 3; | |
361 } | |
362 final t1 = new A(); | |
363 final t2 = new A().x; | |
364 final t3 = new B(); | |
365 final t4 = new B().y; | |
366 | |
367 test1() { | |
368 int i = 0; | |
369 A a; | |
370 B b; | |
371 a = t1; | |
372 i = t2; | |
373 b = t3; | |
374 i = /*info:DynamicCast*/t4; | |
375 i = new B().y; // B.y was inferred though | |
376 } | |
377 ''' | |
378 }); | |
379 | |
380 group('infer types on loop indices', () { | |
381 testChecker('foreach loop', { | |
382 '/main.dart': ''' | |
383 class Foo { | |
384 int bar = 42; | |
385 } | |
386 | |
387 test() { | |
388 var list = <Foo>[]; | |
389 for (var x in list) { | |
390 String y = /*severe:StaticTypeError*/x; | |
391 } | |
392 | |
393 for (dynamic x in list) { | |
394 String y = /*info:DynamicCast*/x; | |
395 } | |
396 | |
397 for (String x in /*severe:StaticTypeError*/list) { | |
398 String y = x; | |
399 } | |
400 | |
401 var z; | |
402 for(z in list) { | |
403 String y = /*info:DynamicCast*/z; | |
404 } | |
405 | |
406 Iterable iter = list; | |
407 for (Foo x in /*warning:DownCastComposite*/iter) { | |
408 var y = x; | |
409 } | |
410 | |
411 dynamic iter2 = list; | |
412 for (Foo x in /*warning:DownCastComposite*/iter2) { | |
413 var y = x; | |
414 } | |
415 | |
416 var map = <String, Foo>{}; | |
417 // Error: map must be an Iterable. | |
418 for (var x in /*severe:StaticTypeError*/map) { | |
419 String y = /*info:DynamicCast*/x; | |
420 } | |
421 | |
422 // We're not properly inferring that map.keys is an Iterable<String> | |
423 // and that x is a String. | |
424 for (var x in map.keys) { | |
425 String y = x; | |
426 } | |
427 } | |
428 ''' | |
429 }); | |
430 | |
431 testChecker('for loop, with inference', { | |
432 '/main.dart': ''' | |
433 test() { | |
434 for (var i = 0; i < 10; i++) { | |
435 int j = i + 1; | |
436 } | |
437 } | |
438 ''' | |
439 }); | |
440 }); | |
441 | |
442 testChecker('propagate inference to field in class', { | |
443 '/main.dart': ''' | |
444 class A { | |
445 int x = 2; | |
446 } | |
447 | |
448 test() { | |
449 var a = new A(); | |
450 A b = a; // doesn't require down cast | |
451 print(a.x); // doesn't require dynamic invoke | |
452 print(a.x + 2); // ok to use in bigger expression | |
453 } | |
454 ''' | |
455 }); | |
456 | |
457 testChecker('propagate inference to field in class dynamic warnings', { | |
458 '/main.dart': ''' | |
459 class A { | |
460 int x = 2; | |
461 } | |
462 | |
463 test() { | |
464 dynamic a = new A(); | |
465 A b = /*info:DynamicCast*/a; | |
466 print(/*info:DynamicInvoke*/a.x); | |
467 print(/*info:DynamicInvoke*/(/*info:DynamicInvoke*/a.x) + 2); | |
468 } | |
469 ''' | |
470 }); | |
471 | |
472 testChecker('propagate inference transitively', { | |
473 '/main.dart': ''' | |
474 class A { | |
475 int x = 2; | |
476 } | |
477 | |
478 test5() { | |
479 var a1 = new A(); | |
480 a1.x = /*severe:StaticTypeError*/"hi"; | |
481 | |
482 A a2 = new A(); | |
483 a2.x = /*severe:StaticTypeError*/"hi"; | |
484 } | |
485 ''' | |
486 }); | |
487 | |
488 testChecker('propagate inference transitively 2', { | |
489 '/main.dart': ''' | |
490 class A { | |
491 int x = 42; | |
492 } | |
493 | |
494 class B { | |
495 A a = new A(); | |
496 } | |
497 | |
498 class C { | |
499 B b = new B(); | |
500 } | |
501 | |
502 class D { | |
503 C c = new C(); | |
504 } | |
505 | |
506 void main() { | |
507 var d1 = new D(); | |
508 print(d1.c.b.a.x); | |
509 | |
510 D d2 = new D(); | |
511 print(d2.c.b.a.x); | |
512 } | |
513 ''' | |
514 }); | |
515 | |
516 group('infer type on overridden fields', () { | |
517 testChecker('2', { | |
518 '/main.dart': ''' | |
519 class A { | |
520 int x = 2; | |
521 } | |
522 | |
523 class B extends A { | |
524 get x => 3; | |
525 } | |
526 | |
527 foo() { | |
528 String y = /*severe:StaticTypeError*/new B().x; | |
529 int z = new B().x; | |
530 } | |
531 ''' | |
532 }); | |
533 | |
534 testChecker('4', { | |
535 '/main.dart': ''' | |
536 class A { | |
537 int x = 2; | |
538 } | |
539 | |
540 class B implements A { | |
541 get x => 3; | |
542 } | |
543 | |
544 foo() { | |
545 String y = /*severe:StaticTypeError*/new B().x; | |
546 int z = new B().x; | |
547 } | |
548 ''' | |
549 }); | |
550 }); | |
551 | |
552 group('infer types on generic instantiations', () { | |
553 testChecker('infer', { | |
554 '/main.dart': ''' | |
555 class A<T> { | |
556 T x; | |
557 } | |
558 | |
559 class B implements A<int> { | |
560 /*severe:InvalidMethodOverride*/dynamic get x => 3; | |
561 } | |
562 | |
563 foo() { | |
564 String y = /*info:DynamicCast*/new B().x; | |
565 int z = /*info:DynamicCast*/new B().x; | |
566 } | |
567 ''' | |
568 }); | |
569 | |
570 testChecker('3', { | |
571 '/main.dart': ''' | |
572 class A<T> { | |
573 T x; | |
574 T w; | |
575 } | |
576 | |
577 class B implements A<int> { | |
578 get x => 3; | |
579 get w => /*severe:StaticTypeError*/"hello"; | |
580 } | |
581 | |
582 foo() { | |
583 String y = /*severe:StaticTypeError*/new B().x; | |
584 int z = new B().x; | |
585 } | |
586 ''' | |
587 }); | |
588 | |
589 testChecker('4', { | |
590 '/main.dart': ''' | |
591 class A<T> { | |
592 T x; | |
593 } | |
594 | |
595 class B<E> extends A<E> { | |
596 E y; | |
597 get x => y; | |
598 } | |
599 | |
600 foo() { | |
601 int y = /*severe:StaticTypeError*/new B<String>().x; | |
602 String z = new B<String>().x; | |
603 } | |
604 ''' | |
605 }); | |
606 | |
607 testChecker('5', { | |
608 '/main.dart': ''' | |
609 abstract class I<E> { | |
610 String m(a, String f(v, T e)); | |
611 } | |
612 | |
613 abstract class A<E> implements I<E> { | |
614 const A(); | |
615 String m(a, String f(v, T e)); | |
616 } | |
617 | |
618 abstract class M { | |
619 int y; | |
620 } | |
621 | |
622 class B<E> extends A<E> implements M { | |
623 const B(); | |
624 int get y => 0; | |
625 | |
626 m(a, f(v, T e)) {} | |
627 } | |
628 | |
629 foo () { | |
630 int y = /*severe:StaticTypeError*/new B().m(null, null); | |
631 String z = new B().m(null, null); | |
632 } | |
633 ''' | |
634 }); | |
635 }); | |
636 | |
637 testChecker('infer type regardless of declaration order or cycles', { | |
638 '/b.dart': ''' | |
639 import 'main.dart'; | |
640 | |
641 class B extends A { } | |
642 ''', | |
643 '/main.dart': ''' | |
644 import 'b.dart'; | |
645 class C extends B { | |
646 get x; | |
647 } | |
648 class A { | |
649 int get x; | |
650 } | |
651 foo () { | |
652 int y = new C().x; | |
653 String y = /*severe:StaticTypeError*/new C().x; | |
654 } | |
655 ''' | |
656 }); | |
657 | |
658 // Note: this is a regression test for a non-deterministic behavior we used to | |
659 // have with inference in library cycles. If you see this test flake out, | |
660 // change `test` to `skip_test` and reopen bug #48. | |
661 testChecker('infer types on generic instantiations in library cycle', { | |
662 '/a.dart': ''' | |
663 import 'main.dart'; | |
664 abstract class I<E> { | |
665 A<E> m(a, String f(v, int e)); | |
666 } | |
667 ''', | |
668 '/main.dart': ''' | |
669 import 'a.dart'; | |
670 | |
671 abstract class A<E> implements I<E> { | |
672 const A(); | |
673 | |
674 E value; | |
675 } | |
676 | |
677 abstract class M { | |
678 int y; | |
679 } | |
680 | |
681 class B<E> extends A<E> implements M { | |
682 const B(); | |
683 int get y => 0; | |
684 | |
685 m(a, f(v, int e)) {} | |
686 } | |
687 | |
688 foo () { | |
689 int y = /*severe:StaticTypeError*/new B<String>().m(null, null).value; | |
690 String z = new B<String>().m(null, null).value; | |
691 } | |
692 ''' | |
693 }); | |
694 | |
695 group('do not infer overridden fields that explicitly say dynamic', () { | |
696 testChecker('infer', { | |
697 '/main.dart': ''' | |
698 class A { | |
699 int x = 2; | |
700 } | |
701 | |
702 class B implements A { | |
703 /*severe:InvalidMethodOverride*/dynamic get x => 3; | |
704 } | |
705 | |
706 foo() { | |
707 String y = /*info:DynamicCast*/new B().x; | |
708 int z = /*info:DynamicCast*/new B().x; | |
709 } | |
710 ''' | |
711 }); | |
712 }); | |
713 | |
714 testChecker('conflicts can happen', { | |
715 '/main.dart': ''' | |
716 class I1 { | |
717 int x; | |
718 } | |
719 class I2 extends I1 { | |
720 int y; | |
721 } | |
722 | |
723 class A { | |
724 final I1 a; | |
725 } | |
726 | |
727 class B { | |
728 final I2 a; | |
729 } | |
730 | |
731 class C1 extends A implements B { | |
732 /*severe:InvalidMethodOverride,severe:InvalidMethodOverride*/get a =>
null; | |
733 } | |
734 | |
735 // Still ambiguous | |
736 class C2 extends B implements A { | |
737 /*severe:InvalidMethodOverride,severe:InvalidMethodOverride*/get a =>
null; | |
738 } | |
739 ''' | |
740 }); | |
741 | |
742 testChecker('conflicts can happen 2', { | |
743 '/main.dart': ''' | |
744 class I1 { | |
745 int x; | |
746 } | |
747 class I2 { | |
748 int y; | |
749 } | |
750 | |
751 class I3 implements I1, I2 { | |
752 int x; | |
753 int y; | |
754 } | |
755 | |
756 class A { | |
757 final I1 a; | |
758 } | |
759 | |
760 class B { | |
761 final I2 a; | |
762 } | |
763 | |
764 class C1 extends A implements B { | |
765 I3 get a => null; | |
766 } | |
767 | |
768 class C2 extends A implements B { | |
769 /*severe:InvalidMethodOverride,severe:InvalidMethodOverride*/get a =>
null; | |
770 } | |
771 ''' | |
772 }); | |
773 | |
774 testChecker( | |
775 'infer from RHS only if it wont conflict with overridden fields', { | |
776 '/main.dart': ''' | |
777 class A { | |
778 var x; | |
779 } | |
780 | |
781 class B extends A { | |
782 var x = 2; | |
783 } | |
784 | |
785 foo() { | |
786 String y = /*info:DynamicCast*/new B().x; | |
787 int z = /*info:DynamicCast*/new B().x; | |
788 } | |
789 ''' | |
790 }); | |
791 | |
792 testChecker( | |
793 'infer from RHS only if it wont conflict with overridden fields 2', { | |
794 '/main.dart': ''' | |
795 class A { | |
796 final x; | |
797 } | |
798 | |
799 class B extends A { | |
800 final x = 2; | |
801 } | |
802 | |
803 foo() { | |
804 String y = /*severe:StaticTypeError*/new B().x; | |
805 int z = new B().x; | |
806 } | |
807 ''' | |
808 }); | |
809 | |
810 testChecker('infer correctly on multiple variables declared together', { | |
811 '/main.dart': ''' | |
812 class A { | |
813 var x, y = 2, z = "hi"; | |
814 } | |
815 | |
816 class B extends A { | |
817 var x = 2, y = 3, z, w = 2; | |
818 } | |
819 | |
820 foo() { | |
821 String s; | |
822 int i; | |
823 | |
824 s = /*info:DynamicCast*/new B().x; | |
825 s = /*severe:StaticTypeError*/new B().y; | |
826 s = new B().z; | |
827 s = /*severe:StaticTypeError*/new B().w; | |
828 | |
829 i = /*info:DynamicCast*/new B().x; | |
830 i = new B().y; | |
831 i = /*severe:StaticTypeError*/new B().z; | |
832 i = new B().w; | |
833 } | |
834 ''' | |
835 }); | |
836 | |
837 testChecker('infer consts transitively', { | |
838 '/b.dart': ''' | |
839 const b1 = 2; | |
840 ''', | |
841 '/a.dart': ''' | |
842 import 'main.dart'; | |
843 import 'b.dart'; | |
844 const a1 = m2; | |
845 const a2 = b1; | |
846 ''', | |
847 '/main.dart': ''' | |
848 import 'a.dart'; | |
849 const m1 = a1; | |
850 const m2 = a2; | |
851 | |
852 foo() { | |
853 int i; | |
854 i = m1; | |
855 } | |
856 ''' | |
857 }); | |
858 | |
859 testChecker('infer statics transitively', { | |
860 '/b.dart': ''' | |
861 final b1 = 2; | |
862 ''', | |
863 '/a.dart': ''' | |
864 import 'main.dart'; | |
865 import 'b.dart'; | |
866 final a1 = m2; | |
867 class A { | |
868 static final a2 = b1; | |
869 } | |
870 ''', | |
871 '/main.dart': ''' | |
872 import 'a.dart'; | |
873 final m1 = a1; | |
874 final m2 = A.a2; | |
875 | |
876 foo() { | |
877 int i; | |
878 i = m1; | |
879 } | |
880 ''' | |
881 }); | |
882 | |
883 testChecker('infer statics transitively 2', { | |
884 '/main.dart': ''' | |
885 const x1 = 1; | |
886 final x2 = 1; | |
887 final y1 = x1; | |
888 final y2 = x2; | |
889 | |
890 foo() { | |
891 int i; | |
892 i = y1; | |
893 i = y2; | |
894 } | |
895 ''' | |
896 }); | |
897 | |
898 testChecker('infer statics transitively 3', { | |
899 '/a.dart': ''' | |
900 const a1 = 3; | |
901 const a2 = 4; | |
902 class A { | |
903 a3; | |
904 } | |
905 ''', | |
906 '/main.dart': ''' | |
907 import 'a.dart' show a1, A; | |
908 import 'a.dart' as p show a2, A; | |
909 const t1 = 1; | |
910 const t2 = t1; | |
911 const t3 = a1; | |
912 const t4 = p.a2; | |
913 const t5 = A.a3; | |
914 const t6 = p.A.a3; | |
915 | |
916 foo() { | |
917 int i; | |
918 i = t1; | |
919 i = t2; | |
920 i = t3; | |
921 i = t4; | |
922 } | |
923 ''' | |
924 }); | |
925 | |
926 testChecker('infer statics with method invocations', { | |
927 '/a.dart': ''' | |
928 m3(String a, String b, [a1,a2]) {} | |
929 ''', | |
930 '/main.dart': ''' | |
931 import 'a.dart'; | |
932 class T { | |
933 static final T foo = m1(m2(m3('', ''))); | |
934 static T m1(String m) { return null; } | |
935 static String m2(e) { return ''; } | |
936 } | |
937 | |
938 | |
939 ''' | |
940 }); | |
941 | |
942 testChecker('downwards inference: miscellaneous', { | |
943 '/main.dart': ''' | |
944 typedef (T x); | |
945 class A<T> { | |
946 Function2<T> x; | |
947 A(this.x); | |
948 } | |
949 void main() { | |
950 { // Variables, nested literals | |
951 var x = "hello"; | |
952 var y = 3; | |
953 void f(List<Map<int, String>> l) {}; | |
954 f(/*info:InferredTypeLiteral*/[{y: x}]); | |
955 } | |
956 { | |
957 int f(int x) {}; | |
958 A<int> a = /*info:InferredTypeAllocation*/new A(f); | |
959 } | |
960 } | |
961 ''' | |
962 }); | |
963 | |
964 group('downwards inference on instance creations', () { | |
965 String info = 'info:InferredTypeAllocation'; | |
966 String code = ''' | |
967 class A<S, T> { | |
968 S x; | |
969 T y; | |
970 A(this.x, this.y); | |
971 A.named(this.x, this.y); | |
972 } | |
973 | |
974 class B<S, T> extends A<T, S> { | |
975 B(S y, T x) : super(x, y); | |
976 B.named(S y, T x) : super.named(x, y); | |
977 } | |
978 | |
979 class C<S> extends B<S, S> { | |
980 C(S a) : super(a, a); | |
981 C.named(S a) : super.named(a, a); | |
982 } | |
983 | |
984 class D<S, T> extends B<T, int> { | |
985 D(T a) : super(a, 3); | |
986 D.named(T a) : super.named(a, 3); | |
987 } | |
988 | |
989 class E<S, T> extends A<C<S>, T> { | |
990 E(T a) : super(null, a); | |
991 } | |
992 | |
993 class F<S, T> extends A<S, T> { | |
994 F(S x, T y, {List<S> a, List<T> b}) : super(x, y); | |
995 F.named(S x, T y, [S a, T b]) : super(a, b); | |
996 } | |
997 | |
998 void main() { | |
999 { | |
1000 A<int, String> a0 = /*$info*/new A(3, "hello"); | |
1001 A<int, String> a1 = /*$info*/new A.named(3, "hello"); | |
1002 A<int, String> a2 = new A<int, String>(3, "hello"); | |
1003 A<int, String> a3 = new A<int, String>.named(3, "hello"); | |
1004 A<int, String> a4 = /*severe:StaticTypeError*/new A<int, dynamic>(3, "
hello"); | |
1005 A<int, String> a5 = /*severe:StaticTypeError*/new A<dynamic, dynamic>.
named(3, "hello"); | |
1006 } | |
1007 { | |
1008 A<int, String> a0 = /*severe:StaticTypeError*/new A("hello", 3); | |
1009 A<int, String> a1 = /*severe:StaticTypeError*/new A.named("hello", 3); | |
1010 } | |
1011 { | |
1012 A<int, String> a0 = /*$info*/new B("hello", 3); | |
1013 A<int, String> a1 = /*$info*/new B.named("hello", 3); | |
1014 A<int, String> a2 = new B<String, int>("hello", 3); | |
1015 A<int, String> a3 = new B<String, int>.named("hello", 3); | |
1016 A<int, String> a4 = /*severe:StaticTypeError*/new B<String, dynamic>("
hello", 3); | |
1017 A<int, String> a5 = /*severe:StaticTypeError*/new B<dynamic, dynamic>.
named("hello", 3); | |
1018 } | |
1019 { | |
1020 A<int, String> a0 = /*severe:StaticTypeError*/new B(3, "hello"); | |
1021 A<int, String> a1 = /*severe:StaticTypeError*/new B.named(3, "hello"); | |
1022 } | |
1023 { | |
1024 A<int, int> a0 = /*$info*/new C(3); | |
1025 A<int, int> a1 = /*$info*/new C.named(3); | |
1026 A<int, int> a2 = new C<int>(3); | |
1027 A<int, int> a3 = new C<int>.named(3); | |
1028 A<int, int> a4 = /*severe:StaticTypeError*/new C<dynamic>(3); | |
1029 A<int, int> a5 = /*severe:StaticTypeError*/new C<dynamic>.named(3); | |
1030 } | |
1031 { | |
1032 A<int, int> a0 = /*severe:StaticTypeError*/new C("hello"); | |
1033 A<int, int> a1 = /*severe:StaticTypeError*/new C.named("hello"); | |
1034 } | |
1035 { | |
1036 A<int, String> a0 = /*$info*/new D("hello"); | |
1037 A<int, String> a1 = /*$info*/new D.named("hello"); | |
1038 A<int, String> a2 = new D<int, String>("hello"); | |
1039 A<int, String> a3 = new D<String, String>.named("hello"); | |
1040 A<int, String> a4 = /*severe:StaticTypeError*/new D<num, dynamic>("hel
lo"); | |
1041 A<int, String> a5 = /*severe:StaticTypeError*/new D<dynamic, dynamic>.
named("hello"); | |
1042 } | |
1043 { | |
1044 A<int, String> a0 = /*severe:StaticTypeError*/new D(3); | |
1045 A<int, String> a1 = /*severe:StaticTypeError*/new D.named(3); | |
1046 } | |
1047 { // Currently we only allow variable constraints. Test that we reject. | |
1048 A<C<int>, String> a0 = /*severe:StaticTypeError*/new E("hello"); | |
1049 } | |
1050 { // Check named and optional arguments | |
1051 A<int, String> a0 = /*$info*/new F(3, "hello", a: [3], b: ["hello"]); | |
1052 A<int, String> a1 = /*severe:StaticTypeError*/new F(3, "hello", a: ["h
ello"], b:[3]); | |
1053 A<int, String> a2 = /*$info*/new F.named(3, "hello", 3, "hello"); | |
1054 A<int, String> a3 = /*$info*/new F.named(3, "hello"); | |
1055 A<int, String> a4 = /*severe:StaticTypeError*/new F.named(3, "hello",
"hello", 3); | |
1056 A<int, String> a5 = /*severe:StaticTypeError*/new F.named(3, "hello",
"hello"); | |
1057 } | |
1058 } | |
1059 '''; | |
1060 testChecker('infer downwards', {'/main.dart': code}); | |
1061 }); | |
1062 | |
1063 group('downwards inference on list literals', () { | |
1064 String info = "info:InferredTypeLiteral"; | |
1065 String code = ''' | |
1066 void foo([List<String> list1 = /*$info*/const [], | |
1067 List<String> list2 = /*severe:StaticTypeError*/const [42]]) { | |
1068 } | |
1069 | |
1070 void main() { | |
1071 { | |
1072 List<int> l0 = /*$info*/[]; | |
1073 List<int> l1 = /*$info*/[3]; | |
1074 List<int> l2 = /*severe:StaticTypeError*/["hello"]; | |
1075 List<int> l3 = /*severe:StaticTypeError*/["hello", 3]; | |
1076 } | |
1077 { | |
1078 List<dynamic> l0 = []; | |
1079 List<dynamic> l1 = [3]; | |
1080 List<dynamic> l2 = ["hello"]; | |
1081 List<dynamic> l3 = ["hello", 3]; | |
1082 } | |
1083 { | |
1084 List<int> l0 = /*severe:StaticTypeError*/<num>[]; | |
1085 List<int> l1 = /*severe:StaticTypeError*/<num>[3]; | |
1086 List<int> l2 = /*severe:StaticTypeError*/<num>[/*severe:StaticTypeErro
r*/"hello"]; | |
1087 List<int> l3 = /*severe:StaticTypeError*/<num>[/*severe:StaticTypeErro
r*/"hello", 3]; | |
1088 } | |
1089 { | |
1090 Iterable<int> i0 = /*$info*/[]; | |
1091 Iterable<int> i1 = /*$info*/[3]; | |
1092 Iterable<int> i2 = /*severe:StaticTypeError*/["hello"]; | |
1093 Iterable<int> i3 = /*severe:StaticTypeError*/["hello", 3]; | |
1094 } | |
1095 { | |
1096 const List<int> c0 = /*$info*/const []; | |
1097 const List<int> c1 = /*$info*/const [3]; | |
1098 const List<int> c2 = /*severe:StaticTypeError*/const ["hello"]; | |
1099 const List<int> c3 = /*severe:StaticTypeError*/const ["hello", 3]; | |
1100 } | |
1101 } | |
1102 '''; | |
1103 testChecker('infer downwards', {'/main.dart': code}); | |
1104 }); | |
1105 | |
1106 group('downwards inference on function arguments', () { | |
1107 String info = "info:InferredTypeLiteral"; | |
1108 String code = ''' | |
1109 void f0(List<int> a) {}; | |
1110 void f1({List<int> a}) {}; | |
1111 void f2(Iterable<int> a) {}; | |
1112 void f3(Iterable<Iterable<int>> a) {}; | |
1113 void f4({Iterable<Iterable<int>> a}) {}; | |
1114 void main() { | |
1115 f0(/*$info*/[]); | |
1116 f0(/*$info*/[3]); | |
1117 f0(/*severe:StaticTypeError*/["hello"]); | |
1118 f0(/*severe:StaticTypeError*/["hello", 3]); | |
1119 | |
1120 f1(a: /*$info*/[]); | |
1121 f1(a: /*$info*/[3]); | |
1122 f1(a: /*severe:StaticTypeError*/["hello"]); | |
1123 f1(a: /*severe:StaticTypeError*/["hello", 3]); | |
1124 | |
1125 f2(/*$info*/[]); | |
1126 f2(/*$info*/[3]); | |
1127 f2(/*severe:StaticTypeError*/["hello"]); | |
1128 f2(/*severe:StaticTypeError*/["hello", 3]); | |
1129 | |
1130 f3(/*$info*/[]); | |
1131 f3(/*$info*/[[3]]); | |
1132 f3(/*severe:StaticTypeError*/[["hello"]]); | |
1133 f3(/*severe:StaticTypeError*/[["hello"], [3]]); | |
1134 | |
1135 f4(a: /*$info*/[]); | |
1136 f4(a: /*$info*/[[3]]); | |
1137 f4(a: /*severe:StaticTypeError*/[["hello"]]); | |
1138 f4(a: /*severe:StaticTypeError*/[["hello"], [3]]); | |
1139 } | |
1140 '''; | |
1141 testChecker('infer downwards', {'/main.dart': code}); | |
1142 }); | |
1143 | |
1144 group('downwards inference on map literals', () { | |
1145 String info = "info:InferredTypeLiteral"; | |
1146 String code = ''' | |
1147 void foo([Map<int, String> m1 = /*$info*/const {1: "hello"}, | |
1148 Map<int, String> m1 = /*severe:StaticTypeError*/const {"hello":
"world"}]) { | |
1149 } | |
1150 void main() { | |
1151 { | |
1152 Map<int, String> l0 = /*$info*/{}; | |
1153 Map<int, String> l1 = /*$info*/{3: "hello"}; | |
1154 Map<int, String> l2 = /*severe:StaticTypeError*/{"hello": "hello"}; | |
1155 Map<int, String> l3 = /*severe:StaticTypeError*/{3: 3}; | |
1156 Map<int, String> l4 = /*severe:StaticTypeError*/{3:"hello", "hello": 3
}; | |
1157 } | |
1158 { | |
1159 Map<dynamic, dynamic> l0 = {}; | |
1160 Map<dynamic, dynamic> l1 = {3: "hello"}; | |
1161 Map<dynamic, dynamic> l2 = {"hello": "hello"}; | |
1162 Map<dynamic, dynamic> l3 = {3: 3}; | |
1163 Map<dynamic, dynamic> l4 = {3:"hello", "hello": 3}; | |
1164 } | |
1165 { | |
1166 Map<dynamic, String> l0 = /*$info*/{}; | |
1167 Map<dynamic, String> l1 = /*$info*/{3: "hello"}; | |
1168 Map<dynamic, String> l2 = /*$info*/{"hello": "hello"}; | |
1169 Map<dynamic, String> l3 = /*severe:StaticTypeError*/{3: 3}; | |
1170 Map<dynamic, String> l4 = /*severe:StaticTypeError*/{3:"hello", "hello
": 3}; | |
1171 } | |
1172 { | |
1173 Map<int, dynamic> l0 = /*$info*/{}; | |
1174 Map<int, dynamic> l1 = /*$info*/{3: "hello"}; | |
1175 Map<int, dynamic> l2 = /*severe:StaticTypeError*/{"hello": "hello"}; | |
1176 Map<int, dynamic> l3 = /*$info*/{3: 3}; | |
1177 Map<int, dynamic> l3 = /*severe:StaticTypeError*/{3:"hello", "hello":
3}; | |
1178 } | |
1179 { | |
1180 Map<int, String> l0 = /*severe:StaticTypeError*/<num, dynamic>{}; | |
1181 Map<int, String> l1 = /*severe:StaticTypeError*/<num, dynamic>{3: "hel
lo"}; | |
1182 Map<int, String> l3 = /*severe:StaticTypeError*/<num, dynamic>{3: 3}; | |
1183 } | |
1184 { | |
1185 const Map<int, String> l0 = /*$info*/const {}; | |
1186 const Map<int, String> l1 = /*$info*/const {3: "hello"}; | |
1187 const Map<int, String> l2 = /*severe:StaticTypeError*/const {"hello":
"hello"}; | |
1188 const Map<int, String> l3 = /*severe:StaticTypeError*/const {3: 3}; | |
1189 const Map<int, String> l4 = /*severe:StaticTypeError*/const {3:"hello"
, "hello": 3}; | |
1190 } | |
1191 } | |
1192 '''; | |
1193 testChecker('infer downwards', {'/main.dart': code}); | |
1194 }); | |
1195 | |
1196 testChecker('downwards inference on function expressions', { | |
1197 '/main.dart': ''' | |
1198 typedef T Function2<S, T>(S x); | |
1199 | |
1200 void main () { | |
1201 { | |
1202 Function2<int, String> l0 = (int x) => null; | |
1203 Function2<int, String> l1 = (int x) => "hello"; | |
1204 Function2<int, String> l2 = /*severe:StaticTypeError*/(String x) => "h
ello"; | |
1205 Function2<int, String> l3 = /*severe:StaticTypeError*/(int x) => 3; | |
1206 Function2<int, String> l4 = /*warning:UninferredClosure should be seve
re:StaticTypeError*/(int x) {return 3}; | |
1207 } | |
1208 { | |
1209 Function2<int, String> l0 = /*info:InferredTypeClosure*/(x) => null; | |
1210 Function2<int, String> l1 = /*info:InferredTypeClosure*/(x) => "hello"
; | |
1211 Function2<int, String> l2 = /*severe:StaticTypeError*/(x) => 3; | |
1212 Function2<int, String> l3 = /*warning:UninferredClosure should be seve
re:StaticTypeError*/(x) {return 3}; | |
1213 } | |
1214 { | |
1215 Function2<int, List<String>> l0 = (int x) => null; | |
1216 Function2<int, List<String>> l1 = /*info:InferredTypeClosure*/(int x)
=> ["hello"]; | |
1217 Function2<int, List<String>> l2 = /*severe:StaticTypeError*/(String x)
=> ["hello"]; | |
1218 Function2<int, List<String>> l3 = /*warning:UninferredClosure should b
e severe:StaticTypeError*/(int x) => [3]; | |
1219 Function2<int, List<String>> l4 = /*warning:UninferredClosure should b
e severe:StaticTypeError*/(int x) {return [3]}; | |
1220 } | |
1221 { | |
1222 Function2<int, int> l0 = /*info:InferredTypeClosure*/(x) => x; | |
1223 Function2<int, int> l1 = /*info:InferredTypeClosure*/(x) => /*info:Dyn
amicInvoke should be pass*/x+1; | |
1224 Function2<int, String> l2 = /*info:InferredTypeClosure should be sever
e:StaticTypeError*/(x) => x; | |
1225 Function2<int, String> l3 = /*info:InferredTypeClosure should be sever
e:StaticTypeError*/(x) => /*info:DynamicInvoke should be pass*/x.substring(3); | |
1226 Function2<String, String> l4 = /*info:InferredTypeClosure*/(x) => /*in
fo:DynamicInvoke should be pass*/x.substring(3); | |
1227 } | |
1228 } | |
1229 ''' | |
1230 }); | |
1231 | |
1232 testChecker('inferred initializing formal checks default value', { | |
1233 '/main.dart': ''' | |
1234 class Foo { | |
1235 var x = 1; | |
1236 Foo([this.x = /*severe:StaticTypeError*/"1"]); | |
1237 }''' | |
1238 }); | |
1239 | |
1240 group('quasi-generics', () { | |
1241 testChecker('dart:math min/max', { | |
1242 '/main.dart': ''' | |
1243 import 'dart:math'; | |
1244 | |
1245 void printInt(int x) => print(x); | |
1246 void printDouble(double x) => print(x); | |
1247 | |
1248 num myMax(num x, num y) => max(x, y); | |
1249 | |
1250 main() { | |
1251 // Okay if static types match. | |
1252 printInt(max(1, 2)); | |
1253 printInt(min(1, 2)); | |
1254 printDouble(max(1.0, 2.0)); | |
1255 printDouble(min(1.0, 2.0)); | |
1256 | |
1257 // No help for user-defined functions from num->num->num. | |
1258 printInt(/*info:DownCastImplicit*/myMax(1, 2)); | |
1259 printInt(myMax(1, 2) as int); | |
1260 | |
1261 // Mixing int and double means return type is num. | |
1262 printInt(/*info:DownCastImplicit*/max(1, 2.0)); | |
1263 printInt(/*info:DownCastImplicit*/min(1, 2.0)); | |
1264 printDouble(/*info:DownCastImplicit*/max(1, 2.0)); | |
1265 printDouble(/*info:DownCastImplicit*/min(1, 2.0)); | |
1266 | |
1267 // Types other than int and double are not accepted. | |
1268 printInt( | |
1269 /*info:DownCastImplicit*/min( | |
1270 /*severe:StaticTypeError*/"hi", | |
1271 /*severe:StaticTypeError*/"there")); | |
1272 } | |
1273 ''' | |
1274 }); | |
1275 | |
1276 testChecker('Iterable and Future', { | |
1277 '/main.dart': ''' | |
1278 import 'dart:async'; | |
1279 | |
1280 Future<int> make(int x) => (/*info:InferredTypeAllocation*/new Future(()
=> x)); | |
1281 | |
1282 main() { | |
1283 Iterable<Future<int>> list = <int>[1, 2, 3].map(make); | |
1284 Future<List<int>> results = Future.wait(list); | |
1285 Future<String> results2 = results.then((List<int> list) | |
1286 => list.fold('', (String x, int y) => x + y.toString())); | |
1287 } | |
1288 ''' | |
1289 }); | |
1290 }); | |
1291 } | |
OLD | NEW |