| 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 there are no 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 |
| 1010 test('infer correctly on multiple variables declared together', () { |
| 1011 testChecker({ |
| 1012 '/main.dart': ''' |
| 1013 class A { |
| 1014 var x, y = 2, z = "hi"; |
| 1015 } |
| 1016 |
| 1017 class B extends A { |
| 1018 var x = 2, y = 3, z, w = 2; |
| 1019 } |
| 1020 |
| 1021 foo() { |
| 1022 String s; |
| 1023 int i; |
| 1024 |
| 1025 s = /*info:DownCast*/new B().x; |
| 1026 s = /*severe:StaticTypeError*/new B().y; |
| 1027 s = new B().z; |
| 1028 s = /*severe:StaticTypeError*/new B().w; |
| 1029 |
| 1030 i = /*info:DownCast*/new B().x; |
| 1031 i = new B().y; |
| 1032 i = /*severe:StaticTypeError*/new B().z; |
| 1033 i = new B().w; |
| 1034 } |
| 1035 ''' |
| 1036 }, inferFromOverrides: true); |
| 1037 }); |
| 936 } | 1038 } |
| OLD | NEW |