- exports.go contains a stripped-down (but semantically unchanged)
version of the code in go/ast/filter.go for export filtering
- filter.go contains the documentation filtering code found before
at the end of doc.go; this is simply a code move w/o any semantic
changes
- godoc now relies on go/doc for export filtering when creating
documentation. It still has a separate form of export filtering
for showing the source code version. This needs to be consolidated
(perhaps the source form view should just be removed?).
- Stripping of function bodies (stripFunctionBodies function of
godoc.go) is now happening in doc.go (line 176).
- doc.NewPackageDoc has an extra parameter "exportsOnly. If set
to false, the behavior is as before. This function is only called
once in our source code; a gofix module is probably not warranted.
- Deleted doc.NewFileDoc - was never called.
This change is mostly a code move w/ some minimal tweaks. It should
not cause any changes to the behavior of godoc. It's a prerequisite
for extracting anonymous embedded fields.
R=golang-dev, r
CC=golang-dev
https://golang.org/cl/
5502072
return false
}
-func stripFunctionBodies(pkg *ast.Package) {
- for _, f := range pkg.Files {
- for _, d := range f.Decls {
- if f, ok := d.(*ast.FuncDecl); ok {
- f.Body = nil
- }
- }
- }
-}
-
// getPageInfo returns the PageInfo for a package directory abspath. If the
// parameter genAST is set, an AST containing only the package exports is
// computed (PageInfo.PAst), otherwise package documentation (PageInfo.Doc)
var past *ast.File
var pdoc *doc.PackageDoc
if pkg != nil {
- if mode&noFiltering == 0 {
- ast.PackageExports(pkg)
- }
+ exportsOnly := mode&noFiltering == 0
if mode&showSource == 0 {
- stripFunctionBodies(pkg)
- pdoc = doc.NewPackageDoc(pkg, path.Clean(relpath)) // no trailing '/' in importpath
+ // show extracted documentation
+ pdoc = doc.NewPackageDoc(pkg, path.Clean(relpath), exportsOnly) // no trailing '/' in importpath
} else {
+ // show source code
+ // TODO(gri) Consider eliminating export filtering in this mode,
+ // or perhaps eliminating the mode altogether.
+ if exportsOnly {
+ ast.PackageExports(pkg)
+ }
past = ast.MergePackageFiles(pkg, ast.FilterUnassociatedComments)
}
}
comment.go\
doc.go\
example.go\
+ exports.go\
+ filter.go\
include ../../../Make.pkg
)
// ----------------------------------------------------------------------------
+// Collection of documentation info
// embeddedType describes the type of an anonymous field.
//
methods map[string]*ast.FuncDecl
}
+func (info *typeInfo) addEmbeddedType(embedded *typeInfo, isPtr bool) {
+ info.embedded = append(info.embedded, embeddedType{embedded, isPtr})
+}
+
// docReader accumulates documentation for a single package.
// It modifies the AST: Comments (declaration documentation)
// that have been collected by the DocReader are set to nil
}
func (doc *docReader) addFunc(fun *ast.FuncDecl) {
+ // strip function body
+ fun.Body = nil
+
// determine if it should be associated with a type
if fun.Recv != nil {
// method
// anonymous field - add corresponding type
// to the info and collect it in doc
name := baseTypeName(field.Type, true)
- edoc := doc.lookupTypeInfo(name)
- if edoc != nil {
+ if embedded := doc.lookupTypeInfo(name); embedded != nil {
_, ptr := field.Type.(*ast.StarExpr)
- info.embedded = append(info.embedded, embeddedType{edoc, ptr})
+ info.addEmbeddedType(embedded, ptr)
}
}
}
src.Comments = nil // consumed unassociated comments - remove from ast.File node
}
-func NewFileDoc(file *ast.File) *PackageDoc {
- var r docReader
- r.init(file.Name.Name)
- r.addFile(file)
- return r.newDoc("", nil)
-}
-
-func NewPackageDoc(pkg *ast.Package, importpath string) *PackageDoc {
+func NewPackageDoc(pkg *ast.Package, importpath string, exportsOnly bool) *PackageDoc {
var r docReader
r.init(pkg.Name)
filenames := make([]string, len(pkg.Files))
i := 0
for filename, f := range pkg.Files {
+ if exportsOnly {
+ r.fileExports(f)
+ }
r.addFile(f)
filenames[i] = filename
i++
p.Bugs = makeBugDocs(doc.bugs)
return p
}
-
-// ----------------------------------------------------------------------------
-// Filtering by name
-
-type Filter func(string) bool
-
-func matchFields(fields *ast.FieldList, f Filter) bool {
- if fields != nil {
- for _, field := range fields.List {
- for _, name := range field.Names {
- if f(name.Name) {
- return true
- }
- }
- }
- }
- return false
-}
-
-func matchDecl(d *ast.GenDecl, f Filter) bool {
- for _, d := range d.Specs {
- switch v := d.(type) {
- case *ast.ValueSpec:
- for _, name := range v.Names {
- if f(name.Name) {
- return true
- }
- }
- case *ast.TypeSpec:
- if f(v.Name.Name) {
- return true
- }
- switch t := v.Type.(type) {
- case *ast.StructType:
- if matchFields(t.Fields, f) {
- return true
- }
- case *ast.InterfaceType:
- if matchFields(t.Methods, f) {
- return true
- }
- }
- }
- }
- return false
-}
-
-func filterValueDocs(a []*ValueDoc, f Filter) []*ValueDoc {
- w := 0
- for _, vd := range a {
- if matchDecl(vd.Decl, f) {
- a[w] = vd
- w++
- }
- }
- return a[0:w]
-}
-
-func filterFuncDocs(a []*FuncDoc, f Filter) []*FuncDoc {
- w := 0
- for _, fd := range a {
- if f(fd.Name) {
- a[w] = fd
- w++
- }
- }
- return a[0:w]
-}
-
-func filterTypeDocs(a []*TypeDoc, f Filter) []*TypeDoc {
- w := 0
- for _, td := range a {
- n := 0 // number of matches
- if matchDecl(td.Decl, f) {
- n = 1
- } else {
- // type name doesn't match, but we may have matching consts, vars, factories or methods
- td.Consts = filterValueDocs(td.Consts, f)
- td.Vars = filterValueDocs(td.Vars, f)
- td.Factories = filterFuncDocs(td.Factories, f)
- td.Methods = filterFuncDocs(td.Methods, f)
- n += len(td.Consts) + len(td.Vars) + len(td.Factories) + len(td.Methods)
- }
- if n > 0 {
- a[w] = td
- w++
- }
- }
- return a[0:w]
-}
-
-// Filter eliminates documentation for names that don't pass through the filter f.
-// TODO: Recognize "Type.Method" as a name.
-//
-func (p *PackageDoc) Filter(f Filter) {
- p.Consts = filterValueDocs(p.Consts, f)
- p.Vars = filterValueDocs(p.Vars, f)
- p.Types = filterTypeDocs(p.Types, f)
- p.Funcs = filterFuncDocs(p.Funcs, f)
- p.Doc = "" // don't show top-level package doc
-}
--- /dev/null
+// 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"
+
+func filterIdentList(list []*ast.Ident) []*ast.Ident {
+ j := 0
+ for _, x := range list {
+ if ast.IsExported(x.Name) {
+ list[j] = x
+ j++
+ }
+ }
+ return list[0:j]
+}
+
+func baseName(x ast.Expr) *ast.Ident {
+ switch t := x.(type) {
+ case *ast.Ident:
+ return t
+ case *ast.SelectorExpr:
+ if _, ok := t.X.(*ast.Ident); ok {
+ return t.Sel
+ }
+ case *ast.StarExpr:
+ return baseName(t.X)
+ }
+ return nil
+}
+
+func (doc *docReader) filterFieldList(fields *ast.FieldList) (removedFields bool) {
+ if fields == nil {
+ return false
+ }
+ list := fields.List
+ j := 0
+ for _, f := range list {
+ keepField := false
+ if len(f.Names) == 0 {
+ // anonymous field
+ name := baseName(f.Type)
+ keepField = name != nil && name.IsExported()
+ } else {
+ n := len(f.Names)
+ f.Names = filterIdentList(f.Names)
+ if len(f.Names) < n {
+ removedFields = true
+ }
+ keepField = len(f.Names) > 0
+ }
+ if keepField {
+ doc.filterType(f.Type)
+ list[j] = f
+ j++
+ }
+ }
+ if j < len(list) {
+ removedFields = true
+ }
+ fields.List = list[0:j]
+ return
+}
+
+func (doc *docReader) filterParamList(fields *ast.FieldList) bool {
+ if fields == nil {
+ return false
+ }
+ var b bool
+ for _, f := range fields.List {
+ if doc.filterType(f.Type) {
+ b = true
+ }
+ }
+ return b
+}
+
+func (doc *docReader) filterType(typ ast.Expr) bool {
+ switch t := typ.(type) {
+ case *ast.Ident:
+ return ast.IsExported(t.Name)
+ case *ast.ParenExpr:
+ return doc.filterType(t.X)
+ case *ast.ArrayType:
+ return doc.filterType(t.Elt)
+ case *ast.StructType:
+ if doc.filterFieldList(t.Fields) {
+ t.Incomplete = true
+ }
+ return len(t.Fields.List) > 0
+ case *ast.FuncType:
+ b1 := doc.filterParamList(t.Params)
+ b2 := doc.filterParamList(t.Results)
+ return b1 || b2
+ case *ast.InterfaceType:
+ if doc.filterFieldList(t.Methods) {
+ t.Incomplete = true
+ }
+ return len(t.Methods.List) > 0
+ case *ast.MapType:
+ b1 := doc.filterType(t.Key)
+ b2 := doc.filterType(t.Value)
+ return b1 || b2
+ case *ast.ChanType:
+ return doc.filterType(t.Value)
+ }
+ return false
+}
+
+func (doc *docReader) filterSpec(spec ast.Spec) bool {
+ switch s := spec.(type) {
+ case *ast.ValueSpec:
+ s.Names = filterIdentList(s.Names)
+ if len(s.Names) > 0 {
+ doc.filterType(s.Type)
+ return true
+ }
+ case *ast.TypeSpec:
+ if ast.IsExported(s.Name.Name) {
+ doc.filterType(s.Type)
+ return true
+ }
+ }
+ return false
+}
+
+func (doc *docReader) filterSpecList(list []ast.Spec) []ast.Spec {
+ j := 0
+ for _, s := range list {
+ if doc.filterSpec(s) {
+ list[j] = s
+ j++
+ }
+ }
+ return list[0:j]
+}
+
+func (doc *docReader) filterDecl(decl ast.Decl) bool {
+ switch d := decl.(type) {
+ case *ast.GenDecl:
+ d.Specs = doc.filterSpecList(d.Specs)
+ return len(d.Specs) > 0
+ case *ast.FuncDecl:
+ return ast.IsExported(d.Name.Name)
+ }
+ return false
+}
+
+// fileExports trims the AST for a Go file in place such that
+// only exported nodes remain. fileExports returns true if
+// there are exported declarations; otherwise it returns false.
+//
+func (doc *docReader) fileExports(src *ast.File) bool {
+ j := 0
+ for _, d := range src.Decls {
+ if doc.filterDecl(d) {
+ src.Decls[j] = d
+ j++
+ }
+ }
+ src.Decls = src.Decls[0:j]
+ return j > 0
+}
--- /dev/null
+// Copyright 2009 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.
+
+package doc
+
+import "go/ast"
+
+type Filter func(string) bool
+
+func matchFields(fields *ast.FieldList, f Filter) bool {
+ if fields != nil {
+ for _, field := range fields.List {
+ for _, name := range field.Names {
+ if f(name.Name) {
+ return true
+ }
+ }
+ }
+ }
+ return false
+}
+
+func matchDecl(d *ast.GenDecl, f Filter) bool {
+ for _, d := range d.Specs {
+ switch v := d.(type) {
+ case *ast.ValueSpec:
+ for _, name := range v.Names {
+ if f(name.Name) {
+ return true
+ }
+ }
+ case *ast.TypeSpec:
+ if f(v.Name.Name) {
+ return true
+ }
+ switch t := v.Type.(type) {
+ case *ast.StructType:
+ if matchFields(t.Fields, f) {
+ return true
+ }
+ case *ast.InterfaceType:
+ if matchFields(t.Methods, f) {
+ return true
+ }
+ }
+ }
+ }
+ return false
+}
+
+func filterValueDocs(a []*ValueDoc, f Filter) []*ValueDoc {
+ w := 0
+ for _, vd := range a {
+ if matchDecl(vd.Decl, f) {
+ a[w] = vd
+ w++
+ }
+ }
+ return a[0:w]
+}
+
+func filterFuncDocs(a []*FuncDoc, f Filter) []*FuncDoc {
+ w := 0
+ for _, fd := range a {
+ if f(fd.Name) {
+ a[w] = fd
+ w++
+ }
+ }
+ return a[0:w]
+}
+
+func filterTypeDocs(a []*TypeDoc, f Filter) []*TypeDoc {
+ w := 0
+ for _, td := range a {
+ n := 0 // number of matches
+ if matchDecl(td.Decl, f) {
+ n = 1
+ } else {
+ // type name doesn't match, but we may have matching consts, vars, factories or methods
+ td.Consts = filterValueDocs(td.Consts, f)
+ td.Vars = filterValueDocs(td.Vars, f)
+ td.Factories = filterFuncDocs(td.Factories, f)
+ td.Methods = filterFuncDocs(td.Methods, f)
+ n += len(td.Consts) + len(td.Vars) + len(td.Factories) + len(td.Methods)
+ }
+ if n > 0 {
+ a[w] = td
+ w++
+ }
+ }
+ return a[0:w]
+}
+
+// Filter eliminates documentation for names that don't pass through the filter f.
+// TODO: Recognize "Type.Method" as a name.
+//
+func (p *PackageDoc) Filter(f Filter) {
+ p.Consts = filterValueDocs(p.Consts, f)
+ p.Vars = filterValueDocs(p.Vars, f)
+ p.Types = filterTypeDocs(p.Types, f)
+ p.Funcs = filterFuncDocs(p.Funcs, f)
+ p.Doc = "" // don't show top-level package doc
+}