Source file
src/go/ast/filter.go
1
2
3
4
5 package ast
6
7 import (
8 "go/token"
9 "slices"
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
38
39
40 func PackageExports(pkg *Package) bool {
41 return filterPackage(pkg, exportFilter, true)
42 }
43
44
45
46
47 type Filter func(string) bool
48
49 func filterIdentList(list []*Ident, f Filter) []*Ident {
50 j := 0
51 for _, x := range list {
52 if f(x.Name) {
53 list[j] = x
54 j++
55 }
56 }
57 return list[0:j]
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 func FilterDecl(decl Decl, f Filter) bool {
233 return filterDecl(decl, f, false)
234 }
235
236 func filterDecl(decl Decl, f Filter, export bool) bool {
237 switch d := decl.(type) {
238 case *GenDecl:
239 d.Specs = filterSpecList(d.Specs, f, export)
240 return len(d.Specs) > 0
241 case *FuncDecl:
242 return f(d.Name.Name)
243 }
244 return false
245 }
246
247
248
249
250
251
252
253
254
255
256 func FilterFile(src *File, f Filter) bool {
257 return filterFile(src, f, false)
258 }
259
260 func filterFile(src *File, f Filter, export bool) bool {
261 j := 0
262 for _, d := range src.Decls {
263 if filterDecl(d, f, export) {
264 src.Decls[j] = d
265 j++
266 }
267 }
268 src.Decls = src.Decls[0:j]
269 return j > 0
270 }
271
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
304
305
306 type MergeMode uint
307
308
309
310 const (
311
312 FilterFuncDuplicates MergeMode = 1 << iota
313
314
315 FilterUnassociatedComments
316
317 FilterImportDuplicates
318 )
319
320
321
322
323 func nameOf(f *FuncDecl) string {
324 if r := f.Recv; r != nil && len(r.List) == 1 {
325
326 t := r.List[0].Type
327
328 if p, _ := t.(*StarExpr); p != nil {
329 t = p.X
330 }
331
332 if p, _ := t.(*Ident); p != nil {
333 return p.Name + "." + f.Name.Name
334 }
335
336 }
337 return f.Name.Name
338 }
339
340
341
342 var separator = &Comment{token.NoPos, "//"}
343
344
345
346
347
348
349 func MergePackageFiles(pkg *Package, mode MergeMode) *File {
350
351
352
353 ndocs := 0
354 ncomments := 0
355 ndecls := 0
356 filenames := make([]string, len(pkg.Files))
357 var minPos, maxPos token.Pos
358 i := 0
359 for filename, f := range pkg.Files {
360 filenames[i] = filename
361 i++
362 if f.Doc != nil {
363 ndocs += len(f.Doc.List) + 1
364 }
365 ncomments += len(f.Comments)
366 ndecls += len(f.Decls)
367 if i == 0 || f.FileStart < minPos {
368 minPos = f.FileStart
369 }
370 if i == 0 || f.FileEnd > maxPos {
371 maxPos = f.FileEnd
372 }
373 }
374 slices.Sort(filenames)
375
376
377
378
379
380 var doc *CommentGroup
381 var pos token.Pos
382 if ndocs > 0 {
383 list := make([]*Comment, ndocs-1)
384 i := 0
385 for _, filename := range filenames {
386 f := pkg.Files[filename]
387 if f.Doc != nil {
388 if i > 0 {
389
390 list[i] = separator
391 i++
392 }
393 for _, c := range f.Doc.List {
394 list[i] = c
395 i++
396 }
397 if f.Package > pos {
398
399
400
401 pos = f.Package
402 }
403 }
404 }
405 doc = &CommentGroup{list}
406 }
407
408
409 var decls []Decl
410 if ndecls > 0 {
411 decls = make([]Decl, ndecls)
412 funcs := make(map[string]int)
413 i := 0
414 n := 0
415 for _, filename := range filenames {
416 f := pkg.Files[filename]
417 for _, d := range f.Decls {
418 if mode&FilterFuncDuplicates != 0 {
419
420
421
422
423
424
425
426
427
428 if f, isFun := d.(*FuncDecl); isFun {
429 name := nameOf(f)
430 if j, exists := funcs[name]; exists {
431
432 if decls[j] != nil && decls[j].(*FuncDecl).Doc == nil {
433
434
435 decls[j] = nil
436 } else {
437
438 d = nil
439 }
440 n++
441 } else {
442 funcs[name] = i
443 }
444 }
445 }
446 decls[i] = d
447 i++
448 }
449 }
450
451
452
453
454
455
456 if n > 0 {
457 i = 0
458 for _, d := range decls {
459 if d != nil {
460 decls[i] = d
461 i++
462 }
463 }
464 decls = decls[0:i]
465 }
466 }
467
468
469 var imports []*ImportSpec
470 if mode&FilterImportDuplicates != 0 {
471 seen := make(map[string]bool)
472 for _, filename := range filenames {
473 f := pkg.Files[filename]
474 for _, imp := range f.Imports {
475 if path := imp.Path.Value; !seen[path] {
476
477
478
479
480
481
482
483 imports = append(imports, imp)
484 seen[path] = true
485 }
486 }
487 }
488 } else {
489
490 for _, filename := range filenames {
491 f := pkg.Files[filename]
492 imports = append(imports, f.Imports...)
493 }
494 }
495
496
497 var comments []*CommentGroup
498 if mode&FilterUnassociatedComments == 0 {
499 comments = make([]*CommentGroup, ncomments)
500 i := 0
501 for _, filename := range filenames {
502 f := pkg.Files[filename]
503 i += copy(comments[i:], f.Comments)
504 }
505 }
506
507
508 return &File{doc, pos, NewIdent(pkg.Name), decls, minPos, maxPos, pkg.Scope, imports, nil, comments, ""}
509 }
510
View as plain text