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