Commit 0389051a authored by Robert Griesemer's avatar Robert Griesemer

go/ast, go/doc, godoc: consider struct fields and interface methods when filtering ASTs

So far, only top-level names where considered when trimming ASTs
using a filter function. For instance, "godoc reflect Implements"
didn't show the "Implements" method of the type Interface because
the local method name was not considered (on the other hand, "top-
level" declared methods associated with types were considered).

With this CL, AST filter functions look also at struct fields
and interface methods.

R=rsc, r
CC=golang-dev
https://golang.org/cl/4518050
parent 69a91663
......@@ -206,13 +206,43 @@ func filterIdentList(list []*Ident, f Filter) []*Ident {
}
func filterFieldList(list []*Field, f Filter) []*Field {
j := 0
for _, field := range list {
field.Names = filterIdentList(field.Names, f)
if len(field.Names) > 0 {
list[j] = field
j++
}
}
return list[0:j]
}
func filterFields(fields *FieldList, f Filter) bool {
if fields == nil {
return false
}
fields.List = filterFieldList(fields.List, f)
return len(fields.List) > 0
}
func filterSpec(spec Spec, f Filter) bool {
switch s := spec.(type) {
case *ValueSpec:
s.Names = filterIdentList(s.Names, f)
return len(s.Names) > 0
case *TypeSpec:
return f(s.Name.Name)
if f(s.Name.Name) {
return true
}
switch t := s.Type.(type) {
case *StructType:
return filterFields(t.Fields, f)
case *InterfaceType:
return filterFields(t.Methods, f)
}
}
return false
}
......@@ -230,7 +260,14 @@ func filterSpecList(list []Spec, f Filter) []Spec {
}
func filterDecl(decl Decl, f Filter) bool {
// FilterDecl trims the AST for a Go declaration in place by removing
// all names (including struct field and interface method names, but
// not from parameter lists) that don't pass through the filter f.
//
// FilterDecl returns true if there are any declared names left after
// filtering; it returns false otherwise.
//
func FilterDecl(decl Decl, f Filter) bool {
switch d := decl.(type) {
case *GenDecl:
d.Specs = filterSpecList(d.Specs, f)
......@@ -243,10 +280,10 @@ func filterDecl(decl Decl, f Filter) bool {
// FilterFile trims the AST for a Go file in place by removing all
// names from top-level declarations (but not from parameter lists
// or inside types) that don't pass through the filter f. If the
// declaration is empty afterwards, the declaration is removed from
// the AST.
// names from top-level declarations (including struct field and
// interface method names, but not from parameter lists) that don't
// pass through the filter f. If the declaration is empty afterwards,
// the declaration is removed from the AST.
// The File.comments list is not changed.
//
// FilterFile returns true if there are any top-level declarations
......@@ -255,7 +292,7 @@ func filterDecl(decl Decl, f Filter) bool {
func FilterFile(src *File, f Filter) bool {
j := 0
for _, d := range src.Decls {
if filterDecl(d, f) {
if FilterDecl(d, f) {
src.Decls[j] = d
j++
}
......@@ -266,10 +303,10 @@ func FilterFile(src *File, f Filter) bool {
// FilterPackage trims the AST for a Go package in place by removing all
// names from top-level declarations (but not from parameter lists
// or inside types) that don't pass through the filter f. If the
// declaration is empty afterwards, the declaration is removed from
// the AST.
// names from top-level declarations (including struct field and
// interface method names, but not from parameter lists) that don't
// pass through the filter f. If the declaration is empty afterwards,
// the declaration is removed from the AST.
// The pkg.Files list is not changed, so that file names and top-level
// package comments don't get lost.
//
......
......@@ -569,32 +569,10 @@ func (doc *docReader) newDoc(importpath string, filenames []string) *PackageDoc
// ----------------------------------------------------------------------------
// Filtering by name
type Filter func(string) bool
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
}
}
}
return false
}
func filterValueDocs(a []*ValueDoc, f Filter) []*ValueDoc {
func filterValueDocs(a []*ValueDoc, f ast.Filter) []*ValueDoc {
w := 0
for _, vd := range a {
if matchDecl(vd.Decl, f) {
if ast.FilterDecl(vd.Decl, f) {
a[w] = vd
w++
}
......@@ -603,7 +581,7 @@ func filterValueDocs(a []*ValueDoc, f Filter) []*ValueDoc {
}
func filterFuncDocs(a []*FuncDoc, f Filter) []*FuncDoc {
func filterFuncDocs(a []*FuncDoc, f ast.Filter) []*FuncDoc {
w := 0
for _, fd := range a {
if f(fd.Name) {
......@@ -615,11 +593,11 @@ func filterFuncDocs(a []*FuncDoc, f Filter) []*FuncDoc {
}
func filterTypeDocs(a []*TypeDoc, f Filter) []*TypeDoc {
func filterTypeDocs(a []*TypeDoc, f ast.Filter) []*TypeDoc {
w := 0
for _, td := range a {
n := 0 // number of matches
if matchDecl(td.Decl, f) {
if ast.FilterDecl(td.Decl, f) {
n = 1
} else {
// type name doesn't match, but we may have matching consts, vars, factories or methods
......@@ -641,7 +619,7 @@ func filterTypeDocs(a []*TypeDoc, f Filter) []*TypeDoc {
// 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) {
func (p *PackageDoc) Filter(f ast.Filter) {
p.Consts = filterValueDocs(p.Consts, f)
p.Vars = filterValueDocs(p.Vars, f)
p.Types = filterTypeDocs(p.Types, f)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment