Source file src/pkg/cmd/vendor/golang.org/x/tools/go/ast/astutil/imports.go
1
2
3
4
5
6 package astutil
7
8 import (
9 "fmt"
10 "go/ast"
11 "go/token"
12 "strconv"
13 "strings"
14 )
15
16
17 func AddImport(fset *token.FileSet, f *ast.File, path string) (added bool) {
18 return AddNamedImport(fset, f, "", path)
19 }
20
21
22
23
24
25
26
27
28 func AddNamedImport(fset *token.FileSet, f *ast.File, name, path string) (added bool) {
29 if imports(f, name, path) {
30 return false
31 }
32
33 newImport := &ast.ImportSpec{
34 Path: &ast.BasicLit{
35 Kind: token.STRING,
36 Value: strconv.Quote(path),
37 },
38 }
39 if name != "" {
40 newImport.Name = &ast.Ident{Name: name}
41 }
42
43
44
45
46
47 var (
48 bestMatch = -1
49 lastImport = -1
50 impDecl *ast.GenDecl
51 impIndex = -1
52
53 isThirdPartyPath = isThirdParty(path)
54 )
55 for i, decl := range f.Decls {
56 gen, ok := decl.(*ast.GenDecl)
57 if ok && gen.Tok == token.IMPORT {
58 lastImport = i
59
60
61 if declImports(gen, "C") {
62 continue
63 }
64
65
66 if len(gen.Specs) == 0 && bestMatch == -1 {
67 impDecl = gen
68 }
69
70
71
72
73
74
75
76
77
78
79
80 seenAnyThirdParty := false
81 for j, spec := range gen.Specs {
82 impspec := spec.(*ast.ImportSpec)
83 p := importPath(impspec)
84 n := matchLen(p, path)
85 if n > bestMatch || (bestMatch == 0 && !seenAnyThirdParty && isThirdPartyPath) {
86 bestMatch = n
87 impDecl = gen
88 impIndex = j
89 }
90 seenAnyThirdParty = seenAnyThirdParty || isThirdParty(p)
91 }
92 }
93 }
94
95
96 if impDecl == nil {
97 impDecl = &ast.GenDecl{
98 Tok: token.IMPORT,
99 }
100 if lastImport >= 0 {
101 impDecl.TokPos = f.Decls[lastImport].End()
102 } else {
103
104
105
106
107 impDecl.TokPos = f.Package
108
109 file := fset.File(f.Package)
110 pkgLine := file.Line(f.Package)
111 for _, c := range f.Comments {
112 if file.Line(c.Pos()) > pkgLine {
113 break
114 }
115
116 impDecl.TokPos = c.End() + 2
117 }
118 }
119 f.Decls = append(f.Decls, nil)
120 copy(f.Decls[lastImport+2:], f.Decls[lastImport+1:])
121 f.Decls[lastImport+1] = impDecl
122 }
123
124
125 insertAt := 0
126 if impIndex >= 0 {
127
128 insertAt = impIndex + 1
129 }
130 impDecl.Specs = append(impDecl.Specs, nil)
131 copy(impDecl.Specs[insertAt+1:], impDecl.Specs[insertAt:])
132 impDecl.Specs[insertAt] = newImport
133 pos := impDecl.Pos()
134 if insertAt > 0 {
135
136
137 if spec, ok := impDecl.Specs[insertAt-1].(*ast.ImportSpec); ok && spec.Comment != nil {
138 pos = spec.Comment.End()
139 } else {
140
141
142 pos = impDecl.Specs[insertAt-1].Pos()
143 }
144 }
145 if newImport.Name != nil {
146 newImport.Name.NamePos = pos
147 }
148 newImport.Path.ValuePos = pos
149 newImport.EndPos = pos
150
151
152 if len(impDecl.Specs) == 1 {
153
154 impDecl.Lparen = token.NoPos
155 } else if !impDecl.Lparen.IsValid() {
156
157 impDecl.Lparen = impDecl.Specs[0].Pos()
158 }
159
160 f.Imports = append(f.Imports, newImport)
161
162 if len(f.Decls) <= 1 {
163 return true
164 }
165
166
167 var first *ast.GenDecl
168 for i := 0; i < len(f.Decls); i++ {
169 decl := f.Decls[i]
170 gen, ok := decl.(*ast.GenDecl)
171 if !ok || gen.Tok != token.IMPORT || declImports(gen, "C") {
172 continue
173 }
174 if first == nil {
175 first = gen
176 continue
177 }
178
179
180 first.Lparen = first.Pos()
181
182 for _, spec := range gen.Specs {
183 spec.(*ast.ImportSpec).Path.ValuePos = first.Pos()
184 first.Specs = append(first.Specs, spec)
185 }
186 f.Decls = append(f.Decls[:i], f.Decls[i+1:]...)
187 i--
188 }
189
190 return true
191 }
192
193 func isThirdParty(importPath string) bool {
194
195
196 return strings.Contains(importPath, ".")
197 }
198
199
200
201 func DeleteImport(fset *token.FileSet, f *ast.File, path string) (deleted bool) {
202 return DeleteNamedImport(fset, f, "", path)
203 }
204
205
206
207 func DeleteNamedImport(fset *token.FileSet, f *ast.File, name, path string) (deleted bool) {
208 var delspecs []*ast.ImportSpec
209 var delcomments []*ast.CommentGroup
210
211
212 for i := 0; i < len(f.Decls); i++ {
213 decl := f.Decls[i]
214 gen, ok := decl.(*ast.GenDecl)
215 if !ok || gen.Tok != token.IMPORT {
216 continue
217 }
218 for j := 0; j < len(gen.Specs); j++ {
219 spec := gen.Specs[j]
220 impspec := spec.(*ast.ImportSpec)
221 if importName(impspec) != name || importPath(impspec) != path {
222 continue
223 }
224
225
226
227 delspecs = append(delspecs, impspec)
228 deleted = true
229 copy(gen.Specs[j:], gen.Specs[j+1:])
230 gen.Specs = gen.Specs[:len(gen.Specs)-1]
231
232
233
234 if len(gen.Specs) == 0 {
235 copy(f.Decls[i:], f.Decls[i+1:])
236 f.Decls = f.Decls[:len(f.Decls)-1]
237 i--
238 break
239 } else if len(gen.Specs) == 1 {
240 if impspec.Doc != nil {
241 delcomments = append(delcomments, impspec.Doc)
242 }
243 if impspec.Comment != nil {
244 delcomments = append(delcomments, impspec.Comment)
245 }
246 for _, cg := range f.Comments {
247
248 if cg.End() < impspec.Pos() && fset.Position(cg.End()).Line == fset.Position(impspec.Pos()).Line {
249 delcomments = append(delcomments, cg)
250 break
251 }
252 }
253
254 spec := gen.Specs[0].(*ast.ImportSpec)
255
256
257 if spec.Doc != nil {
258 for fset.Position(gen.TokPos).Line+1 < fset.Position(spec.Doc.Pos()).Line {
259 fset.File(gen.TokPos).MergeLine(fset.Position(gen.TokPos).Line)
260 }
261 }
262 for _, cg := range f.Comments {
263 if cg.End() < spec.Pos() && fset.Position(cg.End()).Line == fset.Position(spec.Pos()).Line {
264 for fset.Position(gen.TokPos).Line+1 < fset.Position(spec.Pos()).Line {
265 fset.File(gen.TokPos).MergeLine(fset.Position(gen.TokPos).Line)
266 }
267 break
268 }
269 }
270 }
271 if j > 0 {
272 lastImpspec := gen.Specs[j-1].(*ast.ImportSpec)
273 lastLine := fset.Position(lastImpspec.Path.ValuePos).Line
274 line := fset.Position(impspec.Path.ValuePos).Line
275
276
277
278 if line-lastLine > 1 {
279
280
281
282 } else if line != fset.File(gen.Rparen).LineCount() {
283
284 fset.File(gen.Rparen).MergeLine(line)
285 }
286 }
287 j--
288 }
289 }
290
291
292 for i := 0; i < len(f.Imports); i++ {
293 imp := f.Imports[i]
294 for j, del := range delspecs {
295 if imp == del {
296 copy(f.Imports[i:], f.Imports[i+1:])
297 f.Imports = f.Imports[:len(f.Imports)-1]
298 copy(delspecs[j:], delspecs[j+1:])
299 delspecs = delspecs[:len(delspecs)-1]
300 i--
301 break
302 }
303 }
304 }
305
306
307 for i := 0; i < len(f.Comments); i++ {
308 cg := f.Comments[i]
309 for j, del := range delcomments {
310 if cg == del {
311 copy(f.Comments[i:], f.Comments[i+1:])
312 f.Comments = f.Comments[:len(f.Comments)-1]
313 copy(delcomments[j:], delcomments[j+1:])
314 delcomments = delcomments[:len(delcomments)-1]
315 i--
316 break
317 }
318 }
319 }
320
321 if len(delspecs) > 0 {
322 panic(fmt.Sprintf("deleted specs from Decls but not Imports: %v", delspecs))
323 }
324
325 return
326 }
327
328
329 func RewriteImport(fset *token.FileSet, f *ast.File, oldPath, newPath string) (rewrote bool) {
330 for _, imp := range f.Imports {
331 if importPath(imp) == oldPath {
332 rewrote = true
333
334
335 imp.EndPos = imp.End()
336 imp.Path.Value = strconv.Quote(newPath)
337 }
338 }
339 return
340 }
341
342
343 func UsesImport(f *ast.File, path string) (used bool) {
344 spec := importSpec(f, path)
345 if spec == nil {
346 return
347 }
348
349 name := spec.Name.String()
350 switch name {
351 case "<nil>":
352
353
354 lastSlash := strings.LastIndex(path, "/")
355 if lastSlash == -1 {
356 name = path
357 } else {
358 name = path[lastSlash+1:]
359 }
360 case "_", ".":
361
362 return true
363 }
364
365 ast.Walk(visitFn(func(n ast.Node) {
366 sel, ok := n.(*ast.SelectorExpr)
367 if ok && isTopName(sel.X, name) {
368 used = true
369 }
370 }), f)
371
372 return
373 }
374
375 type visitFn func(node ast.Node)
376
377 func (fn visitFn) Visit(node ast.Node) ast.Visitor {
378 fn(node)
379 return fn
380 }
381
382
383 func imports(f *ast.File, name, path string) bool {
384 for _, s := range f.Imports {
385 if importName(s) == name && importPath(s) == path {
386 return true
387 }
388 }
389 return false
390 }
391
392
393
394 func importSpec(f *ast.File, path string) *ast.ImportSpec {
395 for _, s := range f.Imports {
396 if importPath(s) == path {
397 return s
398 }
399 }
400 return nil
401 }
402
403
404
405 func importName(s *ast.ImportSpec) string {
406 if s.Name == nil {
407 return ""
408 }
409 return s.Name.Name
410 }
411
412
413
414 func importPath(s *ast.ImportSpec) string {
415 t, err := strconv.Unquote(s.Path.Value)
416 if err != nil {
417 return ""
418 }
419 return t
420 }
421
422
423 func declImports(gen *ast.GenDecl, path string) bool {
424 if gen.Tok != token.IMPORT {
425 return false
426 }
427 for _, spec := range gen.Specs {
428 impspec := spec.(*ast.ImportSpec)
429 if importPath(impspec) == path {
430 return true
431 }
432 }
433 return false
434 }
435
436
437 func matchLen(x, y string) int {
438 n := 0
439 for i := 0; i < len(x) && i < len(y) && x[i] == y[i]; i++ {
440 if x[i] == '/' {
441 n++
442 }
443 }
444 return n
445 }
446
447
448 func isTopName(n ast.Expr, name string) bool {
449 id, ok := n.(*ast.Ident)
450 return ok && id.Name == name && id.Obj == nil
451 }
452
453
454 func Imports(fset *token.FileSet, f *ast.File) [][]*ast.ImportSpec {
455 var groups [][]*ast.ImportSpec
456
457 for _, decl := range f.Decls {
458 genDecl, ok := decl.(*ast.GenDecl)
459 if !ok || genDecl.Tok != token.IMPORT {
460 break
461 }
462
463 group := []*ast.ImportSpec{}
464
465 var lastLine int
466 for _, spec := range genDecl.Specs {
467 importSpec := spec.(*ast.ImportSpec)
468 pos := importSpec.Path.ValuePos
469 line := fset.Position(pos).Line
470 if lastLine > 0 && pos > 0 && line-lastLine > 1 {
471 groups = append(groups, group)
472 group = []*ast.ImportSpec{}
473 }
474 group = append(group, importSpec)
475 lastLine = line
476 }
477 groups = append(groups, group)
478 }
479
480 return groups
481 }
482
View as plain text