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