Update to current Go library.
[official-gcc.git] / libgo / go / go / ast / filter.go
blob090d08d34c78325936bcef8c11e95aa12b95c0c6
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.
5 package ast
7 import "go/token"
9 // ----------------------------------------------------------------------------
10 // Export filtering
12 func identListExports(list []*Ident) []*Ident {
13 j := 0
14 for _, x := range list {
15 if x.IsExported() {
16 list[j] = x
17 j++
20 return list[0:j]
24 // isExportedType assumes that typ is a correct type.
25 func isExportedType(typ Expr) bool {
26 switch t := typ.(type) {
27 case *Ident:
28 return t.IsExported()
29 case *ParenExpr:
30 return isExportedType(t.X)
31 case *SelectorExpr:
32 // assume t.X is a typename
33 return t.Sel.IsExported()
34 case *StarExpr:
35 return isExportedType(t.X)
37 return false
41 func fieldListExports(fields *FieldList, incomplete *bool) {
42 if fields == nil {
43 return
45 list := fields.List
46 j := 0
47 for _, f := range list {
48 exported := false
49 if len(f.Names) == 0 {
50 // anonymous field
51 // (Note that a non-exported anonymous field
52 // may still refer to a type with exported
53 // fields, so this is not absolutely correct.
54 // However, this cannot be done w/o complete
55 // type information.)
56 exported = isExportedType(f.Type)
57 } else {
58 n := len(f.Names)
59 f.Names = identListExports(f.Names)
60 if len(f.Names) < n {
61 *incomplete = true
63 exported = len(f.Names) > 0
65 if exported {
66 typeExports(f.Type)
67 list[j] = f
68 j++
71 if j < len(list) {
72 *incomplete = true
74 fields.List = list[0:j]
78 func paramListExports(fields *FieldList) {
79 if fields == nil {
80 return
82 for _, f := range fields.List {
83 typeExports(f.Type)
88 func typeExports(typ Expr) {
89 switch t := typ.(type) {
90 case *ArrayType:
91 typeExports(t.Elt)
92 case *StructType:
93 fieldListExports(t.Fields, &t.Incomplete)
94 case *FuncType:
95 paramListExports(t.Params)
96 paramListExports(t.Results)
97 case *InterfaceType:
98 fieldListExports(t.Methods, &t.Incomplete)
99 case *MapType:
100 typeExports(t.Key)
101 typeExports(t.Value)
102 case *ChanType:
103 typeExports(t.Value)
108 func specExports(spec Spec) bool {
109 switch s := spec.(type) {
110 case *ValueSpec:
111 s.Names = identListExports(s.Names)
112 if len(s.Names) > 0 {
113 typeExports(s.Type)
114 return true
116 case *TypeSpec:
117 if s.Name.IsExported() {
118 typeExports(s.Type)
119 return true
122 return false
126 func specListExports(list []Spec) []Spec {
127 j := 0
128 for _, s := range list {
129 if specExports(s) {
130 list[j] = s
134 return list[0:j]
138 func declExports(decl Decl) bool {
139 switch d := decl.(type) {
140 case *GenDecl:
141 d.Specs = specListExports(d.Specs)
142 return len(d.Specs) > 0
143 case *FuncDecl:
144 d.Body = nil // strip body
145 return d.Name.IsExported()
147 return false
151 // FileExports trims the AST for a Go source file in place such that only
152 // exported nodes remain: all top-level identifiers which are not exported
153 // and their associated information (such as type, initial value, or function
154 // body) are removed. Non-exported fields and methods of exported types are
155 // stripped, and the function bodies of exported functions are set to nil.
156 // The File.comments list is not changed.
158 // FileExports returns true if there is an exported declaration; it returns
159 // false otherwise.
161 func FileExports(src *File) bool {
162 j := 0
163 for _, d := range src.Decls {
164 if declExports(d) {
165 src.Decls[j] = d
169 src.Decls = src.Decls[0:j]
170 return j > 0
174 // PackageExports trims the AST for a Go package in place such that only
175 // exported nodes remain. The pkg.Files list is not changed, so that file
176 // names and top-level package comments don't get lost.
178 // PackageExports returns true if there is an exported declaration; it
179 // returns false otherwise.
181 func PackageExports(pkg *Package) bool {
182 hasExports := false
183 for _, f := range pkg.Files {
184 if FileExports(f) {
185 hasExports = true
188 return hasExports
192 // ----------------------------------------------------------------------------
193 // General filtering
195 type Filter func(string) bool
197 func filterIdentList(list []*Ident, f Filter) []*Ident {
198 j := 0
199 for _, x := range list {
200 if f(x.Name) {
201 list[j] = x
205 return list[0:j]
209 func filterSpec(spec Spec, f Filter) bool {
210 switch s := spec.(type) {
211 case *ValueSpec:
212 s.Names = filterIdentList(s.Names, f)
213 return len(s.Names) > 0
214 case *TypeSpec:
215 return f(s.Name.Name)
217 return false
221 func filterSpecList(list []Spec, f Filter) []Spec {
222 j := 0
223 for _, s := range list {
224 if filterSpec(s, f) {
225 list[j] = s
229 return list[0:j]
233 func filterDecl(decl Decl, f Filter) bool {
234 switch d := decl.(type) {
235 case *GenDecl:
236 d.Specs = filterSpecList(d.Specs, f)
237 return len(d.Specs) > 0
238 case *FuncDecl:
239 return f(d.Name.Name)
241 return false
245 // FilterFile trims the AST for a Go file in place by removing all
246 // names from top-level declarations (but not from parameter lists
247 // or inside types) that don't pass through the filter f. If the
248 // declaration is empty afterwards, the declaration is removed from
249 // the AST.
250 // The File.comments list is not changed.
252 // FilterFile returns true if there are any top-level declarations
253 // left after filtering; it returns false otherwise.
255 func FilterFile(src *File, f Filter) bool {
256 j := 0
257 for _, d := range src.Decls {
258 if filterDecl(d, f) {
259 src.Decls[j] = d
263 src.Decls = src.Decls[0:j]
264 return j > 0
268 // FilterPackage trims the AST for a Go package in place by removing all
269 // names from top-level declarations (but not from parameter lists
270 // or inside types) that don't pass through the filter f. If the
271 // declaration is empty afterwards, the declaration is removed from
272 // the AST.
273 // The pkg.Files list is not changed, so that file names and top-level
274 // package comments don't get lost.
276 // FilterPackage returns true if there are any top-level declarations
277 // left after filtering; it returns false otherwise.
279 func FilterPackage(pkg *Package, f Filter) bool {
280 hasDecls := false
281 for _, src := range pkg.Files {
282 if FilterFile(src, f) {
283 hasDecls = true
286 return hasDecls
290 // ----------------------------------------------------------------------------
291 // Merging of package files
293 // The MergeMode flags control the behavior of MergePackageFiles.
294 type MergeMode uint
296 const (
297 // If set, duplicate function declarations are excluded.
298 FilterFuncDuplicates MergeMode = 1 << iota
299 // If set, comments that are not associated with a specific
300 // AST node (as Doc or Comment) are excluded.
301 FilterUnassociatedComments
304 // separator is an empty //-style comment that is interspersed between
305 // different comment groups when they are concatenated into a single group
307 var separator = &Comment{noPos, "//"}
310 // MergePackageFiles creates a file AST by merging the ASTs of the
311 // files belonging to a package. The mode flags control merging behavior.
313 func MergePackageFiles(pkg *Package, mode MergeMode) *File {
314 // Count the number of package docs, comments and declarations across
315 // all package files.
316 ndocs := 0
317 ncomments := 0
318 ndecls := 0
319 for _, f := range pkg.Files {
320 if f.Doc != nil {
321 ndocs += len(f.Doc.List) + 1 // +1 for separator
323 ncomments += len(f.Comments)
324 ndecls += len(f.Decls)
327 // Collect package comments from all package files into a single
328 // CommentGroup - the collected package documentation. The order
329 // is unspecified. In general there should be only one file with
330 // a package comment; but it's better to collect extra comments
331 // than drop them on the floor.
332 var doc *CommentGroup
333 var pos token.Pos
334 if ndocs > 0 {
335 list := make([]*Comment, ndocs-1) // -1: no separator before first group
336 i := 0
337 for _, f := range pkg.Files {
338 if f.Doc != nil {
339 if i > 0 {
340 // not the first group - add separator
341 list[i] = separator
344 for _, c := range f.Doc.List {
345 list[i] = c
348 if f.Package > pos {
349 // Keep the maximum package clause position as
350 // position for the package clause of the merged
351 // files.
352 pos = f.Package
356 doc = &CommentGroup{list}
359 // Collect declarations from all package files.
360 var decls []Decl
361 if ndecls > 0 {
362 decls = make([]Decl, ndecls)
363 funcs := make(map[string]int) // map of global function name -> decls index
364 i := 0 // current index
365 n := 0 // number of filtered entries
366 for _, f := range pkg.Files {
367 for _, d := range f.Decls {
368 if mode&FilterFuncDuplicates != 0 {
369 // A language entity may be declared multiple
370 // times in different package files; only at
371 // build time declarations must be unique.
372 // For now, exclude multiple declarations of
373 // functions - keep the one with documentation.
375 // TODO(gri): Expand this filtering to other
376 // entities (const, type, vars) if
377 // multiple declarations are common.
378 if f, isFun := d.(*FuncDecl); isFun {
379 name := f.Name.Name
380 if j, exists := funcs[name]; exists {
381 // function declared already
382 if decls[j] != nil && decls[j].(*FuncDecl).Doc == nil {
383 // existing declaration has no documentation;
384 // ignore the existing declaration
385 decls[j] = nil
386 } else {
387 // ignore the new declaration
388 d = nil
390 n++ // filtered an entry
391 } else {
392 funcs[name] = i
396 decls[i] = d
401 // Eliminate nil entries from the decls list if entries were
402 // filtered. We do this using a 2nd pass in order to not disturb
403 // the original declaration order in the source (otherwise, this
404 // would also invalidate the monotonically increasing position
405 // info within a single file).
406 if n > 0 {
407 i = 0
408 for _, d := range decls {
409 if d != nil {
410 decls[i] = d
414 decls = decls[0:i]
418 // Collect comments from all package files.
419 var comments []*CommentGroup
420 if mode&FilterUnassociatedComments == 0 {
421 comments = make([]*CommentGroup, ncomments)
422 i := 0
423 for _, f := range pkg.Files {
424 i += copy(comments[i:], f.Comments)
428 // TODO(gri) need to compute pkgScope and unresolved identifiers!
429 // TODO(gri) need to compute imports!
430 return &File{doc, pos, NewIdent(pkg.Name), decls, nil, nil, nil, comments}