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:test/test.dart'; | 8 import 'package:test/test.dart'; |
9 | 9 |
10 import '../testing.dart'; | 10 import '../testing.dart'; |
(...skipping 541 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
552 var d1 = new D(); | 552 var d1 = new D(); |
553 print(d1.c.b.a.x); | 553 print(d1.c.b.a.x); |
554 | 554 |
555 D d2 = new D(); | 555 D d2 = new D(); |
556 print(d2.c.b.a.x); | 556 print(d2.c.b.a.x); |
557 } | 557 } |
558 ''' | 558 ''' |
559 }); | 559 }); |
560 | 560 |
561 group('infer type on overridden fields', () { | 561 group('infer type on overridden fields', () { |
562 testChecker( | 562 testChecker('2', { |
563 '2', | 563 '/main.dart': ''' |
564 { | |
565 '/main.dart': ''' | |
566 class A { | 564 class A { |
567 int x = 2; | 565 int x = 2; |
568 } | 566 } |
569 | 567 |
570 class B extends A { | 568 class B extends A { |
571 get x => 3; | 569 get x => 3; |
572 } | 570 } |
573 | 571 |
574 foo() { | 572 foo() { |
575 String y = /*severe:StaticTypeError*/new B().x; | 573 String y = /*severe:StaticTypeError*/new B().x; |
576 int z = new B().x; | 574 int z = new B().x; |
577 } | 575 } |
578 ''' | 576 ''' |
579 }, | 577 }); |
580 inferFromOverrides: true); | |
581 | 578 |
582 testChecker( | 579 testChecker('4', { |
583 '4', | 580 '/main.dart': ''' |
584 { | |
585 '/main.dart': ''' | |
586 class A { | 581 class A { |
587 int x = 2; | 582 int x = 2; |
588 } | 583 } |
589 | 584 |
590 class B implements A { | 585 class B implements A { |
591 get x => 3; | 586 get x => 3; |
592 } | 587 } |
593 | 588 |
594 foo() { | 589 foo() { |
595 String y = /*severe:StaticTypeError*/new B().x; | 590 String y = /*severe:StaticTypeError*/new B().x; |
596 int z = new B().x; | 591 int z = new B().x; |
597 } | 592 } |
598 ''' | 593 ''' |
599 }, | 594 }); |
600 inferFromOverrides: true); | |
601 }); | 595 }); |
602 | 596 |
603 group('infer types on generic instantiations', () { | 597 group('infer types on generic instantiations', () { |
604 for (bool infer in [true, false]) { | 598 testChecker('infer', { |
605 testChecker( | 599 '/main.dart': ''' |
606 'infer: $infer', | 600 class A<T> { |
607 { | 601 T x; |
608 '/main.dart': ''' | 602 } |
609 class A<T> { | |
610 T x; | |
611 } | |
612 | 603 |
613 class B implements A<int> { | 604 class B implements A<int> { |
614 /*severe:InvalidMethodOverride*/dynamic get x => 3; | 605 /*severe:InvalidMethodOverride*/dynamic get x => 3; |
615 } | 606 } |
616 | 607 |
617 foo() { | 608 foo() { |
618 String y = /*info:DynamicCast*/new B().x; | 609 String y = /*info:DynamicCast*/new B().x; |
619 int z = /*info:DynamicCast*/new B().x; | 610 int z = /*info:DynamicCast*/new B().x; |
620 } | 611 } |
621 ''' | 612 ''' |
622 }, | 613 }); |
623 inferFromOverrides: infer); | |
624 } | |
625 | 614 |
626 testChecker( | 615 testChecker('3', { |
627 '3', | 616 '/main.dart': ''' |
628 { | |
629 '/main.dart': ''' | |
630 class A<T> { | 617 class A<T> { |
631 T x; | 618 T x; |
632 T w; | 619 T w; |
633 } | 620 } |
634 | 621 |
635 class B implements A<int> { | 622 class B implements A<int> { |
636 get x => 3; | 623 get x => 3; |
637 get w => /*severe:StaticTypeError*/"hello"; | 624 get w => /*severe:StaticTypeError*/"hello"; |
638 } | 625 } |
639 | 626 |
640 foo() { | 627 foo() { |
641 String y = /*severe:StaticTypeError*/new B().x; | 628 String y = /*severe:StaticTypeError*/new B().x; |
642 int z = new B().x; | 629 int z = new B().x; |
643 } | 630 } |
644 ''' | 631 ''' |
645 }, | 632 }); |
646 inferFromOverrides: true); | |
647 | 633 |
648 testChecker( | 634 testChecker('4', { |
649 '4', | 635 '/main.dart': ''' |
650 { | |
651 '/main.dart': ''' | |
652 class A<T> { | 636 class A<T> { |
653 T x; | 637 T x; |
654 } | 638 } |
655 | 639 |
656 class B<E> extends A<E> { | 640 class B<E> extends A<E> { |
657 E y; | 641 E y; |
658 get x => y; | 642 get x => y; |
659 } | 643 } |
660 | 644 |
661 foo() { | 645 foo() { |
662 int y = /*severe:StaticTypeError*/new B<String>().x; | 646 int y = /*severe:StaticTypeError*/new B<String>().x; |
663 String z = new B<String>().x; | 647 String z = new B<String>().x; |
664 } | 648 } |
665 ''' | 649 ''' |
666 }, | 650 }); |
667 inferFromOverrides: true); | |
668 | 651 |
669 testChecker( | 652 testChecker('5', { |
670 '5', | 653 '/main.dart': ''' |
671 { | |
672 '/main.dart': ''' | |
673 abstract class I<E> { | 654 abstract class I<E> { |
674 String m(a, String f(v, T e)); | 655 String m(a, String f(v, T e)); |
675 } | 656 } |
676 | 657 |
677 abstract class A<E> implements I<E> { | 658 abstract class A<E> implements I<E> { |
678 const A(); | 659 const A(); |
679 String m(a, String f(v, T e)); | 660 String m(a, String f(v, T e)); |
680 } | 661 } |
681 | 662 |
682 abstract class M { | 663 abstract class M { |
683 int y; | 664 int y; |
684 } | 665 } |
685 | 666 |
686 class B<E> extends A<E> implements M { | 667 class B<E> extends A<E> implements M { |
687 const B(); | 668 const B(); |
688 int get y => 0; | 669 int get y => 0; |
689 | 670 |
690 m(a, f(v, T e)) {} | 671 m(a, f(v, T e)) {} |
691 } | 672 } |
692 | 673 |
693 foo () { | 674 foo () { |
694 int y = /*severe:StaticTypeError*/new B().m(null, null); | 675 int y = /*severe:StaticTypeError*/new B().m(null, null); |
695 String z = new B().m(null, null); | 676 String z = new B().m(null, null); |
696 } | 677 } |
697 ''' | 678 ''' |
698 }, | 679 }); |
699 inferFromOverrides: true); | |
700 }); | 680 }); |
701 | 681 |
702 testChecker( | 682 testChecker('infer type regardless of declaration order or cycles', { |
703 'infer type regardless of declaration order or cycles', | 683 '/b.dart': ''' |
704 { | |
705 '/b.dart': ''' | |
706 import 'main.dart'; | 684 import 'main.dart'; |
707 | 685 |
708 class B extends A { } | 686 class B extends A { } |
709 ''', | 687 ''', |
710 '/main.dart': ''' | 688 '/main.dart': ''' |
711 import 'b.dart'; | 689 import 'b.dart'; |
712 class C extends B { | 690 class C extends B { |
713 get x; | 691 get x; |
714 } | 692 } |
715 class A { | 693 class A { |
716 int get x; | 694 int get x; |
717 } | 695 } |
718 foo () { | 696 foo () { |
719 int y = new C().x; | 697 int y = new C().x; |
720 String y = /*severe:StaticTypeError*/new C().x; | 698 String y = /*severe:StaticTypeError*/new C().x; |
721 } | 699 } |
722 ''' | 700 ''' |
723 }, | 701 }); |
724 inferFromOverrides: true); | |
725 | 702 |
726 // Note: this is a regression test for a non-deterministic behavior we used to | 703 // Note: this is a regression test for a non-deterministic behavior we used to |
727 // have with inference in library cycles. If you see this test flake out, | 704 // have with inference in library cycles. If you see this test flake out, |
728 // change `test` to `skip_test` and reopen bug #48. | 705 // change `test` to `skip_test` and reopen bug #48. |
729 testChecker( | 706 testChecker('infer types on generic instantiations in library cycle', { |
730 'infer types on generic instantiations in library cycle', | 707 '/a.dart': ''' |
731 { | |
732 '/a.dart': ''' | |
733 import 'main.dart'; | 708 import 'main.dart'; |
734 abstract class I<E> { | 709 abstract class I<E> { |
735 A<E> m(a, String f(v, int e)); | 710 A<E> m(a, String f(v, int e)); |
736 } | 711 } |
737 ''', | 712 ''', |
738 '/main.dart': ''' | 713 '/main.dart': ''' |
739 import 'a.dart'; | 714 import 'a.dart'; |
740 | 715 |
741 abstract class A<E> implements I<E> { | 716 abstract class A<E> implements I<E> { |
742 const A(); | 717 const A(); |
743 | 718 |
744 E value; | 719 E value; |
745 } | 720 } |
746 | 721 |
747 abstract class M { | 722 abstract class M { |
748 int y; | 723 int y; |
749 } | 724 } |
750 | 725 |
751 class B<E> extends A<E> implements M { | 726 class B<E> extends A<E> implements M { |
752 const B(); | 727 const B(); |
753 int get y => 0; | 728 int get y => 0; |
754 | 729 |
755 m(a, f(v, int e)) {} | 730 m(a, f(v, int e)) {} |
756 } | 731 } |
757 | 732 |
758 foo () { | 733 foo () { |
759 int y = /*severe:StaticTypeError*/new B<String>().m(null, null).value; | 734 int y = /*severe:StaticTypeError*/new B<String>().m(null, null).value; |
760 String z = new B<String>().m(null, null).value; | 735 String z = new B<String>().m(null, null).value; |
761 } | 736 } |
762 ''' | 737 ''' |
763 }, | 738 }); |
764 inferFromOverrides: true); | |
765 | 739 |
766 group('do not infer overridden fields that explicitly say dynamic', () { | 740 group('do not infer overridden fields that explicitly say dynamic', () { |
767 for (bool infer in [true, false]) { | 741 testChecker('infer', { |
768 testChecker( | 742 '/main.dart': ''' |
769 'infer: $infer', | |
770 { | |
771 '/main.dart': ''' | |
772 class A { | 743 class A { |
773 int x = 2; | 744 int x = 2; |
774 } | 745 } |
775 | 746 |
776 class B implements A { | 747 class B implements A { |
777 /*severe:InvalidMethodOverride*/dynamic get x => 3; | 748 /*severe:InvalidMethodOverride*/dynamic get x => 3; |
778 } | 749 } |
779 | 750 |
780 foo() { | 751 foo() { |
781 String y = /*info:DynamicCast*/new B().x; | 752 String y = /*info:DynamicCast*/new B().x; |
782 int z = /*info:DynamicCast*/new B().x; | 753 int z = /*info:DynamicCast*/new B().x; |
783 } | 754 } |
784 ''' | 755 ''' |
785 }, | 756 }); |
786 inferFromOverrides: infer); | |
787 } | |
788 }); | 757 }); |
789 | 758 |
790 testChecker( | 759 testChecker('conflicts can happen', { |
791 'conflicts can happen', | 760 '/main.dart': ''' |
792 { | |
793 '/main.dart': ''' | |
794 class I1 { | 761 class I1 { |
795 int x; | 762 int x; |
796 } | 763 } |
797 class I2 extends I1 { | 764 class I2 extends I1 { |
798 int y; | 765 int y; |
799 } | 766 } |
800 | 767 |
801 class A { | 768 class A { |
802 final I1 a; | 769 final I1 a; |
803 } | 770 } |
804 | 771 |
805 class B { | 772 class B { |
806 final I2 a; | 773 final I2 a; |
807 } | 774 } |
808 | 775 |
809 class C1 extends A implements B { | 776 class C1 extends A implements B { |
810 /*severe:InvalidMethodOverride,severe:InvalidMethodOverride*/get a =>
null; | 777 /*severe:InvalidMethodOverride,severe:InvalidMethodOverride*/get a =>
null; |
811 } | 778 } |
812 | 779 |
813 // Still ambiguous | 780 // Still ambiguous |
814 class C2 extends B implements A { | 781 class C2 extends B implements A { |
815 /*severe:InvalidMethodOverride,severe:InvalidMethodOverride*/get a =>
null; | 782 /*severe:InvalidMethodOverride,severe:InvalidMethodOverride*/get a =>
null; |
816 } | 783 } |
817 ''' | 784 ''' |
818 }, | 785 }); |
819 inferFromOverrides: true); | |
820 | 786 |
821 testChecker( | 787 testChecker('conflicts can happen 2', { |
822 'conflicts can happen 2', | 788 '/main.dart': ''' |
823 { | |
824 '/main.dart': ''' | |
825 class I1 { | 789 class I1 { |
826 int x; | 790 int x; |
827 } | 791 } |
828 class I2 { | 792 class I2 { |
829 int y; | 793 int y; |
830 } | 794 } |
831 | 795 |
832 class I3 implements I1, I2 { | 796 class I3 implements I1, I2 { |
833 int x; | 797 int x; |
834 int y; | 798 int y; |
835 } | 799 } |
836 | 800 |
837 class A { | 801 class A { |
838 final I1 a; | 802 final I1 a; |
839 } | 803 } |
840 | 804 |
841 class B { | 805 class B { |
842 final I2 a; | 806 final I2 a; |
843 } | 807 } |
844 | 808 |
845 class C1 extends A implements B { | 809 class C1 extends A implements B { |
846 I3 get a => null; | 810 I3 get a => null; |
847 } | 811 } |
848 | 812 |
849 class C2 extends A implements B { | 813 class C2 extends A implements B { |
850 /*severe:InvalidMethodOverride,severe:InvalidMethodOverride*/get a =>
null; | 814 /*severe:InvalidMethodOverride,severe:InvalidMethodOverride*/get a =>
null; |
851 } | 815 } |
852 ''' | 816 ''' |
853 }, | 817 }); |
854 inferFromOverrides: true); | |
855 | 818 |
856 testChecker( | 819 testChecker( |
857 'infer from RHS only if it wont conflict with overridden fields', | 820 'infer from RHS only if it wont conflict with overridden fields', { |
858 { | 821 '/main.dart': ''' |
859 '/main.dart': ''' | |
860 class A { | 822 class A { |
861 var x; | 823 var x; |
862 } | 824 } |
863 | 825 |
864 class B extends A { | 826 class B extends A { |
865 var x = 2; | 827 var x = 2; |
866 } | 828 } |
867 | 829 |
868 foo() { | 830 foo() { |
869 String y = /*info:DynamicCast*/new B().x; | 831 String y = /*info:DynamicCast*/new B().x; |
870 int z = /*info:DynamicCast*/new B().x; | 832 int z = /*info:DynamicCast*/new B().x; |
871 } | 833 } |
872 ''' | 834 ''' |
873 }, | 835 }); |
874 inferFromOverrides: true); | |
875 | 836 |
876 testChecker( | 837 testChecker( |
877 'infer from RHS only if it wont conflict with overridden fields 2', | 838 'infer from RHS only if it wont conflict with overridden fields 2', { |
878 { | 839 '/main.dart': ''' |
879 '/main.dart': ''' | |
880 class A { | 840 class A { |
881 final x; | 841 final x; |
882 } | 842 } |
883 | 843 |
884 class B extends A { | 844 class B extends A { |
885 final x = 2; | 845 final x = 2; |
886 } | 846 } |
887 | 847 |
888 foo() { | 848 foo() { |
889 String y = /*severe:StaticTypeError*/new B().x; | 849 String y = /*severe:StaticTypeError*/new B().x; |
890 int z = new B().x; | 850 int z = new B().x; |
891 } | 851 } |
892 ''' | 852 ''' |
893 }, | 853 }); |
894 inferFromOverrides: true); | |
895 | 854 |
896 testChecker( | 855 testChecker('infer correctly on multiple variables declared together', { |
897 'infer correctly on multiple variables declared together', | 856 '/main.dart': ''' |
898 { | |
899 '/main.dart': ''' | |
900 class A { | 857 class A { |
901 var x, y = 2, z = "hi"; | 858 var x, y = 2, z = "hi"; |
902 } | 859 } |
903 | 860 |
904 class B extends A { | 861 class B extends A { |
905 var x = 2, y = 3, z, w = 2; | 862 var x = 2, y = 3, z, w = 2; |
906 } | 863 } |
907 | 864 |
908 foo() { | 865 foo() { |
909 String s; | 866 String s; |
910 int i; | 867 int i; |
911 | 868 |
912 s = /*info:DynamicCast*/new B().x; | 869 s = /*info:DynamicCast*/new B().x; |
913 s = /*severe:StaticTypeError*/new B().y; | 870 s = /*severe:StaticTypeError*/new B().y; |
914 s = new B().z; | 871 s = new B().z; |
915 s = /*severe:StaticTypeError*/new B().w; | 872 s = /*severe:StaticTypeError*/new B().w; |
916 | 873 |
917 i = /*info:DynamicCast*/new B().x; | 874 i = /*info:DynamicCast*/new B().x; |
918 i = new B().y; | 875 i = new B().y; |
919 i = /*severe:StaticTypeError*/new B().z; | 876 i = /*severe:StaticTypeError*/new B().z; |
920 i = new B().w; | 877 i = new B().w; |
921 } | 878 } |
922 ''' | 879 ''' |
923 }, | 880 }); |
924 inferFromOverrides: true); | |
925 | 881 |
926 testChecker( | 882 testChecker( |
927 'infer consts transitively', | 883 'infer consts transitively', |
928 { | 884 { |
929 '/b.dart': ''' | 885 '/b.dart': ''' |
930 const b1 = 2; | 886 const b1 = 2; |
931 ''', | 887 ''', |
932 '/a.dart': ''' | 888 '/a.dart': ''' |
933 import 'main.dart'; | 889 import 'main.dart'; |
934 import 'b.dart'; | 890 import 'b.dart'; |
935 const a1 = m2; | 891 const a1 = m2; |
936 const a2 = b1; | 892 const a2 = b1; |
937 ''', | 893 ''', |
938 '/main.dart': ''' | 894 '/main.dart': ''' |
939 import 'a.dart'; | 895 import 'a.dart'; |
940 const m1 = a1; | 896 const m1 = a1; |
941 const m2 = a2; | 897 const m2 = a2; |
942 | 898 |
943 foo() { | 899 foo() { |
944 int i; | 900 int i; |
945 i = m1; | 901 i = m1; |
946 } | 902 } |
947 ''' | 903 ''' |
948 }, | 904 }, |
949 inferFromOverrides: true, | |
950 inferTransitively: true); | 905 inferTransitively: true); |
951 | 906 |
952 testChecker( | 907 testChecker( |
953 'infer statics transitively', | 908 'infer statics transitively', |
954 { | 909 { |
955 '/b.dart': ''' | 910 '/b.dart': ''' |
956 final b1 = 2; | 911 final b1 = 2; |
957 ''', | 912 ''', |
958 '/a.dart': ''' | 913 '/a.dart': ''' |
959 import 'main.dart'; | 914 import 'main.dart'; |
960 import 'b.dart'; | 915 import 'b.dart'; |
961 final a1 = m2; | 916 final a1 = m2; |
962 class A { | 917 class A { |
963 static final a2 = b1; | 918 static final a2 = b1; |
964 } | 919 } |
965 ''', | 920 ''', |
966 '/main.dart': ''' | 921 '/main.dart': ''' |
967 import 'a.dart'; | 922 import 'a.dart'; |
968 final m1 = a1; | 923 final m1 = a1; |
969 final m2 = A.a2; | 924 final m2 = A.a2; |
970 | 925 |
971 foo() { | 926 foo() { |
972 int i; | 927 int i; |
973 i = m1; | 928 i = m1; |
974 } | 929 } |
975 ''' | 930 ''' |
976 }, | 931 }, |
977 inferFromOverrides: true, | |
978 inferTransitively: true); | 932 inferTransitively: true); |
979 | 933 |
980 testChecker( | 934 testChecker( |
981 'infer statics transitively 2', | 935 'infer statics transitively 2', |
982 { | 936 { |
983 '/main.dart': ''' | 937 '/main.dart': ''' |
984 const x1 = 1; | 938 const x1 = 1; |
985 final x2 = 1; | 939 final x2 = 1; |
986 final y1 = x1; | 940 final y1 = x1; |
987 final y2 = x2; | 941 final y2 = x2; |
988 | 942 |
989 foo() { | 943 foo() { |
990 int i; | 944 int i; |
991 i = y1; | 945 i = y1; |
992 i = y2; | 946 i = y2; |
993 } | 947 } |
994 ''' | 948 ''' |
995 }, | 949 }, |
996 inferFromOverrides: true, | |
997 inferTransitively: true); | 950 inferTransitively: true); |
998 | 951 |
999 testChecker( | 952 testChecker( |
1000 'infer statics transitively 3', | 953 'infer statics transitively 3', |
1001 { | 954 { |
1002 '/a.dart': ''' | 955 '/a.dart': ''' |
1003 const a1 = 3; | 956 const a1 = 3; |
1004 const a2 = 4; | 957 const a2 = 4; |
1005 class A { | 958 class A { |
1006 a3; | 959 a3; |
(...skipping 11 matching lines...) Expand all Loading... |
1018 | 971 |
1019 foo() { | 972 foo() { |
1020 int i; | 973 int i; |
1021 i = t1; | 974 i = t1; |
1022 i = t2; | 975 i = t2; |
1023 i = t3; | 976 i = t3; |
1024 i = t4; | 977 i = t4; |
1025 } | 978 } |
1026 ''' | 979 ''' |
1027 }, | 980 }, |
1028 inferFromOverrides: true, | |
1029 inferTransitively: true); | 981 inferTransitively: true); |
1030 | 982 |
1031 testChecker( | 983 testChecker( |
1032 'infer statics with method invocations', | 984 'infer statics with method invocations', |
1033 { | 985 { |
1034 '/a.dart': ''' | 986 '/a.dart': ''' |
1035 m3(String a, String b, [a1,a2]) {} | 987 m3(String a, String b, [a1,a2]) {} |
1036 ''', | 988 ''', |
1037 '/main.dart': ''' | 989 '/main.dart': ''' |
1038 import 'a.dart'; | 990 import 'a.dart'; |
1039 class T { | 991 class T { |
1040 static final T foo = m1(m2(m3('', ''))); | 992 static final T foo = m1(m2(m3('', ''))); |
1041 static T m1(String m) { return null; } | 993 static T m1(String m) { return null; } |
1042 static String m2(e) { return ''; } | 994 static String m2(e) { return ''; } |
1043 } | 995 } |
1044 | 996 |
1045 | 997 |
1046 ''' | 998 ''' |
1047 }, | 999 }, |
1048 inferFromOverrides: true, | |
1049 inferTransitively: true); | 1000 inferTransitively: true); |
1050 | 1001 |
1051 testChecker( | 1002 testChecker( |
1052 'downwards inference: miscellaneous', | 1003 'downwards inference: miscellaneous', |
1053 { | 1004 { |
1054 '/main.dart': ''' | 1005 '/main.dart': ''' |
1055 typedef (T x); | 1006 typedef (T x); |
1056 class A<T> { | 1007 class A<T> { |
1057 Function2<T> x; | 1008 Function2<T> x; |
1058 A(this.x); | 1009 A(this.x); |
(...skipping 354 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1413 main() { | 1364 main() { |
1414 Iterable<Future<int>> list = <int>[1, 2, 3].map(make); | 1365 Iterable<Future<int>> list = <int>[1, 2, 3].map(make); |
1415 Future<List<int>> results = Future.wait(list); | 1366 Future<List<int>> results = Future.wait(list); |
1416 Future<String> results2 = results.then((List<int> list) | 1367 Future<String> results2 = results.then((List<int> list) |
1417 => list.fold('', (String x, int y) => x + y.toString())); | 1368 => list.fold('', (String x, int y) => x + y.toString())); |
1418 } | 1369 } |
1419 ''' | 1370 ''' |
1420 }); | 1371 }); |
1421 }); | 1372 }); |
1422 } | 1373 } |
OLD | NEW |