Source file
src/go/ast/filter.go
Documentation: go/ast
1
2
3
4
5 package ast
6
7 import (
8 "go/token"
9 "sort"
10 )
11
12
13
14
15
16 func exportFilter(name string) bool {
17 return IsExported(name)
18 }
19
20
21
22
23
24
25
26
27 func FileExports(src *File) bool {
28 return filterFile(src, exportFilter, true)
29 }
30
31
32
33
34
35
36
37 func PackageExports(pkg *Package) bool {
38 return filterPackage(pkg, exportFilter, true)
39 }
40
41
42
43
44 type Filter func(string) bool
45
46 func filterIdentList(list []*Ident, f Filter) []*Ident {
47 j := 0
48 for _, x := range list {
49 if f(x.Name) {
50 list[j] = x
51 j++
52 }
53 }
54 return list[0:j]
55 }
56
57
58
59
60 func fieldName(x Expr) *Ident {
61 switch t := x.(type) {
62 case *Ident:
63 return t
64 case *SelectorExpr:
65 if _, ok := t.X.(*Ident); ok {
66 return t.Sel
67 }
68 case *StarExpr:
69 return fieldName(t.X)
70 }
71 return nil
72 }
73
74 func filterFieldList(fields *FieldList, filter Filter, export bool) (removedFields bool) {
75 if fields == nil {
76 return false
77 }
78 list := fields.List
79 j := 0
80 for _, f := range list {
81 keepField := false
82 if len(f.Names) == 0 {
83
84 name := fieldName(f.Type)
85 keepField = name != nil && filter(name.Name)
86 } else {
87 n := len(f.Names)
88 f.Names = filterIdentList(f.Names, filter)
89 if len(f.Names) < n {
90 removedFields = true
91 }
92 keepField = len(f.Names) > 0
93 }
94 if keepField {
95 if export {
96 filterType(f.Type, filter, export)
97 }
98 list[j] = f
99 j++
100 }
101 }
102 if j < len(list) {
103 removedFields = true
104 }
105 fields.List = list[0:j]
106 return
107 }
108
109 func filterCompositeLit(lit *CompositeLit, filter Filter, export bool) {
110 n := len(lit.Elts)
111 lit.Elts = filterExprList(lit.Elts, filter, export)
112 if len(lit.Elts) < n {
113 lit.Incomplete = true
114 }
115 }
116
117 func filterExprList(list []Expr, filter Filter, export bool) []Expr {
118 j := 0
119 for _, exp := range list {
120 switch x := exp.(type) {
121 case *CompositeLit:
122 filterCompositeLit(x, filter, export)
123 case *KeyValueExpr:
124 if x, ok := x.Key.(*Ident); ok && !filter(x.Name) {
125 continue
126 }
127 if x, ok := x.Value.(*CompositeLit); ok {
128 filterCompositeLit(x, filter, export)
129 }
130 }
131 list[j] = exp
132 j++
133 }
134 return list[0:j]
135 }
136
137 func filterParamList(fields *FieldList, filter Filter, export bool) bool {
138 if fields == nil {
139 return false
140 }
141 var b bool
142 for _, f := range fields.List {
143 if filterType(f.Type, filter, export) {
144 b = true
145 }
146 }
147 return b
148 }
149
150 func filterType(typ Expr, f Filter, export bool) bool {
151 switch t := typ.(type) {
152 case *Ident:
153 return f(t.Name)
154 case *ParenExpr:
155 return filterType(t.X, f, export)
156 case *ArrayType:
157 return filterType(t.Elt, f, export)
158 case *StructType:
159 if filterFieldList(t.Fields, f, export) {
160 t.Incomplete = true
161 }
162 return len(t.Fields.List) > 0
163 case *FuncType:
164 b1 := filterParamList(t.Params, f, export)
165 b2 := filterParamList(t.Results, f, export)
166 return b1 || b2
167 case *InterfaceType:
168 if filterFieldList(t.Methods, f, export) {
169 t.Incomplete = true
170 }
171 return len(t.Methods.List) > 0
172 case *MapType:
173 b1 := filterType(t.Key, f, export)
174 b2 := filterType(t.Value, f, export)
175 return b1 || b2
176 case *ChanType:
177 return filterType(t.Value, f, export)
178 }
179 return false
180 }
181
182 func filterSpec(spec Spec, f Filter, export bool) bool {
183 switch s := spec.(type) {
184 case *ValueSpec:
185 s.Names = filterIdentList(s.Names, f)
186 s.Values = filterExprList(s.Values, f, export)
187 if len(s.Names) > 0 {
188 if export {
189 filterType(s.Type, f, export)
190 }
191 return true
192 }
193 case *TypeSpec:
194 if f(s.Name.Name) {
195 if export {
196 filterType(s.Type, f, export)
197 }
198 return true
199 }
200 if !export {
201
202
203
204
205
206 return filterType(s.Type, f, export)
207 }
208 }
209 return false
210 }
211
212 func filterSpecList(list []Spec, f Filter, export bool) []Spec {
213 j := 0
214 for _, s := range list {
215 if filterSpec(s, f, export) {
216 list[j] = s
217 j++
218 }
219 }
220 return list[0:j]
221 }
222
223
224
225
226
227
228
229 func FilterDecl(decl Decl, f Filter) bool {
230 return filterDecl(decl, f, false)
231 }
232
233 func filterDecl(decl Decl, f Filter, export bool) bool {
234 switch d := decl.(type) {
235 case *GenDecl:
236 d.Specs = filterSpecList(d.Specs, f, export)
237 return len(d.Specs) > 0
238 case *FuncDecl:
239 return f(d.Name.Name)
240 }
241 return false
242 }
243
244
245
246
247
248
249
250
251
252
253 func FilterFile(src *File, f Filter) bool {
254 return filterFile(src, f, false)
255 }
256
257 func filterFile(src *File, f Filter, export bool) bool {
258 j := 0
259 for _, d := range src.Decls {
260 if filterDecl(d, f, export) {
261 src.Decls[j] = d
262 j++
263 }
264 }
265 src.Decls = src.Decls[0:j]
266 return j > 0
267 }
268
269
270
271
272
273
274
275
276
277
278
279 func FilterPackage(pkg *Package, f Filter) bool {
280 return filterPackage(pkg, f, false)
281 }
282
283 func filterPackage(pkg *Package, f Filter, export bool) bool {
284 hasDecls := false
285 for _, src := range pkg.Files {
286 if filterFile(src, f, export) {
287 hasDecls = true
288 }
289 }
290 return hasDecls
291 }
292
293
294
295
296
297 type MergeMode uint
298
299 const (
300
301 FilterFuncDuplicates MergeMode = 1 << iota
302
303
304 FilterUnassociatedComments
305
306 FilterImportDuplicates
307 )
308
309
310
311
312 func nameOf(f *FuncDecl) string {
313 if r := f.Recv; r != nil && len(r.List) == 1 {
314
315 t := r.List[0].Type
316
317 if p, _ := t.(*StarExpr); p != nil {
318 t = p.X
319 }
320
321 if p, _ := t.(*Ident); p != nil {
322 return p.Name + "." + f.Name.Name
323 }
324
325 }
326 return f.Name.Name
327 }
328
329
330
331 var separator = &Comment{token.NoPos, "//"}
332
333
334
335 func MergePackageFiles(pkg *Package, mode MergeMode) *File {
336
337
338
339 ndocs := 0
340 ncomments := 0
341 ndecls := 0
342 filenames := make([]string, len(pkg.Files))
343 i := 0
344 for filename, f := range pkg.Files {
345 filenames[i] = filename
346 i++
347 if f.Doc != nil {
348 ndocs += len(f.Doc.List) + 1
349 }
350 ncomments += len(f.Comments)
351 ndecls += len(f.Decls)
352 }
353 sort.Strings(filenames)
354
355
356
357
358
359 var doc *CommentGroup
360 var pos token.Pos
361 if ndocs > 0 {
362 list := make([]*Comment, ndocs-1)
363 i := 0
364 for _, filename := range filenames {
365 f := pkg.Files[filename]
366 if f.Doc != nil {
367 if i > 0 {
368
369 list[i] = separator
370 i++
371 }
372 for _, c := range f.Doc.List {
373 list[i] = c
374 i++
375 }
376 if f.Package > pos {
377
378
379
380 pos = f.Package
381 }
382 }
383 }
384 doc = &CommentGroup{list}
385 }
386
387
388 var decls []Decl
389 if ndecls > 0 {
390 decls = make([]Decl, ndecls)
391 funcs := make(map[string]int)
392 i := 0
393 n := 0
394 for _, filename := range filenames {
395 f := pkg.Files[filename]
396 for _, d := range f.Decls {
397 if mode&FilterFuncDuplicates != 0 {
398
399
400
401
402
403
404
405
406
407 if f, isFun := d.(*FuncDecl); isFun {
408 name := nameOf(f)
409 if j, exists := funcs[name]; exists {
410
411 if decls[j] != nil && decls[j].(*FuncDecl).Doc == nil {
412
413
414 decls[j] = nil
415 } else {
416
417 d = nil
418 }
419 n++
420 } else {
421 funcs[name] = i
422 }
423 }
424 }
425 decls[i] = d
426 i++
427 }
428 }
429
430
431
432
433
434
435 if n > 0 {
436 i = 0
437 for _, d := range decls {
438 if d != nil {
439 decls[i] = d
440 i++
441 }
442 }
443 decls = decls[0:i]
444 }
445 }
446
447
448 var imports []*ImportSpec
449 if mode&FilterImportDuplicates != 0 {
450 seen := make(map[string]bool)
451 for _, filename := range filenames {
452 f := pkg.Files[filename]
453 for _, imp := range f.Imports {
454 if path := imp.Path.Value; !seen[path] {
455
456
457
458
459
460
461
462 imports = append(imports, imp)
463 seen[path] = true
464 }
465 }
466 }
467 } else {
468
469 for _, filename := range filenames {
470 f := pkg.Files[filename]
471 imports = append(imports, f.Imports...)
472 }
473 }
474
475
476 var comments []*CommentGroup
477 if mode&FilterUnassociatedComments == 0 {
478 comments = make([]*CommentGroup, ncomments)
479 i := 0
480 for _, filename := range filenames {
481 f := pkg.Files[filename]
482 i += copy(comments[i:], f.Comments)
483 }
484 }
485
486
487 return &File{doc, pos, NewIdent(pkg.Name), decls, pkg.Scope, imports, nil, comments}
488 }
489
View as plain text