OSDN Git Service

d7d4b4b6b6d6e3dfc5620f8cf6050151846406ae
[pf3gnuchains/gcc-fork.git] / libgo / go / go / ast / filter.go
1 // Copyright 2009 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
5 package ast
6
7 import "go/token"
8
9 // ----------------------------------------------------------------------------
10 // Export filtering
11
12 // exportFilter is a special filter function to extract exported nodes.
13 func exportFilter(name string) bool {
14         return IsExported(name)
15 }
16
17 // FileExports trims the AST for a Go source file in place such that
18 // only exported nodes remain: all top-level identifiers which are not exported
19 // and their associated information (such as type, initial value, or function
20 // body) are removed. Non-exported fields and methods of exported types are
21 // stripped. The File.Comments list is not changed.
22 //
23 // FileExports returns true if there are exported declarationa;
24 // it returns false otherwise.
25 //
26 func FileExports(src *File) bool {
27         return FilterFile(src, exportFilter)
28 }
29
30 // PackageExports trims the AST for a Go package in place such that
31 // only exported nodes remain. The pkg.Files list is not changed, so that
32 // file names and top-level package comments don't get lost.
33 //
34 // PackageExports returns true if there are exported declarations;
35 // it returns false otherwise.
36 //
37 func PackageExports(pkg *Package) bool {
38         return FilterPackage(pkg, exportFilter)
39 }
40
41 // ----------------------------------------------------------------------------
42 // General filtering
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 // fieldName assumes that x is the type of an anonymous field and
58 // returns the corresponding field name. If x is not an acceptable
59 // anonymous field, the result is nil.
60 //
61 func fieldName(x Expr) *Ident {
62         switch t := x.(type) {
63         case *Ident:
64                 return t
65         case *SelectorExpr:
66                 if _, ok := t.X.(*Ident); ok {
67                         return t.Sel
68                 }
69         case *StarExpr:
70                 return fieldName(t.X)
71         }
72         return nil
73 }
74
75 func filterFieldList(fields *FieldList, filter Filter) (removedFields bool) {
76         if fields == nil {
77                 return false
78         }
79         list := fields.List
80         j := 0
81         for _, f := range list {
82                 keepField := false
83                 if len(f.Names) == 0 {
84                         // anonymous field
85                         name := fieldName(f.Type)
86                         keepField = name != nil && filter(name.Name)
87                 } else {
88                         n := len(f.Names)
89                         f.Names = filterIdentList(f.Names, filter)
90                         if len(f.Names) < n {
91                                 removedFields = true
92                         }
93                         keepField = len(f.Names) > 0
94                 }
95                 if keepField {
96                         if filter == exportFilter {
97                                 filterType(f.Type, filter)
98                         }
99                         list[j] = f
100                         j++
101                 }
102         }
103         if j < len(list) {
104                 removedFields = true
105         }
106         fields.List = list[0:j]
107         return
108 }
109
110 func filterParamList(fields *FieldList, filter Filter) bool {
111         if fields == nil {
112                 return false
113         }
114         var b bool
115         for _, f := range fields.List {
116                 if filterType(f.Type, filter) {
117                         b = true
118                 }
119         }
120         return b
121 }
122
123 func filterType(typ Expr, f Filter) bool {
124         switch t := typ.(type) {
125         case *Ident:
126                 return f(t.Name)
127         case *ParenExpr:
128                 return filterType(t.X, f)
129         case *ArrayType:
130                 return filterType(t.Elt, f)
131         case *StructType:
132                 if filterFieldList(t.Fields, f) {
133                         t.Incomplete = true
134                 }
135                 return len(t.Fields.List) > 0
136         case *FuncType:
137                 b1 := filterParamList(t.Params, f)
138                 b2 := filterParamList(t.Results, f)
139                 return b1 || b2
140         case *InterfaceType:
141                 if filterFieldList(t.Methods, f) {
142                         t.Incomplete = true
143                 }
144                 return len(t.Methods.List) > 0
145         case *MapType:
146                 b1 := filterType(t.Key, f)
147                 b2 := filterType(t.Value, f)
148                 return b1 || b2
149         case *ChanType:
150                 return filterType(t.Value, f)
151         }
152         return false
153 }
154
155 func filterSpec(spec Spec, f Filter) bool {
156         switch s := spec.(type) {
157         case *ValueSpec:
158                 s.Names = filterIdentList(s.Names, f)
159                 if len(s.Names) > 0 {
160                         if f == exportFilter {
161                                 filterType(s.Type, f)
162                         }
163                         return true
164                 }
165         case *TypeSpec:
166                 if f(s.Name.Name) {
167                         if f == exportFilter {
168                                 filterType(s.Type, f)
169                         }
170                         return true
171                 }
172                 if f != exportFilter {
173                         // For general filtering (not just exports),
174                         // filter type even if name is not filtered
175                         // out.
176                         // If the type contains filtered elements,
177                         // keep the declaration.
178                         return filterType(s.Type, f)
179                 }
180         }
181         return false
182 }
183
184 func filterSpecList(list []Spec, f Filter) []Spec {
185         j := 0
186         for _, s := range list {
187                 if filterSpec(s, f) {
188                         list[j] = s
189                         j++
190                 }
191         }
192         return list[0:j]
193 }
194
195 // FilterDecl trims the AST for a Go declaration in place by removing
196 // all names (including struct field and interface method names, but
197 // not from parameter lists) that don't pass through the filter f.
198 //
199 // FilterDecl returns true if there are any declared names left after
200 // filtering; it returns false otherwise.
201 //
202 func FilterDecl(decl Decl, f Filter) bool {
203         switch d := decl.(type) {
204         case *GenDecl:
205                 d.Specs = filterSpecList(d.Specs, f)
206                 return len(d.Specs) > 0
207         case *FuncDecl:
208                 return f(d.Name.Name)
209         }
210         return false
211 }
212
213 // FilterFile trims the AST for a Go file in place by removing all
214 // names from top-level declarations (including struct field and
215 // interface method names, but not from parameter lists) that don't
216 // pass through the filter f. If the declaration is empty afterwards,
217 // the declaration is removed from the AST. The File.Comments list
218 // is not changed.
219 //
220 // FilterFile returns true if there are any top-level declarations
221 // left after filtering; it returns false otherwise.
222 //
223 func FilterFile(src *File, f Filter) bool {
224         j := 0
225         for _, d := range src.Decls {
226                 if FilterDecl(d, f) {
227                         src.Decls[j] = d
228                         j++
229                 }
230         }
231         src.Decls = src.Decls[0:j]
232         return j > 0
233 }
234
235 // FilterPackage trims the AST for a Go package in place by removing
236 // all names from top-level declarations (including struct field and
237 // interface method names, but not from parameter lists) that don't
238 // pass through the filter f. If the declaration is empty afterwards,
239 // the declaration is removed from the AST. The pkg.Files list is not
240 // changed, so that file names and top-level package comments don't get
241 // lost.
242 //
243 // FilterPackage returns true if there are any top-level declarations
244 // left after filtering; it returns false otherwise.
245 //
246 func FilterPackage(pkg *Package, f Filter) bool {
247         hasDecls := false
248         for _, src := range pkg.Files {
249                 if FilterFile(src, f) {
250                         hasDecls = true
251                 }
252         }
253         return hasDecls
254 }
255
256 // ----------------------------------------------------------------------------
257 // Merging of package files
258
259 // The MergeMode flags control the behavior of MergePackageFiles.
260 type MergeMode uint
261
262 const (
263         // If set, duplicate function declarations are excluded.
264         FilterFuncDuplicates MergeMode = 1 << iota
265         // If set, comments that are not associated with a specific
266         // AST node (as Doc or Comment) are excluded.
267         FilterUnassociatedComments
268         // If set, duplicate import declarations are excluded.
269         FilterImportDuplicates
270 )
271
272 // separator is an empty //-style comment that is interspersed between
273 // different comment groups when they are concatenated into a single group
274 //
275 var separator = &Comment{noPos, "//"}
276
277 // MergePackageFiles creates a file AST by merging the ASTs of the
278 // files belonging to a package. The mode flags control merging behavior.
279 //
280 func MergePackageFiles(pkg *Package, mode MergeMode) *File {
281         // Count the number of package docs, comments and declarations across
282         // all package files.
283         ndocs := 0
284         ncomments := 0
285         ndecls := 0
286         for _, f := range pkg.Files {
287                 if f.Doc != nil {
288                         ndocs += len(f.Doc.List) + 1 // +1 for separator
289                 }
290                 ncomments += len(f.Comments)
291                 ndecls += len(f.Decls)
292         }
293
294         // Collect package comments from all package files into a single
295         // CommentGroup - the collected package documentation. The order
296         // is unspecified. In general there should be only one file with
297         // a package comment; but it's better to collect extra comments
298         // than drop them on the floor.
299         var doc *CommentGroup
300         var pos token.Pos
301         if ndocs > 0 {
302                 list := make([]*Comment, ndocs-1) // -1: no separator before first group
303                 i := 0
304                 for _, f := range pkg.Files {
305                         if f.Doc != nil {
306                                 if i > 0 {
307                                         // not the first group - add separator
308                                         list[i] = separator
309                                         i++
310                                 }
311                                 for _, c := range f.Doc.List {
312                                         list[i] = c
313                                         i++
314                                 }
315                                 if f.Package > pos {
316                                         // Keep the maximum package clause position as
317                                         // position for the package clause of the merged
318                                         // files.
319                                         pos = f.Package
320                                 }
321                         }
322                 }
323                 doc = &CommentGroup{list}
324         }
325
326         // Collect declarations from all package files.
327         var decls []Decl
328         if ndecls > 0 {
329                 decls = make([]Decl, ndecls)
330                 funcs := make(map[string]int) // map of global function name -> decls index
331                 i := 0                        // current index
332                 n := 0                        // number of filtered entries
333                 for _, f := range pkg.Files {
334                         for _, d := range f.Decls {
335                                 if mode&FilterFuncDuplicates != 0 {
336                                         // A language entity may be declared multiple
337                                         // times in different package files; only at
338                                         // build time declarations must be unique.
339                                         // For now, exclude multiple declarations of
340                                         // functions - keep the one with documentation.
341                                         //
342                                         // TODO(gri): Expand this filtering to other
343                                         //            entities (const, type, vars) if
344                                         //            multiple declarations are common.
345                                         if f, isFun := d.(*FuncDecl); isFun {
346                                                 name := f.Name.Name
347                                                 if j, exists := funcs[name]; exists {
348                                                         // function declared already
349                                                         if decls[j] != nil && decls[j].(*FuncDecl).Doc == nil {
350                                                                 // existing declaration has no documentation;
351                                                                 // ignore the existing declaration
352                                                                 decls[j] = nil
353                                                         } else {
354                                                                 // ignore the new declaration
355                                                                 d = nil
356                                                         }
357                                                         n++ // filtered an entry
358                                                 } else {
359                                                         funcs[name] = i
360                                                 }
361                                         }
362                                 }
363                                 decls[i] = d
364                                 i++
365                         }
366                 }
367
368                 // Eliminate nil entries from the decls list if entries were
369                 // filtered. We do this using a 2nd pass in order to not disturb
370                 // the original declaration order in the source (otherwise, this
371                 // would also invalidate the monotonically increasing position
372                 // info within a single file).
373                 if n > 0 {
374                         i = 0
375                         for _, d := range decls {
376                                 if d != nil {
377                                         decls[i] = d
378                                         i++
379                                 }
380                         }
381                         decls = decls[0:i]
382                 }
383         }
384
385         // Collect import specs from all package files.
386         var imports []*ImportSpec
387         if mode&FilterImportDuplicates != 0 {
388                 seen := make(map[string]bool)
389                 for _, f := range pkg.Files {
390                         for _, imp := range f.Imports {
391                                 if path := imp.Path.Value; !seen[path] {
392                                         // TODO: consider handling cases where:
393                                         // - 2 imports exist with the same import path but
394                                         //   have different local names (one should probably 
395                                         //   keep both of them)
396                                         // - 2 imports exist but only one has a comment
397                                         // - 2 imports exist and they both have (possibly
398                                         //   different) comments
399                                         imports = append(imports, imp)
400                                         seen[path] = true
401                                 }
402                         }
403                 }
404         } else {
405                 for _, f := range pkg.Files {
406                         imports = append(imports, f.Imports...)
407                 }
408         }
409
410         // Collect comments from all package files.
411         var comments []*CommentGroup
412         if mode&FilterUnassociatedComments == 0 {
413                 comments = make([]*CommentGroup, ncomments)
414                 i := 0
415                 for _, f := range pkg.Files {
416                         i += copy(comments[i:], f.Comments)
417                 }
418         }
419
420         // TODO(gri) need to compute unresolved identifiers!
421         return &File{doc, pos, NewIdent(pkg.Name), decls, pkg.Scope, imports, nil, comments}
422 }