]> git.ipfire.org Git - thirdparty/gcc.git/blame - libgo/go/go/ast/import.go
libgo: update to Go1.14beta1
[thirdparty/gcc.git] / libgo / go / go / ast / import.go
CommitLineData
9c63abc9
ILT
1// Copyright 2011 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 ast
6
7import (
8 "go/token"
9 "sort"
10 "strconv"
11)
12
13// SortImports sorts runs of consecutive import lines in import blocks in f.
f038dae6 14// It also removes duplicate imports when it is possible to do so without data loss.
9c63abc9
ILT
15func SortImports(fset *token.FileSet, f *File) {
16 for _, d := range f.Decls {
17 d, ok := d.(*GenDecl)
18 if !ok || d.Tok != token.IMPORT {
19 // Not an import declaration, so we're done.
20 // Imports are always first.
21 break
22 }
23
a42a906c 24 if !d.Lparen.IsValid() {
9c63abc9
ILT
25 // Not a block: sorted by default.
26 continue
27 }
28
29 // Identify and sort runs of specs on successive lines.
30 i := 0
f038dae6 31 specs := d.Specs[:0]
9c63abc9 32 for j, s := range d.Specs {
5a8ea165 33 if j > i && lineAt(fset, s.Pos()) > 1+lineAt(fset, d.Specs[j-1].End()) {
22b955cc 34 // j begins a new run. End this one.
f038dae6 35 specs = append(specs, sortSpecs(fset, f, d.Specs[i:j])...)
9c63abc9
ILT
36 i = j
37 }
38 }
f038dae6
ILT
39 specs = append(specs, sortSpecs(fset, f, d.Specs[i:])...)
40 d.Specs = specs
41
42 // Deduping can leave a blank line before the rparen; clean that up.
43 if len(d.Specs) > 0 {
44 lastSpec := d.Specs[len(d.Specs)-1]
5a8ea165
ILT
45 lastLine := lineAt(fset, lastSpec.Pos())
46 rParenLine := lineAt(fset, d.Rparen)
f98dd1a3
ILT
47 for rParenLine > lastLine+1 {
48 rParenLine--
49 fset.File(d.Rparen).MergeLine(rParenLine)
f038dae6
ILT
50 }
51 }
9c63abc9
ILT
52 }
53}
54
5a8ea165
ILT
55func lineAt(fset *token.FileSet, pos token.Pos) int {
56 return fset.PositionFor(pos, false).Line
57}
58
9c63abc9
ILT
59func importPath(s Spec) string {
60 t, err := strconv.Unquote(s.(*ImportSpec).Path.Value)
61 if err == nil {
62 return t
63 }
64 return ""
65}
66
f038dae6
ILT
67func importName(s Spec) string {
68 n := s.(*ImportSpec).Name
69 if n == nil {
70 return ""
71 }
72 return n.Name
73}
74
75func importComment(s Spec) string {
76 c := s.(*ImportSpec).Comment
77 if c == nil {
78 return ""
79 }
80 return c.Text()
81}
82
83// collapse indicates whether prev may be removed, leaving only next.
84func collapse(prev, next Spec) bool {
85 if importPath(next) != importPath(prev) || importName(next) != importName(prev) {
86 return false
87 }
88 return prev.(*ImportSpec).Comment == nil
89}
90
9c63abc9
ILT
91type posSpan struct {
92 Start token.Pos
93 End token.Pos
94}
95
5a8ea165
ILT
96type cgPos struct {
97 left bool // true if comment is to the left of the spec, false otherwise.
98 cg *CommentGroup
99}
100
f038dae6
ILT
101func sortSpecs(fset *token.FileSet, f *File, specs []Spec) []Spec {
102 // Can't short-circuit here even if specs are already sorted,
103 // since they might yet need deduplication.
104 // A lone import, however, may be safely ignored.
105 if len(specs) <= 1 {
106 return specs
9c63abc9
ILT
107 }
108
109 // Record positions for specs.
110 pos := make([]posSpan, len(specs))
111 for i, s := range specs {
9af4cb95 112 pos[i] = posSpan{s.Pos(), s.End()}
9c63abc9
ILT
113 }
114
115 // Identify comments in this range.
5a8ea165
ILT
116 begSpecs := pos[0].Start
117 endSpecs := pos[len(pos)-1].End
118 beg := fset.File(begSpecs).LineStart(lineAt(fset, begSpecs))
119 endLine := lineAt(fset, endSpecs)
120 endFile := fset.File(endSpecs)
121 var end token.Pos
122 if endLine == endFile.LineCount() {
123 end = endSpecs
124 } else {
125 end = endFile.LineStart(endLine + 1) // beginning of next line
126 }
127 first := len(f.Comments)
128 last := -1
9c63abc9 129 for i, g := range f.Comments {
5a8ea165 130 if g.End() >= end {
656297e1 131 break
aa8901e9 132 }
5a8ea165
ILT
133 // g.End() < end
134 if beg <= g.Pos() {
135 // comment is within the range [beg, end[ of import declarations
136 if i < first {
137 first = i
138 }
139 if i > last {
140 last = i
141 }
142 }
9c63abc9 143 }
9c63abc9 144
5a8ea165
ILT
145 var comments []*CommentGroup
146 if last >= 0 {
147 comments = f.Comments[first : last+1]
148 }
149
150 // Assign each comment to the import spec on the same line.
151 importComments := map[*ImportSpec][]cgPos{}
9c63abc9
ILT
152 specIndex := 0
153 for _, g := range comments {
154 for specIndex+1 < len(specs) && pos[specIndex+1].Start <= g.Pos() {
155 specIndex++
156 }
5a8ea165
ILT
157 var left bool
158 // A block comment can appear before the first import spec.
159 if specIndex == 0 && pos[specIndex].Start > g.Pos() {
160 left = true
161 } else if specIndex+1 < len(specs) && // Or it can appear on the left of an import spec.
162 lineAt(fset, pos[specIndex].Start)+1 == lineAt(fset, g.Pos()) {
163 specIndex++
164 left = true
165 }
9c63abc9 166 s := specs[specIndex].(*ImportSpec)
5a8ea165 167 importComments[s] = append(importComments[s], cgPos{left: left, cg: g})
9c63abc9
ILT
168 }
169
170 // Sort the import specs by import path.
f038dae6 171 // Remove duplicates, when possible without data loss.
9c63abc9 172 // Reassign the import paths to have the same position sequence.
5a8ea165 173 // Reassign each comment to the spec on the same line.
9c63abc9 174 // Sort the comments by new position.
1a2f01ef
ILT
175 sort.Slice(specs, func(i, j int) bool {
176 ipath := importPath(specs[i])
177 jpath := importPath(specs[j])
178 if ipath != jpath {
179 return ipath < jpath
180 }
181 iname := importName(specs[i])
182 jname := importName(specs[j])
183 if iname != jname {
184 return iname < jname
185 }
186 return importComment(specs[i]) < importComment(specs[j])
187 })
f038dae6
ILT
188
189 // Dedup. Thanks to our sorting, we can just consider
190 // adjacent pairs of imports.
191 deduped := specs[:0]
192 for i, s := range specs {
193 if i == len(specs)-1 || !collapse(s, specs[i+1]) {
194 deduped = append(deduped, s)
195 } else {
196 p := s.Pos()
5a8ea165 197 fset.File(p).MergeLine(lineAt(fset, p))
f038dae6
ILT
198 }
199 }
200 specs = deduped
201
202 // Fix up comment positions
9c63abc9
ILT
203 for i, s := range specs {
204 s := s.(*ImportSpec)
205 if s.Name != nil {
206 s.Name.NamePos = pos[i].Start
207 }
208 s.Path.ValuePos = pos[i].Start
209 s.EndPos = pos[i].End
1a2f01ef 210 for _, g := range importComments[s] {
5a8ea165
ILT
211 for _, c := range g.cg.List {
212 if g.left {
213 c.Slash = pos[i].Start - 1
214 } else {
215 // An import spec can have both block comment and a line comment
216 // to its right. In that case, both of them will have the same pos.
217 // But while formatting the AST, the line comment gets moved to
218 // after the block comment.
219 c.Slash = pos[i].End
220 }
9c63abc9
ILT
221 }
222 }
223 }
f038dae6 224
1a2f01ef
ILT
225 sort.Slice(comments, func(i, j int) bool {
226 return comments[i].Pos() < comments[j].Pos()
227 })
f038dae6
ILT
228
229 return specs
9c63abc9 230}