diff options
author | Tianon Gravi <admwiggin@gmail.com> | 2015-01-15 11:54:00 -0700 |
---|---|---|
committer | Tianon Gravi <admwiggin@gmail.com> | 2015-01-15 11:54:00 -0700 |
commit | f154da9e12608589e8d5f0508f908a0c3e88a1bb (patch) | |
tree | f8255d51e10c6f1e0ed69702200b966c9556a431 /src/go/doc/exports.go | |
parent | 8d8329ed5dfb9622c82a9fbec6fd99a580f9c9f6 (diff) | |
download | golang-upstream/1.4.tar.gz |
Imported Upstream version 1.4upstream/1.4
Diffstat (limited to 'src/go/doc/exports.go')
-rw-r--r-- | src/go/doc/exports.go | 205 |
1 files changed, 205 insertions, 0 deletions
diff --git a/src/go/doc/exports.go b/src/go/doc/exports.go new file mode 100644 index 000000000..1d3b466d8 --- /dev/null +++ b/src/go/doc/exports.go @@ -0,0 +1,205 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This file implements export filtering of an AST. + +package doc + +import ( + "go/ast" + "go/token" +) + +// filterIdentList removes unexported names from list in place +// and returns the resulting list. If blankOk is set, blank +// identifiers are considered exported names. +// +func filterIdentList(list []*ast.Ident, blankOk bool) []*ast.Ident { + j := 0 + for _, x := range list { + if ast.IsExported(x.Name) || (blankOk && x.Name == "_") { + list[j] = x + j++ + } + } + return list[0:j] +} + +// removeErrorField removes anonymous fields named "error" from an interface. +// This is called when "error" has been determined to be a local name, +// not the predeclared type. +// +func removeErrorField(ityp *ast.InterfaceType) { + list := ityp.Methods.List // we know that ityp.Methods != nil + j := 0 + for _, field := range list { + keepField := true + if n := len(field.Names); n == 0 { + // anonymous field + if fname, _ := baseTypeName(field.Type); fname == "error" { + keepField = false + } + } + if keepField { + list[j] = field + j++ + } + } + if j < len(list) { + ityp.Incomplete = true + } + ityp.Methods.List = list[0:j] +} + +// filterFieldList removes unexported fields (field names) from the field list +// in place and returns true if fields were removed. Anonymous fields are +// recorded with the parent type. filterType is called with the types of +// all remaining fields. +// +func (r *reader) filterFieldList(parent *namedType, fields *ast.FieldList, ityp *ast.InterfaceType) (removedFields bool) { + if fields == nil { + return + } + list := fields.List + j := 0 + for _, field := range list { + keepField := false + if n := len(field.Names); n == 0 { + // anonymous field + fname := r.recordAnonymousField(parent, field.Type) + if ast.IsExported(fname) { + keepField = true + } else if ityp != nil && fname == "error" { + // possibly the predeclared error interface; keep + // it for now but remember this interface so that + // it can be fixed if error is also defined locally + keepField = true + r.remember(ityp) + } + } else { + field.Names = filterIdentList(field.Names, false) + if len(field.Names) < n { + removedFields = true + } + if len(field.Names) > 0 { + keepField = true + } + } + if keepField { + r.filterType(nil, field.Type) + list[j] = field + j++ + } + } + if j < len(list) { + removedFields = true + } + fields.List = list[0:j] + return +} + +// filterParamList applies filterType to each parameter type in fields. +// +func (r *reader) filterParamList(fields *ast.FieldList) { + if fields != nil { + for _, f := range fields.List { + r.filterType(nil, f.Type) + } + } +} + +// filterType strips any unexported struct fields or method types from typ +// in place. If fields (or methods) have been removed, the corresponding +// struct or interface type has the Incomplete field set to true. +// +func (r *reader) filterType(parent *namedType, typ ast.Expr) { + switch t := typ.(type) { + case *ast.Ident: + // nothing to do + case *ast.ParenExpr: + r.filterType(nil, t.X) + case *ast.ArrayType: + r.filterType(nil, t.Elt) + case *ast.StructType: + if r.filterFieldList(parent, t.Fields, nil) { + t.Incomplete = true + } + case *ast.FuncType: + r.filterParamList(t.Params) + r.filterParamList(t.Results) + case *ast.InterfaceType: + if r.filterFieldList(parent, t.Methods, t) { + t.Incomplete = true + } + case *ast.MapType: + r.filterType(nil, t.Key) + r.filterType(nil, t.Value) + case *ast.ChanType: + r.filterType(nil, t.Value) + } +} + +func (r *reader) filterSpec(spec ast.Spec, tok token.Token) bool { + switch s := spec.(type) { + case *ast.ImportSpec: + // always keep imports so we can collect them + return true + case *ast.ValueSpec: + // special case: consider blank constants as exported + // (work-around for issue 5397) + s.Names = filterIdentList(s.Names, tok == token.CONST) + if len(s.Names) > 0 { + r.filterType(nil, s.Type) + return true + } + case *ast.TypeSpec: + if name := s.Name.Name; ast.IsExported(name) { + r.filterType(r.lookupType(s.Name.Name), s.Type) + return true + } else if name == "error" { + // special case: remember that error is declared locally + r.errorDecl = true + } + } + return false +} + +func (r *reader) filterSpecList(list []ast.Spec, tok token.Token) []ast.Spec { + j := 0 + for _, s := range list { + if r.filterSpec(s, tok) { + list[j] = s + j++ + } + } + return list[0:j] +} + +func (r *reader) filterDecl(decl ast.Decl) bool { + switch d := decl.(type) { + case *ast.GenDecl: + d.Specs = r.filterSpecList(d.Specs, d.Tok) + return len(d.Specs) > 0 + case *ast.FuncDecl: + // ok to filter these methods early because any + // conflicting method will be filtered here, too - + // thus, removing these methods early will not lead + // to the false removal of possible conflicts + return ast.IsExported(d.Name.Name) + } + return false +} + +// fileExports removes unexported declarations from src in place. +// +func (r *reader) fileExports(src *ast.File) { + j := 0 + for _, d := range src.Decls { + if r.filterDecl(d) { + src.Decls[j] = d + j++ + } + } + src.Decls = src.Decls[0:j] +} |