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 5package astutil 6 7import ( 8 "bytes" 9 "go/ast" 10 "go/format" 11 "go/parser" 12 "go/token" 13 "reflect" 14 "strconv" 15 "testing" 16) 17 18var fset = token.NewFileSet() 19 20func 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 28func 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 36type 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 45var addTests = []test{ 46 { 47 name: "leave os alone", 48 pkg: "os", 49 in: `package main 50 51import ( 52 "os" 53) 54`, 55 out: `package main 56 57import ( 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 70import "os" 71`, 72 }, 73 { 74 name: "import.2", 75 pkg: "os", 76 in: `package main 77 78// Comment 79import "C" 80`, 81 out: `package main 82 83// Comment 84import "C" 85import "os" 86`, 87 }, 88 { 89 name: "import.3", 90 pkg: "os", 91 in: `package main 92 93// Comment 94import "C" 95 96import ( 97 "io" 98 "utf8" 99) 100`, 101 out: `package main 102 103// Comment 104import "C" 105 106import ( 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 119import "C" 120 121import ( 122 "a" 123 "b" 124 125 "x/w" 126 127 "d/f" 128) 129`, 130 out: `package main 131 132// Comment 133import "C" 134 135import ( 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 152import "C" 153 154import ( 155 "bytes" 156 "os" 157 158 "d.com/f" 159) 160`, 161 out: `package main 162 163// Comment 164import "C" 165 166import ( 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 181import "C" 182 183import ( 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 198import "C" 199 200import ( 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 219import ( 220 "x.org/a" 221 222 "y.org/b" 223) 224`, 225 out: `package main 226 227import ( 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 240import "os" 241 242`, 243 out: `package main 244 245import ( 246 "bytes" 247 "os" 248) 249`, 250 }, 251 { 252 name: "import into singular group with comment", 253 pkg: "bytes", 254 in: `package main 255 256import /* why */ /* comment here? */ "os" 257 258`, 259 out: `package main 260 261import /* 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 272import ( 273 // comment before bytes 274 "bytes" 275 "os" 276) 277 278`, 279 out: `package main 280 281import ( 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 295import "os" 296 297`, 298 out: `package main 299 300import ( 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. 312type T struct { 313 t time.Time 314} 315`, 316 out: `package main 317 318import "time" 319 320// This is a comment before a struct. 321type T struct { 322 t time.Time 323} 324`, 325 }, 326 { 327 name: "issue 8729 import C", 328 pkg: "time", 329 in: `package main 330 331import "C" 332 333// comment 334type T time.Time 335`, 336 out: `package main 337 338import "C" 339import "time" 340 341// comment 342type T time.Time 343`, 344 }, 345 { 346 name: "issue 8729 empty import", 347 pkg: "time", 348 in: `package main 349 350import () 351 352// comment 353type T time.Time 354`, 355 out: `package main 356 357import "time" 358 359// comment 360type T time.Time 361`, 362 }, 363 { 364 name: "issue 8729 comment on package line", 365 pkg: "time", 366 in: `package main // comment 367 368type T time.Time 369`, 370 out: `package main // comment 371 372import "time" 373 374type T time.Time 375`, 376 }, 377 { 378 name: "issue 8729 comment after package", 379 pkg: "time", 380 in: `package main 381// comment 382 383type T time.Time 384`, 385 out: `package main 386 387import "time" 388 389// comment 390 391type T time.Time 392`, 393 }, 394 { 395 name: "issue 8729 comment before and on package line", 396 pkg: "time", 397 in: `// comment before 398package main // comment on 399 400type T time.Time 401`, 402 out: `// comment before 403package main // comment on 404 405import "time" 406 407type 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 417import ( 418 "flag" 419 "testing" 420 421 "rsc.io/p" 422) 423`, 424 out: `package main 425 426import ( 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 441import ( 442 "bytes" // a 443 "log" // c 444) 445`, 446 out: `package main 447 448import ( 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 460import ( 461 "fmt" // b 462 "log" // c 463) 464`, 465 out: `package main 466 467import ( 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 479import ( 480 "bytes" // a 481 "fmt" // b 482) 483`, 484 out: `package main 485 486import ( 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 499import "bytes" 500import "fmt" 501`, 502 out: `package main 503 504import ( 505 "bufio" 506 "bytes" 507 "fmt" 508) 509`, 510 }, 511 { 512 name: "issue 14075 update position", 513 pkg: "bufio", 514 in: `package main 515 516import "bytes" 517import ( 518 "fmt" 519) 520`, 521 out: `package main 522 523import ( 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 536import "C" 537 538import "bytes" 539import "fmt" 540`, 541 out: `package main 542 543// Comment 544import "C" 545 546import ( 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 559import "C" 560import "fmt" 561`, 562 out: `package main 563 564// Comment 565import "C" 566import ( 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 578import "C" 579import "fmt" 580`, 581 out: `package main 582 583// Comment 584import "C" 585import ( 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 597import "C" 598`, 599 out: `package main 600 601// Comment 602import "C" 603import "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 611import "bufio" 612import "net/url" 613`, 614 out: `package main 615 616import ( 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 628import ( 629 "bufio" 630 "net/url" 631) 632`, 633 out: `package main 634 635import ( 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 647import "bufio" 648import "bytes" 649import "errors" 650`, 651 out: `package main 652 653import ( 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 669import ( 670 . "path" 671 _ "path" 672 pathpkg "path" 673) 674`, 675 out: `package main 676 677import ( 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 691import ( 692 "path" 693 . "path" 694 _ "path" 695) 696`, 697 out: `package main 698 699import ( 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 713import ( 714 "path" 715 . "path" 716 pathpkg "path" 717) 718`, 719 out: `package main 720 721import ( 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 735import ( 736 "path" 737 _ "path" 738 pathpkg "path" 739) 740`, 741 out: `package main 742 743import ( 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 758import "fmt" 759import "fmt" 760import f "fmt" 761import f "fmt" 762`, 763 out: `package main 764 765import "fmt" 766import "fmt" 767import f "fmt" 768import f "fmt" 769`, 770 unchanged: true, 771 }, 772} 773 774func 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 802func 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 808import ( 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 818func 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 824import ( 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. 835func TestDoubleAddImportWithDeclComment(t *testing.T) { 836 file := parse(t, "doubleimport", `package main 837 838import ( 839) 840 841// comment 842type I int 843`) 844 // The AddImport order here matters. 845 AddImport(fset, file, "golang.org/x/tools/go/ast/astutil") 846 AddImport(fset, file, "os") 847 want := `package main 848 849import ( 850 "golang.org/x/tools/go/ast/astutil" 851 "os" 852) 853 854// comment 855type 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 862var deleteTests = []test{ 863 { 864 name: "import.4", 865 pkg: "os", 866 in: `package main 867 868import ( 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 881import "C" 882import "os" 883`, 884 out: `package main 885 886// Comment 887import "C" 888`, 889 }, 890 { 891 name: "import.6", 892 pkg: "os", 893 in: `package main 894 895// Comment 896import "C" 897 898import ( 899 "io" 900 "os" 901 "utf8" 902) 903`, 904 out: `package main 905 906// Comment 907import "C" 908 909import ( 910 "io" 911 "utf8" 912) 913`, 914 }, 915 { 916 name: "import.7", 917 pkg: "io", 918 in: `package main 919 920import ( 921 "io" // a 922 "os" // b 923 "utf8" // c 924) 925`, 926 out: `package main 927 928import ( 929 // a 930 "os" // b 931 "utf8" // c 932) 933`, 934 }, 935 { 936 name: "import.8", 937 pkg: "os", 938 in: `package main 939 940import ( 941 "io" // a 942 "os" // b 943 "utf8" // c 944) 945`, 946 out: `package main 947 948import ( 949 "io" // a 950 // b 951 "utf8" // c 952) 953`, 954 }, 955 { 956 name: "import.9", 957 pkg: "utf8", 958 in: `package main 959 960import ( 961 "io" // a 962 "os" // b 963 "utf8" // c 964) 965`, 966 out: `package main 967 968import ( 969 "io" // a 970 "os" // b 971 // c 972) 973`, 974 }, 975 { 976 name: "import.10", 977 pkg: "io", 978 in: `package main 979 980import ( 981 "io" 982 "os" 983 "utf8" 984) 985`, 986 out: `package main 987 988import ( 989 "os" 990 "utf8" 991) 992`, 993 }, 994 { 995 name: "import.11", 996 pkg: "os", 997 in: `package main 998 999import ( 1000 "io" 1001 "os" 1002 "utf8" 1003) 1004`, 1005 out: `package main 1006 1007import ( 1008 "io" 1009 "utf8" 1010) 1011`, 1012 }, 1013 { 1014 name: "import.12", 1015 pkg: "utf8", 1016 in: `package main 1017 1018import ( 1019 "io" 1020 "os" 1021 "utf8" 1022) 1023`, 1024 out: `package main 1025 1026import ( 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 1044import ( 1045 "fmt" 1046 1047 "io" 1048 "os" 1049 "utf8" 1050 1051 "go/format" 1052) 1053`, 1054 out: `package main 1055 1056import ( 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 1071import ( 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 1083import ( 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 1099import ( 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 1112import ( 1113 "toil" 1114 "bubble" 1115 "bubble" 1116 "trouble" 1117) 1118`, 1119 out: `package main 1120 1121import ( 1122 "toil" 1123 "trouble" 1124) 1125`, 1126 }, 1127 { 1128 name: "import.17", 1129 pkg: "quad", 1130 in: `package main 1131 1132import ( 1133 "quad" 1134 "quad" 1135) 1136 1137import ( 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 1151import ( 1152 "fmt" 1153 x "fmt" 1154) 1155`, 1156 out: `package main 1157 1158import ( 1159 "fmt" 1160) 1161`, 1162 }, 1163 { 1164 name: "import.18", 1165 renamedPkg: "x", 1166 pkg: "fmt", 1167 in: `package main 1168 1169import x "fmt" 1170import y "fmt" 1171`, 1172 out: `package main 1173 1174import y "fmt" 1175`, 1176 }, 1177 // Issue #15432, #18051 1178 { 1179 name: "import.19", 1180 pkg: "fmt", 1181 in: `package main 1182 1183import ( 1184 "fmt" 1185 1186 // Some comment. 1187 "io" 1188)`, 1189 out: `package main 1190 1191import ( 1192 // Some comment. 1193 "io" 1194) 1195`, 1196 }, 1197 { 1198 name: "import.20", 1199 pkg: "fmt", 1200 in: `package main 1201 1202import ( 1203 "fmt" 1204 1205 // Some 1206 // comment. 1207 "io" 1208)`, 1209 out: `package main 1210 1211import ( 1212 // Some 1213 // comment. 1214 "io" 1215) 1216`, 1217 }, 1218 { 1219 name: "import.21", 1220 pkg: "fmt", 1221 in: `package main 1222 1223import ( 1224 "fmt" 1225 1226 /* 1227 Some 1228 comment. 1229 */ 1230 "io" 1231)`, 1232 out: `package main 1233 1234import ( 1235 /* 1236 Some 1237 comment. 1238 */ 1239 "io" 1240) 1241`, 1242 }, 1243 { 1244 name: "import.22", 1245 pkg: "fmt", 1246 in: `package main 1247 1248import ( 1249 /* Some */ 1250 // comment. 1251 "io" 1252 "fmt" 1253)`, 1254 out: `package main 1255 1256import ( 1257 /* Some */ 1258 // comment. 1259 "io" 1260) 1261`, 1262 }, 1263 { 1264 name: "import.23", 1265 pkg: "fmt", 1266 in: `package main 1267 1268import ( 1269 // comment 1 1270 "fmt" 1271 // comment 2 1272 "io" 1273)`, 1274 out: `package main 1275 1276import ( 1277 // comment 2 1278 "io" 1279) 1280`, 1281 }, 1282 { 1283 name: "import.24", 1284 pkg: "fmt", 1285 in: `package main 1286 1287import ( 1288 "fmt" // comment 1 1289 "io" // comment 2 1290)`, 1291 out: `package main 1292 1293import ( 1294 "io" // comment 2 1295) 1296`, 1297 }, 1298 { 1299 name: "import.25", 1300 pkg: "fmt", 1301 in: `package main 1302 1303import ( 1304 "fmt" 1305 /* comment */ "io" 1306)`, 1307 out: `package main 1308 1309import ( 1310 /* comment */ "io" 1311) 1312`, 1313 }, 1314 { 1315 name: "import.26", 1316 pkg: "fmt", 1317 in: `package main 1318 1319import ( 1320 "fmt" 1321 "io" /* comment */ 1322)`, 1323 out: `package main 1324 1325import ( 1326 "io" /* comment */ 1327) 1328`, 1329 }, 1330 { 1331 name: "import.27", 1332 pkg: "fmt", 1333 in: `package main 1334 1335import ( 1336 "fmt" /* comment */ 1337 "io" 1338)`, 1339 out: `package main 1340 1341import ( 1342 "io" 1343) 1344`, 1345 }, 1346 { 1347 name: "import.28", 1348 pkg: "fmt", 1349 in: `package main 1350 1351import ( 1352 /* comment */ "fmt" 1353 "io" 1354)`, 1355 out: `package main 1356 1357import ( 1358 "io" 1359) 1360`, 1361 }, 1362 { 1363 name: "import.29", 1364 pkg: "fmt", 1365 in: `package main 1366 1367// comment 1 1368import ( 1369 "fmt" 1370 "io" // comment 2 1371)`, 1372 out: `package main 1373 1374// comment 1 1375import ( 1376 "io" // comment 2 1377) 1378`, 1379 }, 1380 { 1381 name: "import.30", 1382 pkg: "fmt", 1383 in: `package main 1384 1385// comment 1 1386import ( 1387 "fmt" // comment 2 1388 "io" 1389)`, 1390 out: `package main 1391 1392// comment 1 1393import ( 1394 "io" 1395) 1396`, 1397 }, 1398 { 1399 name: "import.31", 1400 pkg: "fmt", 1401 in: `package main 1402 1403// comment 1 1404import ( 1405 "fmt" 1406 /* comment 2 */ "io" 1407)`, 1408 out: `package main 1409 1410// comment 1 1411import ( 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 1423import ( 1424 f "fmt" 1425 /* comment 2 */ i "io" 1426)`, 1427 out: `package main 1428 1429// comment 1 1430import ( 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 1442import ( 1443 /* comment 2 */ f "fmt" 1444 i "io" 1445)`, 1446 out: `package main 1447 1448// comment 1 1449import ( 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 1461import ( 1462 f "fmt" /* comment 2 */ 1463 i "io" 1464)`, 1465 out: `package main 1466 1467// comment 1 1468import ( 1469 i "io" 1470) 1471`, 1472 }, 1473 { 1474 name: "import.35", 1475 pkg: "fmt", 1476 in: `package main 1477 1478// comment 1 1479import ( 1480 "fmt" 1481 // comment 2 1482 "io" 1483)`, 1484 out: `package main 1485 1486// comment 1 1487import ( 1488 // comment 2 1489 "io" 1490) 1491`, 1492 }, 1493 { 1494 name: "import.36", 1495 pkg: "fmt", 1496 in: `package main 1497 1498/* comment 1 */ 1499import ( 1500 "fmt" 1501 /* comment 2 */ 1502 "io" 1503)`, 1504 out: `package main 1505 1506/* comment 1 */ 1507import ( 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 1519import("_" 1520"io")`, 1521 out: `package main 1522 1523import ( 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 1536import ( 1537 "path" 1538 . "path" 1539 _ "path" 1540 pathpkg "path" 1541) 1542`, 1543 out: `package main 1544 1545import ( 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 1558import ( 1559 "path" 1560 . "path" 1561 _ "path" 1562 pathpkg "path" 1563) 1564`, 1565 out: `package main 1566 1567import ( 1568 "path" 1569 . "path" 1570 _ "path" 1571) 1572`, 1573 }, 1574 { 1575 name: "import.40", 1576 renamedPkg: "_", 1577 pkg: "path", 1578 in: `package main 1579 1580import ( 1581 "path" 1582 . "path" 1583 _ "path" 1584 pathpkg "path" 1585) 1586`, 1587 out: `package main 1588 1589import ( 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 1602import ( 1603 "path" 1604 . "path" 1605 _ "path" 1606 pathpkg "path" 1607) 1608`, 1609 out: `package main 1610 1611import ( 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 1626import "fmt" 1627import "fmt" 1628import f "fmt" 1629import f "fmt" 1630`, 1631 out: `package main 1632 1633import "fmt" 1634import "fmt" 1635`, 1636 }, 1637 { 1638 name: "import.43", 1639 renamedPkg: "x", 1640 pkg: "fmt", 1641 in: `package main 1642 1643import "fmt" 1644import "fmt" 1645import f "fmt" 1646import f "fmt" 1647`, 1648 out: `package main 1649 1650import "fmt" 1651import "fmt" 1652import f "fmt" 1653import f "fmt" 1654`, 1655 unchanged: true, 1656 }, 1657} 1658 1659func 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 1687func TestDeleteImportAfterAddImport(t *testing.T) { 1688 file := parse(t, "test", `package main 1689 1690import "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 1700type rewriteTest struct { 1701 name string 1702 srcPkg string 1703 dstPkg string 1704 in string 1705 out string 1706} 1707 1708var rewriteTests = []rewriteTest{ 1709 { 1710 name: "import.13", 1711 srcPkg: "utf8", 1712 dstPkg: "encoding/utf8", 1713 in: `package main 1714 1715import ( 1716 "io" 1717 "os" 1718 "utf8" // thanks ken 1719) 1720`, 1721 out: `package main 1722 1723import ( 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 1736import ( 1737 "asn1" 1738 "crypto" 1739 "crypto/rsa" 1740 _ "crypto/sha1" 1741 "crypto/x509" 1742 "crypto/x509/pkix" 1743 "time" 1744) 1745 1746var x = 1 1747`, 1748 out: `package main 1749 1750import ( 1751 "crypto" 1752 "crypto/rsa" 1753 _ "crypto/sha1" 1754 "crypto/x509" 1755 "crypto/x509/pkix" 1756 "encoding/asn1" 1757 "time" 1758) 1759 1760var x = 1 1761`, 1762 }, 1763 { 1764 name: "import.15", 1765 srcPkg: "url", 1766 dstPkg: "net/url", 1767 in: `package main 1768 1769import ( 1770 "bufio" 1771 "net" 1772 "path" 1773 "url" 1774) 1775 1776var x = 1 // comment on x, not on url 1777`, 1778 out: `package main 1779 1780import ( 1781 "bufio" 1782 "net" 1783 "net/url" 1784 "path" 1785) 1786 1787var 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 1796import ( 1797 "flag" 1798 "http" 1799 "log" 1800 "text/template" 1801) 1802 1803var addr = flag.String("addr", ":1718", "http service address") // Q=17, R=18 1804`, 1805 out: `package main 1806 1807import ( 1808 "flag" 1809 "log" 1810 "net/http" 1811 "text/template" 1812) 1813 1814var addr = flag.String("addr", ":1718", "http service address") // Q=17, R=18 1815`, 1816 }, 1817} 1818 1819func 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 1829var 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 1844import ( 1845 "fmt" 1846 "testing" 1847) 1848`, 1849 want: [][]string{{"fmt", "testing"}}, 1850 }, 1851 { 1852 name: "four groups", 1853 in: `package foo 1854 1855import "C" 1856import ( 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 1877import ( 1878 "fmt" 1879 "testing" 1880 1881 "appengine" 1882) 1883import ( 1884 "reflect" 1885 1886 "bytes" 1887) 1888`, 1889 want: [][]string{ 1890 {"fmt", "testing"}, 1891 {"appengine"}, 1892 {"reflect"}, 1893 {"bytes"}, 1894 }, 1895 }, 1896} 1897 1898func 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 1906func 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 1928var 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 1946import "io" 1947 1948var _ io.Writer 1949`, 1950 want: true, 1951 }, 1952 { 1953 name: "import.2", 1954 path: "io", 1955 in: `package foo 1956 1957import "io" 1958`, 1959 want: false, 1960 }, 1961 { 1962 name: "import.3", 1963 path: "io", 1964 in: `package foo 1965 1966import "io" 1967 1968var io = 42 1969`, 1970 want: false, 1971 }, 1972 { 1973 name: "import.4", 1974 path: "io", 1975 in: `package foo 1976 1977import i "io" 1978 1979var _ i.Writer 1980`, 1981 want: true, 1982 }, 1983 { 1984 name: "import.5", 1985 path: "io", 1986 in: `package foo 1987 1988import i "io" 1989`, 1990 want: false, 1991 }, 1992 { 1993 name: "import.6", 1994 path: "io", 1995 in: `package foo 1996 1997import i "io" 1998 1999var i = 42 2000var io = 42 2001`, 2002 want: false, 2003 }, 2004 { 2005 name: "import.7", 2006 path: "encoding/json", 2007 in: `package foo 2008 2009import "encoding/json" 2010 2011var _ json.Encoder 2012`, 2013 want: true, 2014 }, 2015 { 2016 name: "import.8", 2017 path: "encoding/json", 2018 in: `package foo 2019 2020import "encoding/json" 2021`, 2022 want: false, 2023 }, 2024 { 2025 name: "import.9", 2026 path: "encoding/json", 2027 in: `package foo 2028 2029import "encoding/json" 2030 2031var json = 42 2032`, 2033 want: false, 2034 }, 2035 { 2036 name: "import.10", 2037 path: "encoding/json", 2038 in: `package foo 2039 2040import j "encoding/json" 2041 2042var _ j.Encoder 2043`, 2044 want: true, 2045 }, 2046 { 2047 name: "import.11", 2048 path: "encoding/json", 2049 in: `package foo 2050 2051import j "encoding/json" 2052`, 2053 want: false, 2054 }, 2055 { 2056 name: "import.12", 2057 path: "encoding/json", 2058 in: `package foo 2059 2060import j "encoding/json" 2061 2062var j = 42 2063var json = 42 2064`, 2065 want: false, 2066 }, 2067 { 2068 name: "import.13", 2069 path: "io", 2070 in: `package foo 2071 2072import _ "io" 2073`, 2074 want: true, 2075 }, 2076 { 2077 name: "import.14", 2078 path: "io", 2079 in: `package foo 2080 2081import . "io" 2082`, 2083 want: true, 2084 }, 2085} 2086 2087func 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} 2101