diff options
author | Robert Griesemer <gri@golang.org> | 2011-12-22 15:28:15 -0800 |
---|---|---|
committer | Robert Griesemer <gri@golang.org> | 2011-12-22 15:28:15 -0800 |
commit | 198936f2b871669cefbeb26a6991fbfa3d934254 (patch) | |
tree | cdff78283e8a16a661e50d431f3109ba6ef29f80 | |
parent | 07db252222253ac103ff46ed85a1cccc1f33b73d (diff) | |
download | go-198936f2b871669cefbeb26a6991fbfa3d934254.tar.gz go-198936f2b871669cefbeb26a6991fbfa3d934254.zip |
go/doc, godoc: move export filtering into go/doc
- 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
-rw-r--r-- | src/cmd/godoc/godoc.go | 24 | ||||
-rw-r--r-- | src/pkg/go/doc/Makefile | 2 | ||||
-rw-r--r-- | src/pkg/go/doc/doc.go | 126 | ||||
-rw-r--r-- | src/pkg/go/doc/exports.go | 167 | ||||
-rw-r--r-- | src/pkg/go/doc/filter.go | 105 |
5 files changed, 297 insertions, 127 deletions
diff --git a/src/cmd/godoc/godoc.go b/src/cmd/godoc/godoc.go index f6626a00fb..1785a1f98f 100644 --- a/src/cmd/godoc/godoc.go +++ b/src/cmd/godoc/godoc.go @@ -961,16 +961,6 @@ func inList(name string, list []string) bool { 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) @@ -1096,13 +1086,17 @@ func (h *httpHandler) getPageInfo(abspath, relpath, pkgname string, mode PageInf 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) } } diff --git a/src/pkg/go/doc/Makefile b/src/pkg/go/doc/Makefile index 2a421c3e6b..b27b85abea 100644 --- a/src/pkg/go/doc/Makefile +++ b/src/pkg/go/doc/Makefile @@ -9,6 +9,8 @@ GOFILES=\ comment.go\ doc.go\ example.go\ + exports.go\ + filter.go\ include ../../../Make.pkg diff --git a/src/pkg/go/doc/doc.go b/src/pkg/go/doc/doc.go index 888fbe1bfb..1bb22416c7 100644 --- a/src/pkg/go/doc/doc.go +++ b/src/pkg/go/doc/doc.go @@ -13,6 +13,7 @@ import ( ) // ---------------------------------------------------------------------------- +// Collection of documentation info // embeddedType describes the type of an anonymous field. // @@ -34,6 +35,10 @@ type typeInfo struct { 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 @@ -171,6 +176,9 @@ func setFunc(table map[string]*ast.FuncDecl, f *ast.FuncDecl) { } 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 @@ -257,10 +265,9 @@ func (doc *docReader) addDecl(decl ast.Decl) { // 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) } } } @@ -313,19 +320,15 @@ func (doc *docReader) addFile(src *ast.File) { 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++ @@ -674,104 +677,3 @@ func (doc *docReader) newDoc(importpath string, filenames []string) *PackageDoc 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 -} diff --git a/src/pkg/go/doc/exports.go b/src/pkg/go/doc/exports.go new file mode 100644 index 0000000000..9cd186a9c7 --- /dev/null +++ b/src/pkg/go/doc/exports.go @@ -0,0 +1,167 @@ +// 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 +} diff --git a/src/pkg/go/doc/filter.go b/src/pkg/go/doc/filter.go new file mode 100644 index 0000000000..71c2ebb68b --- /dev/null +++ b/src/pkg/go/doc/filter.go @@ -0,0 +1,105 @@ +// 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 +} |