github.com/jhump/golang-x-tools@v0.0.0-20220218190644-4958d6d39439/go/ast/astutil/imports_test.go (about) 1 // Copyright 2013 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package astutil 6 7 import ( 8 "bytes" 9 "go/ast" 10 "go/format" 11 "go/parser" 12 "go/token" 13 "reflect" 14 "strconv" 15 "testing" 16 ) 17 18 var fset = token.NewFileSet() 19 20 func parse(t *testing.T, name, in string) *ast.File { 21 file, err := parser.ParseFile(fset, name, in, parser.ParseComments) 22 if err != nil { 23 t.Fatalf("%s parse: %v", name, err) 24 } 25 return file 26 } 27 28 func print(t *testing.T, name string, f *ast.File) string { 29 var buf bytes.Buffer 30 if err := format.Node(&buf, fset, f); err != nil { 31 t.Fatalf("%s gofmt: %v", name, err) 32 } 33 return buf.String() 34 } 35 36 type test struct { 37 name string 38 renamedPkg string 39 pkg string 40 in string 41 out string 42 unchanged bool // Expect added/deleted return value to be false. 43 } 44 45 var addTests = []test{ 46 { 47 name: "leave os alone", 48 pkg: "os", 49 in: `package main 50 51 import ( 52 "os" 53 ) 54 `, 55 out: `package main 56 57 import ( 58 "os" 59 ) 60 `, 61 unchanged: true, 62 }, 63 { 64 name: "import.1", 65 pkg: "os", 66 in: `package main 67 `, 68 out: `package main 69 70 import "os" 71 `, 72 }, 73 { 74 name: "import.2", 75 pkg: "os", 76 in: `package main 77 78 // Comment 79 import "C" 80 `, 81 out: `package main 82 83 // Comment 84 import "C" 85 import "os" 86 `, 87 }, 88 { 89 name: "import.3", 90 pkg: "os", 91 in: `package main 92 93 // Comment 94 import "C" 95 96 import ( 97 "io" 98 "utf8" 99 ) 100 `, 101 out: `package main 102 103 // Comment 104 import "C" 105 106 import ( 107 "io" 108 "os" 109 "utf8" 110 ) 111 `, 112 }, 113 { 114 name: "import.17", 115 pkg: "x/y/z", 116 in: `package main 117 118 // Comment 119 import "C" 120 121 import ( 122 "a" 123 "b" 124 125 "x/w" 126 127 "d/f" 128 ) 129 `, 130 out: `package main 131 132 // Comment 133 import "C" 134 135 import ( 136 "a" 137 "b" 138 139 "x/w" 140 "x/y/z" 141 142 "d/f" 143 ) 144 `, 145 }, 146 { 147 name: "issue #19190", 148 pkg: "x.org/y/z", 149 in: `package main 150 151 // Comment 152 import "C" 153 154 import ( 155 "bytes" 156 "os" 157 158 "d.com/f" 159 ) 160 `, 161 out: `package main 162 163 // Comment 164 import "C" 165 166 import ( 167 "bytes" 168 "os" 169 170 "d.com/f" 171 "x.org/y/z" 172 ) 173 `, 174 }, 175 { 176 name: "issue #19190 with existing grouped import packages", 177 pkg: "x.org/y/z", 178 in: `package main 179 180 // Comment 181 import "C" 182 183 import ( 184 "bytes" 185 "os" 186 187 "c.com/f" 188 "d.com/f" 189 190 "y.com/a" 191 "y.com/b" 192 "y.com/c" 193 ) 194 `, 195 out: `package main 196 197 // Comment 198 import "C" 199 200 import ( 201 "bytes" 202 "os" 203 204 "c.com/f" 205 "d.com/f" 206 "x.org/y/z" 207 208 "y.com/a" 209 "y.com/b" 210 "y.com/c" 211 ) 212 `, 213 }, 214 { 215 name: "issue #19190 - match score is still respected", 216 pkg: "y.org/c", 217 in: `package main 218 219 import ( 220 "x.org/a" 221 222 "y.org/b" 223 ) 224 `, 225 out: `package main 226 227 import ( 228 "x.org/a" 229 230 "y.org/b" 231 "y.org/c" 232 ) 233 `, 234 }, 235 { 236 name: "import into singular group", 237 pkg: "bytes", 238 in: `package main 239 240 import "os" 241 242 `, 243 out: `package main 244 245 import ( 246 "bytes" 247 "os" 248 ) 249 `, 250 }, 251 { 252 name: "import into singular group with comment", 253 pkg: "bytes", 254 in: `package main 255 256 import /* why */ /* comment here? */ "os" 257 258 `, 259 out: `package main 260 261 import /* why */ /* comment here? */ ( 262 "bytes" 263 "os" 264 ) 265 `, 266 }, 267 { 268 name: "import into group with leading comment", 269 pkg: "strings", 270 in: `package main 271 272 import ( 273 // comment before bytes 274 "bytes" 275 "os" 276 ) 277 278 `, 279 out: `package main 280 281 import ( 282 // comment before bytes 283 "bytes" 284 "os" 285 "strings" 286 ) 287 `, 288 }, 289 { 290 name: "", 291 renamedPkg: "fmtpkg", 292 pkg: "fmt", 293 in: `package main 294 295 import "os" 296 297 `, 298 out: `package main 299 300 import ( 301 fmtpkg "fmt" 302 "os" 303 ) 304 `, 305 }, 306 { 307 name: "struct comment", 308 pkg: "time", 309 in: `package main 310 311 // This is a comment before a struct. 312 type T struct { 313 t time.Time 314 } 315 `, 316 out: `package main 317 318 import "time" 319 320 // This is a comment before a struct. 321 type T struct { 322 t time.Time 323 } 324 `, 325 }, 326 { 327 name: "issue 8729 import C", 328 pkg: "time", 329 in: `package main 330 331 import "C" 332 333 // comment 334 type T time.Time 335 `, 336 out: `package main 337 338 import "C" 339 import "time" 340 341 // comment 342 type T time.Time 343 `, 344 }, 345 { 346 name: "issue 8729 empty import", 347 pkg: "time", 348 in: `package main 349 350 import () 351 352 // comment 353 type T time.Time 354 `, 355 out: `package main 356 357 import "time" 358 359 // comment 360 type T time.Time 361 `, 362 }, 363 { 364 name: "issue 8729 comment on package line", 365 pkg: "time", 366 in: `package main // comment 367 368 type T time.Time 369 `, 370 out: `package main // comment 371 372 import "time" 373 374 type T time.Time 375 `, 376 }, 377 { 378 name: "issue 8729 comment after package", 379 pkg: "time", 380 in: `package main 381 // comment 382 383 type T time.Time 384 `, 385 out: `package main 386 387 import "time" 388 389 // comment 390 391 type T time.Time 392 `, 393 }, 394 { 395 name: "issue 8729 comment before and on package line", 396 pkg: "time", 397 in: `// comment before 398 package main // comment on 399 400 type T time.Time 401 `, 402 out: `// comment before 403 package main // comment on 404 405 import "time" 406 407 type T time.Time 408 `, 409 }, 410 411 // Issue 9961: Match prefixes using path segments rather than bytes 412 { 413 name: "issue 9961", 414 pkg: "regexp", 415 in: `package main 416 417 import ( 418 "flag" 419 "testing" 420 421 "rsc.io/p" 422 ) 423 `, 424 out: `package main 425 426 import ( 427 "flag" 428 "regexp" 429 "testing" 430 431 "rsc.io/p" 432 ) 433 `, 434 }, 435 // Issue 10337: Preserve comment position 436 { 437 name: "issue 10337", 438 pkg: "fmt", 439 in: `package main 440 441 import ( 442 "bytes" // a 443 "log" // c 444 ) 445 `, 446 out: `package main 447 448 import ( 449 "bytes" // a 450 "fmt" 451 "log" // c 452 ) 453 `, 454 }, 455 { 456 name: "issue 10337 new import at the start", 457 pkg: "bytes", 458 in: `package main 459 460 import ( 461 "fmt" // b 462 "log" // c 463 ) 464 `, 465 out: `package main 466 467 import ( 468 "bytes" 469 "fmt" // b 470 "log" // c 471 ) 472 `, 473 }, 474 { 475 name: "issue 10337 new import at the end", 476 pkg: "log", 477 in: `package main 478 479 import ( 480 "bytes" // a 481 "fmt" // b 482 ) 483 `, 484 out: `package main 485 486 import ( 487 "bytes" // a 488 "fmt" // b 489 "log" 490 ) 491 `, 492 }, 493 // Issue 14075: Merge import declarations 494 { 495 name: "issue 14075", 496 pkg: "bufio", 497 in: `package main 498 499 import "bytes" 500 import "fmt" 501 `, 502 out: `package main 503 504 import ( 505 "bufio" 506 "bytes" 507 "fmt" 508 ) 509 `, 510 }, 511 { 512 name: "issue 14075 update position", 513 pkg: "bufio", 514 in: `package main 515 516 import "bytes" 517 import ( 518 "fmt" 519 ) 520 `, 521 out: `package main 522 523 import ( 524 "bufio" 525 "bytes" 526 "fmt" 527 ) 528 `, 529 }, 530 { 531 name: `issue 14075 ignore import "C"`, 532 pkg: "bufio", 533 in: `package main 534 535 // Comment 536 import "C" 537 538 import "bytes" 539 import "fmt" 540 `, 541 out: `package main 542 543 // Comment 544 import "C" 545 546 import ( 547 "bufio" 548 "bytes" 549 "fmt" 550 ) 551 `, 552 }, 553 { 554 name: `issue 14075 ignore adjacent import "C"`, 555 pkg: "bufio", 556 in: `package main 557 558 // Comment 559 import "C" 560 import "fmt" 561 `, 562 out: `package main 563 564 // Comment 565 import "C" 566 import ( 567 "bufio" 568 "fmt" 569 ) 570 `, 571 }, 572 { 573 name: `issue 14075 ignore adjacent import "C" (without factored import)`, 574 pkg: "bufio", 575 in: `package main 576 577 // Comment 578 import "C" 579 import "fmt" 580 `, 581 out: `package main 582 583 // Comment 584 import "C" 585 import ( 586 "bufio" 587 "fmt" 588 ) 589 `, 590 }, 591 { 592 name: `issue 14075 ignore single import "C"`, 593 pkg: "bufio", 594 in: `package main 595 596 // Comment 597 import "C" 598 `, 599 out: `package main 600 601 // Comment 602 import "C" 603 import "bufio" 604 `, 605 }, 606 { 607 name: `issue 17212 several single-import lines with shared prefix ending in a slash`, 608 pkg: "net/http", 609 in: `package main 610 611 import "bufio" 612 import "net/url" 613 `, 614 out: `package main 615 616 import ( 617 "bufio" 618 "net/http" 619 "net/url" 620 ) 621 `, 622 }, 623 { 624 name: `issue 17212 block imports lines with shared prefix ending in a slash`, 625 pkg: "net/http", 626 in: `package main 627 628 import ( 629 "bufio" 630 "net/url" 631 ) 632 `, 633 out: `package main 634 635 import ( 636 "bufio" 637 "net/http" 638 "net/url" 639 ) 640 `, 641 }, 642 { 643 name: `issue 17213 many single-import lines`, 644 pkg: "fmt", 645 in: `package main 646 647 import "bufio" 648 import "bytes" 649 import "errors" 650 `, 651 out: `package main 652 653 import ( 654 "bufio" 655 "bytes" 656 "errors" 657 "fmt" 658 ) 659 `, 660 }, 661 662 // Issue 28605: Add specified import, even if that import path is imported under another name 663 { 664 name: "issue 28605 add unnamed path", 665 renamedPkg: "", 666 pkg: "path", 667 in: `package main 668 669 import ( 670 . "path" 671 _ "path" 672 pathpkg "path" 673 ) 674 `, 675 out: `package main 676 677 import ( 678 "path" 679 . "path" 680 _ "path" 681 pathpkg "path" 682 ) 683 `, 684 }, 685 { 686 name: "issue 28605 add pathpkg-renamed path", 687 renamedPkg: "pathpkg", 688 pkg: "path", 689 in: `package main 690 691 import ( 692 "path" 693 . "path" 694 _ "path" 695 ) 696 `, 697 out: `package main 698 699 import ( 700 "path" 701 . "path" 702 _ "path" 703 pathpkg "path" 704 ) 705 `, 706 }, 707 { 708 name: "issue 28605 add blank identifier path", 709 renamedPkg: "_", 710 pkg: "path", 711 in: `package main 712 713 import ( 714 "path" 715 . "path" 716 pathpkg "path" 717 ) 718 `, 719 out: `package main 720 721 import ( 722 "path" 723 . "path" 724 _ "path" 725 pathpkg "path" 726 ) 727 `, 728 }, 729 { 730 name: "issue 28605 add dot import path", 731 renamedPkg: ".", 732 pkg: "path", 733 in: `package main 734 735 import ( 736 "path" 737 _ "path" 738 pathpkg "path" 739 ) 740 `, 741 out: `package main 742 743 import ( 744 "path" 745 . "path" 746 _ "path" 747 pathpkg "path" 748 ) 749 `, 750 }, 751 752 { 753 name: "duplicate import declarations, add existing one", 754 renamedPkg: "f", 755 pkg: "fmt", 756 in: `package main 757 758 import "fmt" 759 import "fmt" 760 import f "fmt" 761 import f "fmt" 762 `, 763 out: `package main 764 765 import "fmt" 766 import "fmt" 767 import f "fmt" 768 import f "fmt" 769 `, 770 unchanged: true, 771 }, 772 } 773 774 func TestAddImport(t *testing.T) { 775 for _, test := range addTests { 776 file := parse(t, test.name, test.in) 777 var before bytes.Buffer 778 ast.Fprint(&before, fset, file, nil) 779 added := AddNamedImport(fset, file, test.renamedPkg, test.pkg) 780 if got := print(t, test.name, file); got != test.out { 781 t.Errorf("first run: %s:\ngot: %s\nwant: %s", test.name, got, test.out) 782 var after bytes.Buffer 783 ast.Fprint(&after, fset, file, nil) 784 t.Logf("AST before:\n%s\nAST after:\n%s\n", before.String(), after.String()) 785 } 786 if got, want := added, !test.unchanged; got != want { 787 t.Errorf("first run: %s: added = %v, want %v", test.name, got, want) 788 } 789 790 // AddNamedImport should be idempotent. Verify that by calling it again, 791 // expecting no change to the AST, and the returned added value to always be false. 792 added = AddNamedImport(fset, file, test.renamedPkg, test.pkg) 793 if got := print(t, test.name, file); got != test.out { 794 t.Errorf("second run: %s:\ngot: %s\nwant: %s", test.name, got, test.out) 795 } 796 if got, want := added, false; got != want { 797 t.Errorf("second run: %s: added = %v, want %v", test.name, got, want) 798 } 799 } 800 } 801 802 func TestDoubleAddImport(t *testing.T) { 803 file := parse(t, "doubleimport", "package main\n") 804 AddImport(fset, file, "os") 805 AddImport(fset, file, "bytes") 806 want := `package main 807 808 import ( 809 "bytes" 810 "os" 811 ) 812 ` 813 if got := print(t, "doubleimport", file); got != want { 814 t.Errorf("got: %s\nwant: %s", got, want) 815 } 816 } 817 818 func TestDoubleAddNamedImport(t *testing.T) { 819 file := parse(t, "doublenamedimport", "package main\n") 820 AddNamedImport(fset, file, "o", "os") 821 AddNamedImport(fset, file, "i", "io") 822 want := `package main 823 824 import ( 825 i "io" 826 o "os" 827 ) 828 ` 829 if got := print(t, "doublenamedimport", file); got != want { 830 t.Errorf("got: %s\nwant: %s", got, want) 831 } 832 } 833 834 // Part of issue 8729. 835 func TestDoubleAddImportWithDeclComment(t *testing.T) { 836 file := parse(t, "doubleimport", `package main 837 838 import ( 839 ) 840 841 // comment 842 type I int 843 `) 844 // The AddImport order here matters. 845 AddImport(fset, file, "github.com/jhump/golang-x-tools/go/ast/astutil") 846 AddImport(fset, file, "os") 847 want := `package main 848 849 import ( 850 "github.com/jhump/golang-x-tools/go/ast/astutil" 851 "os" 852 ) 853 854 // comment 855 type I int 856 ` 857 if got := print(t, "doubleimport_with_decl_comment", file); got != want { 858 t.Errorf("got: %s\nwant: %s", got, want) 859 } 860 } 861 862 var deleteTests = []test{ 863 { 864 name: "import.4", 865 pkg: "os", 866 in: `package main 867 868 import ( 869 "os" 870 ) 871 `, 872 out: `package main 873 `, 874 }, 875 { 876 name: "import.5", 877 pkg: "os", 878 in: `package main 879 880 // Comment 881 import "C" 882 import "os" 883 `, 884 out: `package main 885 886 // Comment 887 import "C" 888 `, 889 }, 890 { 891 name: "import.6", 892 pkg: "os", 893 in: `package main 894 895 // Comment 896 import "C" 897 898 import ( 899 "io" 900 "os" 901 "utf8" 902 ) 903 `, 904 out: `package main 905 906 // Comment 907 import "C" 908 909 import ( 910 "io" 911 "utf8" 912 ) 913 `, 914 }, 915 { 916 name: "import.7", 917 pkg: "io", 918 in: `package main 919 920 import ( 921 "io" // a 922 "os" // b 923 "utf8" // c 924 ) 925 `, 926 out: `package main 927 928 import ( 929 // a 930 "os" // b 931 "utf8" // c 932 ) 933 `, 934 }, 935 { 936 name: "import.8", 937 pkg: "os", 938 in: `package main 939 940 import ( 941 "io" // a 942 "os" // b 943 "utf8" // c 944 ) 945 `, 946 out: `package main 947 948 import ( 949 "io" // a 950 // b 951 "utf8" // c 952 ) 953 `, 954 }, 955 { 956 name: "import.9", 957 pkg: "utf8", 958 in: `package main 959 960 import ( 961 "io" // a 962 "os" // b 963 "utf8" // c 964 ) 965 `, 966 out: `package main 967 968 import ( 969 "io" // a 970 "os" // b 971 // c 972 ) 973 `, 974 }, 975 { 976 name: "import.10", 977 pkg: "io", 978 in: `package main 979 980 import ( 981 "io" 982 "os" 983 "utf8" 984 ) 985 `, 986 out: `package main 987 988 import ( 989 "os" 990 "utf8" 991 ) 992 `, 993 }, 994 { 995 name: "import.11", 996 pkg: "os", 997 in: `package main 998 999 import ( 1000 "io" 1001 "os" 1002 "utf8" 1003 ) 1004 `, 1005 out: `package main 1006 1007 import ( 1008 "io" 1009 "utf8" 1010 ) 1011 `, 1012 }, 1013 { 1014 name: "import.12", 1015 pkg: "utf8", 1016 in: `package main 1017 1018 import ( 1019 "io" 1020 "os" 1021 "utf8" 1022 ) 1023 `, 1024 out: `package main 1025 1026 import ( 1027 "io" 1028 "os" 1029 ) 1030 `, 1031 }, 1032 { 1033 name: "handle.raw.quote.imports", 1034 pkg: "os", 1035 in: "package main\n\nimport `os`", 1036 out: `package main 1037 `, 1038 }, 1039 { 1040 name: "import.13", 1041 pkg: "io", 1042 in: `package main 1043 1044 import ( 1045 "fmt" 1046 1047 "io" 1048 "os" 1049 "utf8" 1050 1051 "go/format" 1052 ) 1053 `, 1054 out: `package main 1055 1056 import ( 1057 "fmt" 1058 1059 "os" 1060 "utf8" 1061 1062 "go/format" 1063 ) 1064 `, 1065 }, 1066 { 1067 name: "import.14", 1068 pkg: "io", 1069 in: `package main 1070 1071 import ( 1072 "fmt" // a 1073 1074 "io" // b 1075 "os" // c 1076 "utf8" // d 1077 1078 "go/format" // e 1079 ) 1080 `, 1081 out: `package main 1082 1083 import ( 1084 "fmt" // a 1085 1086 // b 1087 "os" // c 1088 "utf8" // d 1089 1090 "go/format" // e 1091 ) 1092 `, 1093 }, 1094 { 1095 name: "import.15", 1096 pkg: "double", 1097 in: `package main 1098 1099 import ( 1100 "double" 1101 "double" 1102 ) 1103 `, 1104 out: `package main 1105 `, 1106 }, 1107 { 1108 name: "import.16", 1109 pkg: "bubble", 1110 in: `package main 1111 1112 import ( 1113 "toil" 1114 "bubble" 1115 "bubble" 1116 "trouble" 1117 ) 1118 `, 1119 out: `package main 1120 1121 import ( 1122 "toil" 1123 "trouble" 1124 ) 1125 `, 1126 }, 1127 { 1128 name: "import.17", 1129 pkg: "quad", 1130 in: `package main 1131 1132 import ( 1133 "quad" 1134 "quad" 1135 ) 1136 1137 import ( 1138 "quad" 1139 "quad" 1140 ) 1141 `, 1142 out: `package main 1143 `, 1144 }, 1145 { 1146 name: "import.18", 1147 renamedPkg: "x", 1148 pkg: "fmt", 1149 in: `package main 1150 1151 import ( 1152 "fmt" 1153 x "fmt" 1154 ) 1155 `, 1156 out: `package main 1157 1158 import ( 1159 "fmt" 1160 ) 1161 `, 1162 }, 1163 { 1164 name: "import.18", 1165 renamedPkg: "x", 1166 pkg: "fmt", 1167 in: `package main 1168 1169 import x "fmt" 1170 import y "fmt" 1171 `, 1172 out: `package main 1173 1174 import y "fmt" 1175 `, 1176 }, 1177 // Issue #15432, #18051 1178 { 1179 name: "import.19", 1180 pkg: "fmt", 1181 in: `package main 1182 1183 import ( 1184 "fmt" 1185 1186 // Some comment. 1187 "io" 1188 )`, 1189 out: `package main 1190 1191 import ( 1192 // Some comment. 1193 "io" 1194 ) 1195 `, 1196 }, 1197 { 1198 name: "import.20", 1199 pkg: "fmt", 1200 in: `package main 1201 1202 import ( 1203 "fmt" 1204 1205 // Some 1206 // comment. 1207 "io" 1208 )`, 1209 out: `package main 1210 1211 import ( 1212 // Some 1213 // comment. 1214 "io" 1215 ) 1216 `, 1217 }, 1218 { 1219 name: "import.21", 1220 pkg: "fmt", 1221 in: `package main 1222 1223 import ( 1224 "fmt" 1225 1226 /* 1227 Some 1228 comment. 1229 */ 1230 "io" 1231 )`, 1232 out: `package main 1233 1234 import ( 1235 /* 1236 Some 1237 comment. 1238 */ 1239 "io" 1240 ) 1241 `, 1242 }, 1243 { 1244 name: "import.22", 1245 pkg: "fmt", 1246 in: `package main 1247 1248 import ( 1249 /* Some */ 1250 // comment. 1251 "io" 1252 "fmt" 1253 )`, 1254 out: `package main 1255 1256 import ( 1257 /* Some */ 1258 // comment. 1259 "io" 1260 ) 1261 `, 1262 }, 1263 { 1264 name: "import.23", 1265 pkg: "fmt", 1266 in: `package main 1267 1268 import ( 1269 // comment 1 1270 "fmt" 1271 // comment 2 1272 "io" 1273 )`, 1274 out: `package main 1275 1276 import ( 1277 // comment 2 1278 "io" 1279 ) 1280 `, 1281 }, 1282 { 1283 name: "import.24", 1284 pkg: "fmt", 1285 in: `package main 1286 1287 import ( 1288 "fmt" // comment 1 1289 "io" // comment 2 1290 )`, 1291 out: `package main 1292 1293 import ( 1294 "io" // comment 2 1295 ) 1296 `, 1297 }, 1298 { 1299 name: "import.25", 1300 pkg: "fmt", 1301 in: `package main 1302 1303 import ( 1304 "fmt" 1305 /* comment */ "io" 1306 )`, 1307 out: `package main 1308 1309 import ( 1310 /* comment */ "io" 1311 ) 1312 `, 1313 }, 1314 { 1315 name: "import.26", 1316 pkg: "fmt", 1317 in: `package main 1318 1319 import ( 1320 "fmt" 1321 "io" /* comment */ 1322 )`, 1323 out: `package main 1324 1325 import ( 1326 "io" /* comment */ 1327 ) 1328 `, 1329 }, 1330 { 1331 name: "import.27", 1332 pkg: "fmt", 1333 in: `package main 1334 1335 import ( 1336 "fmt" /* comment */ 1337 "io" 1338 )`, 1339 out: `package main 1340 1341 import ( 1342 "io" 1343 ) 1344 `, 1345 }, 1346 { 1347 name: "import.28", 1348 pkg: "fmt", 1349 in: `package main 1350 1351 import ( 1352 /* comment */ "fmt" 1353 "io" 1354 )`, 1355 out: `package main 1356 1357 import ( 1358 "io" 1359 ) 1360 `, 1361 }, 1362 { 1363 name: "import.29", 1364 pkg: "fmt", 1365 in: `package main 1366 1367 // comment 1 1368 import ( 1369 "fmt" 1370 "io" // comment 2 1371 )`, 1372 out: `package main 1373 1374 // comment 1 1375 import ( 1376 "io" // comment 2 1377 ) 1378 `, 1379 }, 1380 { 1381 name: "import.30", 1382 pkg: "fmt", 1383 in: `package main 1384 1385 // comment 1 1386 import ( 1387 "fmt" // comment 2 1388 "io" 1389 )`, 1390 out: `package main 1391 1392 // comment 1 1393 import ( 1394 "io" 1395 ) 1396 `, 1397 }, 1398 { 1399 name: "import.31", 1400 pkg: "fmt", 1401 in: `package main 1402 1403 // comment 1 1404 import ( 1405 "fmt" 1406 /* comment 2 */ "io" 1407 )`, 1408 out: `package main 1409 1410 // comment 1 1411 import ( 1412 /* comment 2 */ "io" 1413 ) 1414 `, 1415 }, 1416 { 1417 name: "import.32", 1418 pkg: "fmt", 1419 renamedPkg: "f", 1420 in: `package main 1421 1422 // comment 1 1423 import ( 1424 f "fmt" 1425 /* comment 2 */ i "io" 1426 )`, 1427 out: `package main 1428 1429 // comment 1 1430 import ( 1431 /* comment 2 */ i "io" 1432 ) 1433 `, 1434 }, 1435 { 1436 name: "import.33", 1437 pkg: "fmt", 1438 renamedPkg: "f", 1439 in: `package main 1440 1441 // comment 1 1442 import ( 1443 /* comment 2 */ f "fmt" 1444 i "io" 1445 )`, 1446 out: `package main 1447 1448 // comment 1 1449 import ( 1450 i "io" 1451 ) 1452 `, 1453 }, 1454 { 1455 name: "import.34", 1456 pkg: "fmt", 1457 renamedPkg: "f", 1458 in: `package main 1459 1460 // comment 1 1461 import ( 1462 f "fmt" /* comment 2 */ 1463 i "io" 1464 )`, 1465 out: `package main 1466 1467 // comment 1 1468 import ( 1469 i "io" 1470 ) 1471 `, 1472 }, 1473 { 1474 name: "import.35", 1475 pkg: "fmt", 1476 in: `package main 1477 1478 // comment 1 1479 import ( 1480 "fmt" 1481 // comment 2 1482 "io" 1483 )`, 1484 out: `package main 1485 1486 // comment 1 1487 import ( 1488 // comment 2 1489 "io" 1490 ) 1491 `, 1492 }, 1493 { 1494 name: "import.36", 1495 pkg: "fmt", 1496 in: `package main 1497 1498 /* comment 1 */ 1499 import ( 1500 "fmt" 1501 /* comment 2 */ 1502 "io" 1503 )`, 1504 out: `package main 1505 1506 /* comment 1 */ 1507 import ( 1508 /* comment 2 */ 1509 "io" 1510 ) 1511 `, 1512 }, 1513 1514 // Issue 20229: MergeLine panic on weird input 1515 { 1516 name: "import.37", 1517 pkg: "io", 1518 in: `package main 1519 import("_" 1520 "io")`, 1521 out: `package main 1522 1523 import ( 1524 "_" 1525 ) 1526 `, 1527 }, 1528 1529 // Issue 28605: Delete specified import, even if that import path is imported under another name 1530 { 1531 name: "import.38", 1532 renamedPkg: "", 1533 pkg: "path", 1534 in: `package main 1535 1536 import ( 1537 "path" 1538 . "path" 1539 _ "path" 1540 pathpkg "path" 1541 ) 1542 `, 1543 out: `package main 1544 1545 import ( 1546 . "path" 1547 _ "path" 1548 pathpkg "path" 1549 ) 1550 `, 1551 }, 1552 { 1553 name: "import.39", 1554 renamedPkg: "pathpkg", 1555 pkg: "path", 1556 in: `package main 1557 1558 import ( 1559 "path" 1560 . "path" 1561 _ "path" 1562 pathpkg "path" 1563 ) 1564 `, 1565 out: `package main 1566 1567 import ( 1568 "path" 1569 . "path" 1570 _ "path" 1571 ) 1572 `, 1573 }, 1574 { 1575 name: "import.40", 1576 renamedPkg: "_", 1577 pkg: "path", 1578 in: `package main 1579 1580 import ( 1581 "path" 1582 . "path" 1583 _ "path" 1584 pathpkg "path" 1585 ) 1586 `, 1587 out: `package main 1588 1589 import ( 1590 "path" 1591 . "path" 1592 pathpkg "path" 1593 ) 1594 `, 1595 }, 1596 { 1597 name: "import.41", 1598 renamedPkg: ".", 1599 pkg: "path", 1600 in: `package main 1601 1602 import ( 1603 "path" 1604 . "path" 1605 _ "path" 1606 pathpkg "path" 1607 ) 1608 `, 1609 out: `package main 1610 1611 import ( 1612 "path" 1613 _ "path" 1614 pathpkg "path" 1615 ) 1616 `, 1617 }, 1618 1619 // Duplicate import declarations, all matching ones are deleted. 1620 { 1621 name: "import.42", 1622 renamedPkg: "f", 1623 pkg: "fmt", 1624 in: `package main 1625 1626 import "fmt" 1627 import "fmt" 1628 import f "fmt" 1629 import f "fmt" 1630 `, 1631 out: `package main 1632 1633 import "fmt" 1634 import "fmt" 1635 `, 1636 }, 1637 { 1638 name: "import.43", 1639 renamedPkg: "x", 1640 pkg: "fmt", 1641 in: `package main 1642 1643 import "fmt" 1644 import "fmt" 1645 import f "fmt" 1646 import f "fmt" 1647 `, 1648 out: `package main 1649 1650 import "fmt" 1651 import "fmt" 1652 import f "fmt" 1653 import f "fmt" 1654 `, 1655 unchanged: true, 1656 }, 1657 } 1658 1659 func TestDeleteImport(t *testing.T) { 1660 for _, test := range deleteTests { 1661 file := parse(t, test.name, test.in) 1662 var before bytes.Buffer 1663 ast.Fprint(&before, fset, file, nil) 1664 deleted := DeleteNamedImport(fset, file, test.renamedPkg, test.pkg) 1665 if got := print(t, test.name, file); got != test.out { 1666 t.Errorf("first run: %s:\ngot: %s\nwant: %s", test.name, got, test.out) 1667 var after bytes.Buffer 1668 ast.Fprint(&after, fset, file, nil) 1669 t.Logf("AST before:\n%s\nAST after:\n%s\n", before.String(), after.String()) 1670 } 1671 if got, want := deleted, !test.unchanged; got != want { 1672 t.Errorf("first run: %s: deleted = %v, want %v", test.name, got, want) 1673 } 1674 1675 // DeleteNamedImport should be idempotent. Verify that by calling it again, 1676 // expecting no change to the AST, and the returned deleted value to always be false. 1677 deleted = DeleteNamedImport(fset, file, test.renamedPkg, test.pkg) 1678 if got := print(t, test.name, file); got != test.out { 1679 t.Errorf("second run: %s:\ngot: %s\nwant: %s", test.name, got, test.out) 1680 } 1681 if got, want := deleted, false; got != want { 1682 t.Errorf("second run: %s: deleted = %v, want %v", test.name, got, want) 1683 } 1684 } 1685 } 1686 1687 func TestDeleteImportAfterAddImport(t *testing.T) { 1688 file := parse(t, "test", `package main 1689 1690 import "os" 1691 `) 1692 if got, want := AddImport(fset, file, "fmt"), true; got != want { 1693 t.Errorf("AddImport: got: %v, want: %v", got, want) 1694 } 1695 if got, want := DeleteImport(fset, file, "fmt"), true; got != want { 1696 t.Errorf("DeleteImport: got: %v, want: %v", got, want) 1697 } 1698 } 1699 1700 type rewriteTest struct { 1701 name string 1702 srcPkg string 1703 dstPkg string 1704 in string 1705 out string 1706 } 1707 1708 var rewriteTests = []rewriteTest{ 1709 { 1710 name: "import.13", 1711 srcPkg: "utf8", 1712 dstPkg: "encoding/utf8", 1713 in: `package main 1714 1715 import ( 1716 "io" 1717 "os" 1718 "utf8" // thanks ken 1719 ) 1720 `, 1721 out: `package main 1722 1723 import ( 1724 "encoding/utf8" // thanks ken 1725 "io" 1726 "os" 1727 ) 1728 `, 1729 }, 1730 { 1731 name: "import.14", 1732 srcPkg: "asn1", 1733 dstPkg: "encoding/asn1", 1734 in: `package main 1735 1736 import ( 1737 "asn1" 1738 "crypto" 1739 "crypto/rsa" 1740 _ "crypto/sha1" 1741 "crypto/x509" 1742 "crypto/x509/pkix" 1743 "time" 1744 ) 1745 1746 var x = 1 1747 `, 1748 out: `package main 1749 1750 import ( 1751 "crypto" 1752 "crypto/rsa" 1753 _ "crypto/sha1" 1754 "crypto/x509" 1755 "crypto/x509/pkix" 1756 "encoding/asn1" 1757 "time" 1758 ) 1759 1760 var x = 1 1761 `, 1762 }, 1763 { 1764 name: "import.15", 1765 srcPkg: "url", 1766 dstPkg: "net/url", 1767 in: `package main 1768 1769 import ( 1770 "bufio" 1771 "net" 1772 "path" 1773 "url" 1774 ) 1775 1776 var x = 1 // comment on x, not on url 1777 `, 1778 out: `package main 1779 1780 import ( 1781 "bufio" 1782 "net" 1783 "net/url" 1784 "path" 1785 ) 1786 1787 var x = 1 // comment on x, not on url 1788 `, 1789 }, 1790 { 1791 name: "import.16", 1792 srcPkg: "http", 1793 dstPkg: "net/http", 1794 in: `package main 1795 1796 import ( 1797 "flag" 1798 "http" 1799 "log" 1800 "text/template" 1801 ) 1802 1803 var addr = flag.String("addr", ":1718", "http service address") // Q=17, R=18 1804 `, 1805 out: `package main 1806 1807 import ( 1808 "flag" 1809 "log" 1810 "net/http" 1811 "text/template" 1812 ) 1813 1814 var addr = flag.String("addr", ":1718", "http service address") // Q=17, R=18 1815 `, 1816 }, 1817 } 1818 1819 func TestRewriteImport(t *testing.T) { 1820 for _, test := range rewriteTests { 1821 file := parse(t, test.name, test.in) 1822 RewriteImport(fset, file, test.srcPkg, test.dstPkg) 1823 if got := print(t, test.name, file); got != test.out { 1824 t.Errorf("%s:\ngot: %s\nwant: %s", test.name, got, test.out) 1825 } 1826 } 1827 } 1828 1829 var importsTests = []struct { 1830 name string 1831 in string 1832 want [][]string 1833 }{ 1834 { 1835 name: "no packages", 1836 in: `package foo 1837 `, 1838 want: nil, 1839 }, 1840 { 1841 name: "one group", 1842 in: `package foo 1843 1844 import ( 1845 "fmt" 1846 "testing" 1847 ) 1848 `, 1849 want: [][]string{{"fmt", "testing"}}, 1850 }, 1851 { 1852 name: "four groups", 1853 in: `package foo 1854 1855 import "C" 1856 import ( 1857 "fmt" 1858 "testing" 1859 1860 "appengine" 1861 1862 "myproject/mylib1" 1863 "myproject/mylib2" 1864 ) 1865 `, 1866 want: [][]string{ 1867 {"C"}, 1868 {"fmt", "testing"}, 1869 {"appengine"}, 1870 {"myproject/mylib1", "myproject/mylib2"}, 1871 }, 1872 }, 1873 { 1874 name: "multiple factored groups", 1875 in: `package foo 1876 1877 import ( 1878 "fmt" 1879 "testing" 1880 1881 "appengine" 1882 ) 1883 import ( 1884 "reflect" 1885 1886 "bytes" 1887 ) 1888 `, 1889 want: [][]string{ 1890 {"fmt", "testing"}, 1891 {"appengine"}, 1892 {"reflect"}, 1893 {"bytes"}, 1894 }, 1895 }, 1896 } 1897 1898 func unquote(s string) string { 1899 res, err := strconv.Unquote(s) 1900 if err != nil { 1901 return "could_not_unquote" 1902 } 1903 return res 1904 } 1905 1906 func TestImports(t *testing.T) { 1907 fset := token.NewFileSet() 1908 for _, test := range importsTests { 1909 f, err := parser.ParseFile(fset, "test.go", test.in, 0) 1910 if err != nil { 1911 t.Errorf("%s: %v", test.name, err) 1912 continue 1913 } 1914 var got [][]string 1915 for _, group := range Imports(fset, f) { 1916 var b []string 1917 for _, spec := range group { 1918 b = append(b, unquote(spec.Path.Value)) 1919 } 1920 got = append(got, b) 1921 } 1922 if !reflect.DeepEqual(got, test.want) { 1923 t.Errorf("Imports(%s)=%v, want %v", test.name, got, test.want) 1924 } 1925 } 1926 } 1927 1928 var usesImportTests = []struct { 1929 name string 1930 path string 1931 in string 1932 want bool 1933 }{ 1934 { 1935 name: "no packages", 1936 path: "io", 1937 in: `package foo 1938 `, 1939 want: false, 1940 }, 1941 { 1942 name: "import.1", 1943 path: "io", 1944 in: `package foo 1945 1946 import "io" 1947 1948 var _ io.Writer 1949 `, 1950 want: true, 1951 }, 1952 { 1953 name: "import.2", 1954 path: "io", 1955 in: `package foo 1956 1957 import "io" 1958 `, 1959 want: false, 1960 }, 1961 { 1962 name: "import.3", 1963 path: "io", 1964 in: `package foo 1965 1966 import "io" 1967 1968 var io = 42 1969 `, 1970 want: false, 1971 }, 1972 { 1973 name: "import.4", 1974 path: "io", 1975 in: `package foo 1976 1977 import i "io" 1978 1979 var _ i.Writer 1980 `, 1981 want: true, 1982 }, 1983 { 1984 name: "import.5", 1985 path: "io", 1986 in: `package foo 1987 1988 import i "io" 1989 `, 1990 want: false, 1991 }, 1992 { 1993 name: "import.6", 1994 path: "io", 1995 in: `package foo 1996 1997 import i "io" 1998 1999 var i = 42 2000 var io = 42 2001 `, 2002 want: false, 2003 }, 2004 { 2005 name: "import.7", 2006 path: "encoding/json", 2007 in: `package foo 2008 2009 import "encoding/json" 2010 2011 var _ json.Encoder 2012 `, 2013 want: true, 2014 }, 2015 { 2016 name: "import.8", 2017 path: "encoding/json", 2018 in: `package foo 2019 2020 import "encoding/json" 2021 `, 2022 want: false, 2023 }, 2024 { 2025 name: "import.9", 2026 path: "encoding/json", 2027 in: `package foo 2028 2029 import "encoding/json" 2030 2031 var json = 42 2032 `, 2033 want: false, 2034 }, 2035 { 2036 name: "import.10", 2037 path: "encoding/json", 2038 in: `package foo 2039 2040 import j "encoding/json" 2041 2042 var _ j.Encoder 2043 `, 2044 want: true, 2045 }, 2046 { 2047 name: "import.11", 2048 path: "encoding/json", 2049 in: `package foo 2050 2051 import j "encoding/json" 2052 `, 2053 want: false, 2054 }, 2055 { 2056 name: "import.12", 2057 path: "encoding/json", 2058 in: `package foo 2059 2060 import j "encoding/json" 2061 2062 var j = 42 2063 var json = 42 2064 `, 2065 want: false, 2066 }, 2067 { 2068 name: "import.13", 2069 path: "io", 2070 in: `package foo 2071 2072 import _ "io" 2073 `, 2074 want: true, 2075 }, 2076 { 2077 name: "import.14", 2078 path: "io", 2079 in: `package foo 2080 2081 import . "io" 2082 `, 2083 want: true, 2084 }, 2085 } 2086 2087 func TestUsesImport(t *testing.T) { 2088 fset := token.NewFileSet() 2089 for _, test := range usesImportTests { 2090 f, err := parser.ParseFile(fset, "test.go", test.in, 0) 2091 if err != nil { 2092 t.Errorf("%s: %v", test.name, err) 2093 continue 2094 } 2095 got := UsesImport(f, test.path) 2096 if got != test.want { 2097 t.Errorf("UsesImport(%s)=%v, want %v", test.name, got, test.want) 2098 } 2099 } 2100 }