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 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 var minPos, maxPos token.Pos
344 i := 0
345 for filename, f := range pkg.Files {
346 filenames[i] = filename
347 i++
348 if f.Doc != nil {
349 ndocs += len(f.Doc.List) + 1
350 }
351 ncomments += len(f.Comments)
352 ndecls += len(f.Decls)
353 if i == 0 || f.FileStart < minPos {
354 minPos = f.FileStart
355 }
356 if i == 0 || f.FileEnd > maxPos {
357 maxPos = f.FileEnd
358 }
359 }
360 slices.Sort(filenames)
361
362
363
364
365
366 var doc *CommentGroup
367 var pos token.Pos
368 if ndocs > 0 {
369 list := make([]*Comment, ndocs-1)
370 i := 0
371 for _, filename := range filenames {
372 f := pkg.Files[filename]
373 if f.Doc != nil {
374 if i > 0 {
375
376 list[i] = separator
377 i++
378 }
379 for _, c := range f.Doc.List {
380 list[i] = c
381 i++
382 }
383 if f.Package > pos {
384
385
386
387 pos = f.Package
388 }
389 }
390 }
391 doc = &CommentGroup{list}
392 }
393
394
395 var decls []Decl
396 if ndecls > 0 {
397 decls = make([]Decl, ndecls)
398 funcs := make(map[string]int)
399 i := 0
400 n := 0
401 for _, filename := range filenames {
402 f := pkg.Files[filename]
403 for _, d := range f.Decls {
404 if mode&FilterFuncDuplicates != 0 {
405
406
407
408
409
410
411
412
413
414 if f, isFun := d.(*FuncDecl); isFun {
415 name := nameOf(f)
416 if j, exists := funcs[name]; exists {
417
418 if decls[j] != nil && decls[j].(*FuncDecl).Doc == nil {
419
420
421 decls[j] = nil
422 } else {
423
424 d = nil
425 }
426 n++
427 } else {
428 funcs[name] = i
429 }
430 }
431 }
432 decls[i] = d
433 i++
434 }
435 }
436
437
438
439
440
441
442 if n > 0 {
443 i = 0
444 for _, d := range decls {
445 if d != nil {
446 decls[i] = d
447 i++
448 }
449 }
450 decls = decls[0:i]
451 }
452 }
453
454
455 var imports []*ImportSpec
456 if mode&FilterImportDuplicates != 0 {
457 seen := make(map[string]bool)
458 for _, filename := range filenames {
459 f := pkg.Files[filename]
460 for _, imp := range f.Imports {
461 if path := imp.Path.Value; !seen[path] {
462
463
464
465
466
467
468
469 imports = append(imports, imp)
470 seen[path] = true
471 }
472 }
473 }
474 } else {
475
476 for _, filename := range filenames {
477 f := pkg.Files[filename]
478 imports = append(imports, f.Imports...)
479 }
480 }
481
482
483 var comments []*CommentGroup
484 if mode&FilterUnassociatedComments == 0 {
485 comments = make([]*CommentGroup, ncomments)
486 i := 0
487 for _, filename := range filenames {
488 f := pkg.Files[filename]
489 i += copy(comments[i:], f.Comments)
490 }
491 }
492
493
494 return &File{doc, pos, NewIdent(pkg.Name), decls, minPos, maxPos, pkg.Scope, imports, nil, comments, ""}
495 }
496
View as plain text