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