| 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/compact_vm_config.dart'; | 8 import 'package:unittest/compact_vm_config.dart'; |
| 9 import 'package:unittest/unittest.dart'; | 9 import 'package:unittest/unittest.dart'; |
| 10 | 10 |
| (...skipping 143 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 154 static var x = 2; | 154 static var x = 2; |
| 155 static var y = A.x; | 155 static var y = A.x; |
| 156 } | 156 } |
| 157 | 157 |
| 158 test1() { | 158 test1() { |
| 159 A.x = /*severe:StaticTypeError*/"hi"; | 159 A.x = /*severe:StaticTypeError*/"hi"; |
| 160 A.y = "hi"; | 160 A.y = "hi"; |
| 161 } | 161 } |
| 162 ''' | 162 ''' |
| 163 }); | 163 }); |
| 164 | |
| 165 // Allowed with special flag. Note, while the flag is generally not stable, | |
| 166 // we can use it in this test because it is stable within a library (order | |
| 167 // matches program order). | |
| 168 testChecker({ | |
| 169 '/main.dart': ''' | |
| 170 var x = 2; | |
| 171 var y = x; | |
| 172 | |
| 173 test1() { | |
| 174 x = /*severe:StaticTypeError*/"hi"; | |
| 175 y = /*severe:StaticTypeError*/"hi"; | |
| 176 } | |
| 177 ''' | |
| 178 }, inferInNonStableOrder: true); | |
| 179 | |
| 180 testChecker({ | |
| 181 '/main.dart': ''' | |
| 182 class A { | |
| 183 static var x = 2; | |
| 184 static var y = A.x; | |
| 185 } | |
| 186 | |
| 187 test1() { | |
| 188 A.x = /*severe:StaticTypeError*/"hi"; | |
| 189 A.y = /*severe:StaticTypeError*/"hi"; | |
| 190 } | |
| 191 ''' | |
| 192 }, inferInNonStableOrder: true); | |
| 193 }); | 164 }); |
| 194 | 165 |
| 195 test('not ok to infer from variables in non-cycle libs', () { | 166 test('not ok to infer from variables in non-cycle libs', () { |
| 196 testChecker({ | 167 testChecker({ |
| 197 '/a.dart': ''' | 168 '/a.dart': ''' |
| 198 var x = 2; | 169 var x = 2; |
| 199 ''', | 170 ''', |
| 200 '/main.dart': ''' | 171 '/main.dart': ''' |
| 201 import 'a.dart'; | 172 import 'a.dart'; |
| 202 var y = x; | 173 var y = x; |
| (...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 253 B.y = /*severe:StaticTypeError*/"hi"; | 224 B.y = /*severe:StaticTypeError*/"hi"; |
| 254 } | 225 } |
| 255 ''' | 226 ''' |
| 256 }, inferStaticsFromIdentifiers: true); | 227 }, inferStaticsFromIdentifiers: true); |
| 257 }); | 228 }); |
| 258 | 229 |
| 259 test('do not infer from variables in cycle libs', () { | 230 test('do not infer from variables in cycle libs', () { |
| 260 testChecker({ | 231 testChecker({ |
| 261 '/a.dart': ''' | 232 '/a.dart': ''' |
| 262 import 'main.dart'; | 233 import 'main.dart'; |
| 263 var x = 2; | 234 var x = 2; // ok to infer |
| 264 ''', | 235 ''', |
| 265 '/main.dart': ''' | 236 '/main.dart': ''' |
| 266 import 'a.dart'; | 237 import 'a.dart'; |
| 267 var y = x; | 238 var y = x; // not ok to infer yet |
| 268 | 239 |
| 269 test1() { | 240 test1() { |
| 270 int t = 3; | 241 int t = 3; |
| 271 t = /*info:DownCast*/x; | 242 t = x; |
| 272 t = /*info:DownCast*/y; | 243 t = /*info:DownCast*/y; |
| 273 } | 244 } |
| 274 ''' | 245 ''' |
| 275 }, inferStaticsFromIdentifiers: true); | 246 }, inferStaticsFromIdentifiers: true); |
| 276 | 247 |
| 277 testChecker({ | 248 testChecker({ |
| 278 '/a.dart': ''' | 249 '/a.dart': ''' |
| 279 import 'main.dart'; | 250 import 'main.dart'; |
| 280 class A { static var x = 2; } | 251 class A { static var x = 2; } |
| 281 ''', | 252 ''', |
| 282 '/main.dart': ''' | 253 '/main.dart': ''' |
| 283 import 'a.dart'; | 254 import 'a.dart'; |
| 284 class B { static var y = A.x; } | 255 class B { static var y = A.x; } |
| 285 | 256 |
| 286 test1() { | 257 test1() { |
| 287 int t = 3; | 258 int t = 3; |
| 288 t = /*info:DownCast*/A.x; | 259 t = A.x; |
| 289 t = /*info:DownCast*/A.y; | 260 t = /*info:DownCast*/A.y; |
| 290 } | 261 } |
| 291 ''' | 262 ''' |
| 292 }, inferStaticsFromIdentifiers: true); | 263 }, inferStaticsFromIdentifiers: true); |
| 293 }); | 264 }); |
| 294 | 265 |
| 295 test('do not infer from static and instance fields', () { | 266 test('do not infer from static and instance fields', () { |
| 296 testChecker({ | 267 testChecker({ |
| 297 '/a.dart': ''' | 268 '/a.dart': ''' |
| 298 import 'b.dart'; | 269 import 'b.dart'; |
| (...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 341 test1() { | 312 test1() { |
| 342 int x = 0; | 313 int x = 0; |
| 343 // inference in A now works. | 314 // inference in A now works. |
| 344 x = A.a1; | 315 x = A.a1; |
| 345 x = new A().a2; | 316 x = new A().a2; |
| 346 } | 317 } |
| 347 ''' | 318 ''' |
| 348 }, inferStaticsFromIdentifiers: true); | 319 }, inferStaticsFromIdentifiers: true); |
| 349 }); | 320 }); |
| 350 | 321 |
| 351 test('don\'t infer on cycles', () { | 322 test('inference uses declared types', () { |
| 323 testChecker({ |
| 324 '/main.dart': ''' |
| 325 int w = 0; |
| 326 var x = 0; |
| 327 |
| 328 var y = w; // y can be inferred because w is typed int. |
| 329 var z = x; // z cannot, because x would be inferred. |
| 330 |
| 331 test1() { |
| 332 int a; |
| 333 a = w; |
| 334 a = x; |
| 335 a = y; |
| 336 a = /*info:DownCast*/z; |
| 337 } |
| 338 ''' |
| 339 }, inferStaticsFromIdentifiers: true); |
| 340 }); |
| 341 |
| 342 test('inference in cycles is deterministic', () { |
| 352 testChecker({ | 343 testChecker({ |
| 353 '/a.dart': ''' | 344 '/a.dart': ''' |
| 354 import 'b.dart'; | 345 import 'b.dart'; |
| 355 class A { | 346 class A { |
| 356 static final a1 = B.b1; | 347 static final a1 = B.b1; |
| 357 final a2 = new B().b2; | 348 final a2 = new B().b2; |
| 358 } | 349 } |
| 359 ''', | 350 ''', |
| 360 '/b.dart': ''' | 351 '/b.dart': ''' |
| 361 class B { | 352 class B { |
| 362 static final b1 = 1; | 353 static final b1 = 1; |
| 363 final b2 = 1; | 354 final b2 = 1; |
| 364 } | 355 } |
| 365 ''', | 356 ''', |
| 366 '/c.dart': ''' | 357 '/c.dart': ''' |
| 367 import "main.dart"; // creates a cycle | 358 import "main.dart"; // creates a cycle |
| 368 | 359 |
| 369 class C { | 360 class C { |
| 370 static final c1 = 1; | 361 static final c1 = 1; |
| 371 final c2 = 1; | 362 final c2 = 1; |
| 372 } | 363 } |
| 373 ''', | 364 ''', |
| 374 '/e.dart': ''' | 365 '/e.dart': ''' |
| 375 part "e2.dart"; | 366 import 'a.dart'; |
| 367 part 'e2.dart'; |
| 376 | 368 |
| 377 class E { | 369 class E { |
| 378 static final e1 = 1; | 370 static final e1 = 1; |
| 379 final e2 = 1; | 371 static final e2 = F.f1; |
| 372 static final e3 = A.a1; |
| 373 final e4 = 1; |
| 374 final e5 = new F().f2; |
| 375 final e6 = new A().a2; |
| 380 } | 376 } |
| 381 ''', | 377 ''', |
| 382 '/f.dart': ''' | 378 '/f.dart': ''' |
| 383 part "f2.dart"; | 379 part 'f2.dart'; |
| 384 ''', | 380 ''', |
| 385 '/e2.dart': ''' | 381 '/e2.dart': ''' |
| 386 class F { | 382 class F { |
| 387 static final f1 = 1; | 383 static final f1 = 1; |
| 388 final f2 = 1; | 384 final f2 = 1; |
| 389 } | 385 } |
| 390 ''', | 386 ''', |
| 391 '/main.dart': ''' | 387 '/main.dart': ''' |
| 392 import "a.dart"; | 388 import "a.dart"; |
| 393 import "c.dart"; | 389 import "c.dart"; |
| 394 import "e.dart"; | 390 import "e.dart"; |
| 395 | 391 |
| 396 class D { | 392 class D { |
| 397 static final d1 = A.a1 + 1; | 393 static final d1 = A.a1 + 1; |
| 398 static final d2 = C.c1 + 1; | 394 static final d2 = C.c1 + 1; |
| 399 final d3 = new A().a2; | 395 final d3 = new A().a2; |
| 400 final d4 = new C().c2; | 396 final d4 = new C().c2; |
| 401 } | 397 } |
| 402 | 398 |
| 403 test1() { | 399 test1() { |
| 404 int x = 0; | 400 int x = 0; |
| 405 // inference in A works, it's not in a cycle | 401 // inference in A works, it's not in a cycle |
| 406 x = A.a1; | 402 x = A.a1; |
| 407 x = new A().a2; | 403 x = new A().a2; |
| 408 | 404 |
| 409 // inference here or in c.dart is disabled because of the cycle | 405 // Within a cycle we allow inference when the RHS is well known, but |
| 410 x = /*info:DownCast*/C.c1; | 406 // not when it depends on other fields within the cycle |
| 411 x = /*info:DownCast*/D.d1; | 407 x = C.c1; |
| 408 x = D.d1; |
| 412 x = /*info:DownCast*/D.d2; | 409 x = /*info:DownCast*/D.d2; |
| 413 x = /*info:DownCast*/new C().c2; | 410 x = new C().c2; |
| 414 x = /*info:DownCast*/new D().d3; | 411 x = new D().d3; |
| 415 x = /*info:DownCast*/new D().d4; | 412 x = /*info:DownCast*/new D().d4; |
| 416 | 413 |
| 417 | 414 |
| 418 // inference in e.dart and f.dart is disabled because they contains | 415 // Similarly if the library contains parts. |
| 419 // parts | 416 x = E.e1; |
| 420 x = /*info:DownCast*/E.e1; | 417 x = /*info:DownCast*/E.e2; |
| 421 x = /*info:DownCast*/new E().e2; | 418 x = E.e3; |
| 422 x = /*info:DownCast*/F.f1; | 419 x = new E().e4; |
| 423 x = /*info:DownCast*/new F().f2; | 420 x = /*info:DownCast*/new E().e5; |
| 421 x = new E().e6; |
| 422 x = F.f1; |
| 423 x = new F().f2; |
| 424 } | 424 } |
| 425 ''' | 425 ''' |
| 426 }, inferStaticsFromIdentifiers: true); | 426 }, inferStaticsFromIdentifiers: true); |
| 427 }); | 427 }); |
| 428 | 428 |
| 429 test('infer from complex expressions if the outer-most value is precise', () { | 429 test('infer from complex expressions if the outer-most value is precise', () { |
| 430 testChecker({ | 430 testChecker({ |
| 431 '/main.dart': ''' | 431 '/main.dart': ''' |
| 432 class A { int x; B operator+(other) {} } | 432 class A { int x; B operator+(other) {} } |
| 433 class B extends A { B(ignore); } | 433 class B extends A { B(ignore); } |
| 434 var a = new A(); | 434 var a = new A(); |
| 435 // Note: it doesn't matter that some of these refer to 'x'. | 435 // Note: it doesn't matter that some of these refer to 'x'. |
| 436 var b = new B(x); // allocations | 436 var b = new B(x); // allocations |
| 437 var c1 = [x]; // list literals | 437 var c1 = [x]; // list literals |
| 438 var c2 = const []; | 438 var c2 = const []; |
| 439 var d = {'a': 'b'}; // map literals | 439 var d = {'a': 'b'}; // map literals |
| 440 var e = new A()..x = 3; // cascades | 440 var e = new A()..x = 3; // cascades |
| 441 var f = 2 + 3; // binary expressions are OK if the left operand | 441 var f = 2 + 3; // binary expressions are OK if the left operand |
| 442 // is from a library in a different strongest | 442 // is from a library in a different strongest |
| 443 // conected component. | 443 // conected component. |
| 444 var g = -3; | 444 var g = -3; |
| 445 var h = new A() + 3; | 445 var h = new A() + 3; |
| 446 var i = - new A(); | 446 var i = - new A(); |
| 447 var j = null as B; |
| 447 | 448 |
| 448 test1() { | 449 test1() { |
| 449 a = /*severe:StaticTypeError*/"hi"; | 450 a = /*severe:StaticTypeError*/"hi"; |
| 450 a = new B(3); | 451 a = new B(3); |
| 451 b = /*severe:StaticTypeError*/"hi"; | 452 b = /*severe:StaticTypeError*/"hi"; |
| 452 b = new B(3); | 453 b = new B(3); |
| 453 c1 = []; | 454 c1 = []; |
| 454 c1 = /*severe:StaticTypeError*/{}; | 455 c1 = /*severe:StaticTypeError*/{}; |
| 455 c2 = []; | 456 c2 = []; |
| 456 c2 = /*severe:StaticTypeError*/{}; | 457 c2 = /*severe:StaticTypeError*/{}; |
| 457 d = {}; | 458 d = {}; |
| 458 d = /*severe:StaticTypeError*/3; | 459 d = /*severe:StaticTypeError*/3; |
| 459 e = new A(); | 460 e = new A(); |
| 460 e = /*severe:StaticTypeError*/{}; | 461 e = /*severe:StaticTypeError*/{}; |
| 461 f = 3; | 462 f = 3; |
| 462 f = /*severe:StaticTypeError*/false; | 463 f = /*severe:StaticTypeError*/false; |
| 463 g = 1; | 464 g = 1; |
| 464 g = /*severe:StaticTypeError*/false; | 465 g = /*severe:StaticTypeError*/false; |
| 465 h = /*severe:StaticTypeError*/false; | 466 h = /*severe:StaticTypeError*/false; |
| 466 h = new B(); | 467 h = new B(); |
| 467 i = false; | 468 i = false; |
| 469 j = new B(); |
| 470 j = /*severe:StaticTypeError*/false; |
| 471 j = /*severe:StaticTypeError*/[]; |
| 468 } | 472 } |
| 469 ''' | 473 ''' |
| 470 }); | 474 }); |
| 471 }); | 475 }); |
| 472 | 476 |
| 473 test('do not infer if complex expressions read possibly inferred field', () { | 477 test('do not infer if complex expressions read possibly inferred field', () { |
| 474 testChecker({ | 478 testChecker({ |
| 475 '/a.dart': ''' | 479 '/a.dart': ''' |
| 476 class A { | 480 class A { |
| 477 var x = 3; | 481 var x = 3; |
| (...skipping 327 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 805 } | 809 } |
| 806 | 810 |
| 807 foo () { | 811 foo () { |
| 808 int y = /*severe:StaticTypeError*/new B().m(null, null); | 812 int y = /*severe:StaticTypeError*/new B().m(null, null); |
| 809 String z = new B().m(null, null); | 813 String z = new B().m(null, null); |
| 810 } | 814 } |
| 811 ''' | 815 ''' |
| 812 }, inferFromOverrides: true); | 816 }, inferFromOverrides: true); |
| 813 }); | 817 }); |
| 814 | 818 |
| 815 // TODO(sigmund): enable this test, it's currently flaky (see issue #48). | 819 test('infer type regardless of declaration order or cycles', () { |
| 816 skip_test('infer types on generic instantiations in library cycle', () { | 820 testChecker({ |
| 821 '/b.dart': ''' |
| 822 import 'main.dart'; |
| 823 |
| 824 class B extends A { } |
| 825 ''', |
| 826 '/main.dart': ''' |
| 827 import 'b.dart'; |
| 828 class C extends B { |
| 829 get x; |
| 830 } |
| 831 class A { |
| 832 int get x; |
| 833 } |
| 834 foo () { |
| 835 int y = new C().x; |
| 836 String y = /*severe:StaticTypeError*/new C().x; |
| 837 } |
| 838 ''' |
| 839 }, inferFromOverrides: true); |
| 840 }); |
| 841 |
| 842 // Note: this is a regression test for a non-deterministic behavior we used to |
| 843 // have with inference in library cycles. If you see this test flake out, |
| 844 // change `test` to `skip_test` and reopen bug #48. |
| 845 test('infer types on generic instantiations in library cycle', () { |
| 817 testChecker({ | 846 testChecker({ |
| 818 '/a.dart': ''' | 847 '/a.dart': ''' |
| 819 import 'main.dart'; | 848 import 'main.dart'; |
| 820 abstract class I<E> { | 849 abstract class I<E> { |
| 821 A<E> m(a, String f(v, T e)); | 850 A<E> m(a, String f(v, T e)); |
| 822 } | 851 } |
| 823 ''', | 852 ''', |
| 824 '/main.dart': ''' | 853 '/main.dart': ''' |
| 825 import 'a.dart'; | 854 import 'a.dart'; |
| 826 | 855 |
| (...skipping 98 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 925 I3 get a => null; | 954 I3 get a => null; |
| 926 } | 955 } |
| 927 | 956 |
| 928 class C2 extends A implements B { | 957 class C2 extends A implements B { |
| 929 /*severe:InvalidMethodOverride*/get a => null; | 958 /*severe:InvalidMethodOverride*/get a => null; |
| 930 } | 959 } |
| 931 ''' | 960 ''' |
| 932 }, inferFromOverrides: true); | 961 }, inferFromOverrides: true); |
| 933 }); | 962 }); |
| 934 } | 963 } |
| OLD | NEW |