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 1687type rewriteTest struct { 1688 name string 1689 srcPkg string 1690 dstPkg string 1691 in string 1692 out string 1693} 1694 1695var rewriteTests = []rewriteTest{ 1696 { 1697 name: "import.13", 1698 srcPkg: "utf8", 1699 dstPkg: "encoding/utf8", 1700 in: `package main 1701 1702import ( 1703 "io" 1704 "os" 1705 "utf8" // thanks ken 1706) 1707`, 1708 out: `package main 1709 1710import ( 1711 "encoding/utf8" // thanks ken 1712 "io" 1713 "os" 1714) 1715`, 1716 }, 1717 { 1718 name: "import.14", 1719 srcPkg: "asn1", 1720 dstPkg: "encoding/asn1", 1721 in: `package main 1722 1723import ( 1724 "asn1" 1725 "crypto" 1726 "crypto/rsa" 1727 _ "crypto/sha1" 1728 "crypto/x509" 1729 "crypto/x509/pkix" 1730 "time" 1731) 1732 1733var x = 1 1734`, 1735 out: `package main 1736 1737import ( 1738 "crypto" 1739 "crypto/rsa" 1740 _ "crypto/sha1" 1741 "crypto/x509" 1742 "crypto/x509/pkix" 1743 "encoding/asn1" 1744 "time" 1745) 1746 1747var x = 1 1748`, 1749 }, 1750 { 1751 name: "import.15", 1752 srcPkg: "url", 1753 dstPkg: "net/url", 1754 in: `package main 1755 1756import ( 1757 "bufio" 1758 "net" 1759 "path" 1760 "url" 1761) 1762 1763var x = 1 // comment on x, not on url 1764`, 1765 out: `package main 1766 1767import ( 1768 "bufio" 1769 "net" 1770 "net/url" 1771 "path" 1772) 1773 1774var x = 1 // comment on x, not on url 1775`, 1776 }, 1777 { 1778 name: "import.16", 1779 srcPkg: "http", 1780 dstPkg: "net/http", 1781 in: `package main 1782 1783import ( 1784 "flag" 1785 "http" 1786 "log" 1787 "text/template" 1788) 1789 1790var addr = flag.String("addr", ":1718", "http service address") // Q=17, R=18 1791`, 1792 out: `package main 1793 1794import ( 1795 "flag" 1796 "log" 1797 "net/http" 1798 "text/template" 1799) 1800 1801var addr = flag.String("addr", ":1718", "http service address") // Q=17, R=18 1802`, 1803 }, 1804} 1805 1806func TestRewriteImport(t *testing.T) { 1807 for _, test := range rewriteTests { 1808 file := parse(t, test.name, test.in) 1809 RewriteImport(fset, file, test.srcPkg, test.dstPkg) 1810 if got := print(t, test.name, file); got != test.out { 1811 t.Errorf("%s:\ngot: %s\nwant: %s", test.name, got, test.out) 1812 } 1813 } 1814} 1815 1816var importsTests = []struct { 1817 name string 1818 in string 1819 want [][]string 1820}{ 1821 { 1822 name: "no packages", 1823 in: `package foo 1824`, 1825 want: nil, 1826 }, 1827 { 1828 name: "one group", 1829 in: `package foo 1830 1831import ( 1832 "fmt" 1833 "testing" 1834) 1835`, 1836 want: [][]string{{"fmt", "testing"}}, 1837 }, 1838 { 1839 name: "four groups", 1840 in: `package foo 1841 1842import "C" 1843import ( 1844 "fmt" 1845 "testing" 1846 1847 "appengine" 1848 1849 "myproject/mylib1" 1850 "myproject/mylib2" 1851) 1852`, 1853 want: [][]string{ 1854 {"C"}, 1855 {"fmt", "testing"}, 1856 {"appengine"}, 1857 {"myproject/mylib1", "myproject/mylib2"}, 1858 }, 1859 }, 1860 { 1861 name: "multiple factored groups", 1862 in: `package foo 1863 1864import ( 1865 "fmt" 1866 "testing" 1867 1868 "appengine" 1869) 1870import ( 1871 "reflect" 1872 1873 "bytes" 1874) 1875`, 1876 want: [][]string{ 1877 {"fmt", "testing"}, 1878 {"appengine"}, 1879 {"reflect"}, 1880 {"bytes"}, 1881 }, 1882 }, 1883} 1884 1885func unquote(s string) string { 1886 res, err := strconv.Unquote(s) 1887 if err != nil { 1888 return "could_not_unquote" 1889 } 1890 return res 1891} 1892 1893func TestImports(t *testing.T) { 1894 fset := token.NewFileSet() 1895 for _, test := range importsTests { 1896 f, err := parser.ParseFile(fset, "test.go", test.in, 0) 1897 if err != nil { 1898 t.Errorf("%s: %v", test.name, err) 1899 continue 1900 } 1901 var got [][]string 1902 for _, group := range Imports(fset, f) { 1903 var b []string 1904 for _, spec := range group { 1905 b = append(b, unquote(spec.Path.Value)) 1906 } 1907 got = append(got, b) 1908 } 1909 if !reflect.DeepEqual(got, test.want) { 1910 t.Errorf("Imports(%s)=%v, want %v", test.name, got, test.want) 1911 } 1912 } 1913} 1914 1915var usesImportTests = []struct { 1916 name string 1917 path string 1918 in string 1919 want bool 1920}{ 1921 { 1922 name: "no packages", 1923 path: "io", 1924 in: `package foo 1925`, 1926 want: false, 1927 }, 1928 { 1929 name: "import.1", 1930 path: "io", 1931 in: `package foo 1932 1933import "io" 1934 1935var _ io.Writer 1936`, 1937 want: true, 1938 }, 1939 { 1940 name: "import.2", 1941 path: "io", 1942 in: `package foo 1943 1944import "io" 1945`, 1946 want: false, 1947 }, 1948 { 1949 name: "import.3", 1950 path: "io", 1951 in: `package foo 1952 1953import "io" 1954 1955var io = 42 1956`, 1957 want: false, 1958 }, 1959 { 1960 name: "import.4", 1961 path: "io", 1962 in: `package foo 1963 1964import i "io" 1965 1966var _ i.Writer 1967`, 1968 want: true, 1969 }, 1970 { 1971 name: "import.5", 1972 path: "io", 1973 in: `package foo 1974 1975import i "io" 1976`, 1977 want: false, 1978 }, 1979 { 1980 name: "import.6", 1981 path: "io", 1982 in: `package foo 1983 1984import i "io" 1985 1986var i = 42 1987var io = 42 1988`, 1989 want: false, 1990 }, 1991 { 1992 name: "import.7", 1993 path: "encoding/json", 1994 in: `package foo 1995 1996import "encoding/json" 1997 1998var _ json.Encoder 1999`, 2000 want: true, 2001 }, 2002 { 2003 name: "import.8", 2004 path: "encoding/json", 2005 in: `package foo 2006 2007import "encoding/json" 2008`, 2009 want: false, 2010 }, 2011 { 2012 name: "import.9", 2013 path: "encoding/json", 2014 in: `package foo 2015 2016import "encoding/json" 2017 2018var json = 42 2019`, 2020 want: false, 2021 }, 2022 { 2023 name: "import.10", 2024 path: "encoding/json", 2025 in: `package foo 2026 2027import j "encoding/json" 2028 2029var _ j.Encoder 2030`, 2031 want: true, 2032 }, 2033 { 2034 name: "import.11", 2035 path: "encoding/json", 2036 in: `package foo 2037 2038import j "encoding/json" 2039`, 2040 want: false, 2041 }, 2042 { 2043 name: "import.12", 2044 path: "encoding/json", 2045 in: `package foo 2046 2047import j "encoding/json" 2048 2049var j = 42 2050var json = 42 2051`, 2052 want: false, 2053 }, 2054 { 2055 name: "import.13", 2056 path: "io", 2057 in: `package foo 2058 2059import _ "io" 2060`, 2061 want: true, 2062 }, 2063 { 2064 name: "import.14", 2065 path: "io", 2066 in: `package foo 2067 2068import . "io" 2069`, 2070 want: true, 2071 }, 2072} 2073 2074func TestUsesImport(t *testing.T) { 2075 fset := token.NewFileSet() 2076 for _, test := range usesImportTests { 2077 f, err := parser.ParseFile(fset, "test.go", test.in, 0) 2078 if err != nil { 2079 t.Errorf("%s: %v", test.name, err) 2080 continue 2081 } 2082 got := UsesImport(f, test.path) 2083 if got != test.want { 2084 t.Errorf("UsesImport(%s)=%v, want %v", test.name, got, test.want) 2085 } 2086 } 2087} 2088