aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobert Griesemer <gri@golang.org>2011-12-22 13:11:40 -0800
committerRobert Griesemer <gri@golang.org>2011-12-22 13:11:40 -0800
commit7ea92ddd6620cb57c90e7add369ec2b3e6c17444 (patch)
tree4d865f9a89616f6f1c8a8d67418f6be5511c74ac
parentb1a287e3a1f61bcb36b3bdb7555bccd1bc104e0d (diff)
downloadgo-7ea92ddd6620cb57c90e7add369ec2b3e6c17444.tar.gz
go-7ea92ddd6620cb57c90e7add369ec2b3e6c17444.zip
go/doc, godoc: show methods of anonymous fields
Missing: Handling of embedded interfaces. Also, for reasons outlined in the previous CL (5500055), embedded types have to be exported for its "inherited" methods to be visible. This will be addressed w/ a subsequent CL. R=r, rsc CC=golang-dev https://golang.org/cl/5502059
-rw-r--r--src/pkg/go/doc/doc.go216
1 files changed, 166 insertions, 50 deletions
diff --git a/src/pkg/go/doc/doc.go b/src/pkg/go/doc/doc.go
index 52ebda5ea2..8d7a78ca1b 100644
--- a/src/pkg/go/doc/doc.go
+++ b/src/pkg/go/doc/doc.go
@@ -14,15 +14,24 @@ import (
// ----------------------------------------------------------------------------
+// embeddedType describes the type of an anonymous field.
+//
+type embeddedType struct {
+ typ *typeDoc // the corresponding base type
+ ptr bool // if set, the anonymous field type is a pointer
+}
+
type typeDoc struct {
// len(decl.Specs) == 1, and the element type is *ast.TypeSpec
// if the type declaration hasn't been seen yet, decl is nil
- decl *ast.GenDecl
+ decl *ast.GenDecl
+ embedded []embeddedType
+ forward *TypeDoc // forward link to processed type documentation
+
// declarations associated with the type
values []*ast.GenDecl // consts and vars
factories map[string]*ast.FuncDecl
methods map[string]*ast.FuncDecl
- embedded []*typeDoc // list of embedded types
}
// docReader accumulates documentation for a single package.
@@ -63,43 +72,19 @@ func (doc *docReader) addDoc(comments *ast.CommentGroup) {
doc.doc.List = append(list, comments.List...)
}
-func (doc *docReader) addType(decl *ast.GenDecl) *typeDoc {
- spec := decl.Specs[0].(*ast.TypeSpec)
- tdoc := doc.lookupTypeDoc(spec.Name.Name)
- // tdoc should always be != nil since declared types
- // are always named - be conservative and check
- if tdoc != nil {
- // a type should be added at most once, so tdoc.decl
- // should be nil - if it isn't, simply overwrite it
- tdoc.decl = decl
- }
- return tdoc
-}
-
func (doc *docReader) lookupTypeDoc(name string) *typeDoc {
- if name == "" {
+ if name == "" || name == "_" {
return nil // no type docs for anonymous types
}
if tdoc, found := doc.types[name]; found {
return tdoc
}
// type wasn't found - add one without declaration
- tdoc := &typeDoc{nil, nil, make(map[string]*ast.FuncDecl), make(map[string]*ast.FuncDecl), nil}
- doc.types[name] = tdoc
- return tdoc
-}
-
-func (doc *docReader) lookupEmbeddedDoc(name string) *typeDoc {
- if name == "" {
- return nil
+ tdoc := &typeDoc{
+ factories: make(map[string]*ast.FuncDecl),
+ methods: make(map[string]*ast.FuncDecl),
}
- if tdoc, found := doc.embedded[name]; found {
- return tdoc
- }
- // type wasn't found - add one without declaration
- // note: embedded types only have methods associated with them
- tdoc := &typeDoc{nil, nil, make(map[string]*ast.FuncDecl), make(map[string]*ast.FuncDecl), nil}
- doc.embedded[name] = tdoc
+ doc.types[name] = tdoc
return tdoc
}
@@ -235,10 +220,17 @@ func (doc *docReader) addDecl(decl ast.Decl) {
case token.TYPE:
// types are handled individually
for _, spec := range d.Specs {
- // make a (fake) GenDecl node for this TypeSpec
+ tspec := spec.(*ast.TypeSpec)
+ // add the type to the documentation
+ tdoc := doc.lookupTypeDoc(tspec.Name.Name)
+ if tdoc == nil {
+ continue // no name - ignore the type
+ }
+ // Make a (fake) GenDecl node for this TypeSpec
// (we need to do this here - as opposed to just
// for printing - so we don't lose the GenDecl
- // documentation)
+ // documentation). Since a new GenDecl node is
+ // created, there's no need to nil out d.Doc.
//
// TODO(gri): Consider just collecting the TypeSpec
// node (and copy in the GenDecl.doc if there is no
@@ -246,11 +238,12 @@ func (doc *docReader) addDecl(decl ast.Decl) {
// makeTypeDocs below). Simpler data structures, but
// would lose GenDecl documentation if the TypeSpec
// has documentation as well.
- tdoc := doc.addType(&ast.GenDecl{d.Doc, d.Pos(), token.TYPE, token.NoPos, []ast.Spec{spec}, token.NoPos})
- // A new GenDecl node is created, no need to nil out d.Doc.
- if tdoc == nil {
- continue // some error happened; ignore
- }
+ fake := &ast.GenDecl{d.Doc, d.Pos(), token.TYPE, token.NoPos,
+ []ast.Spec{tspec}, token.NoPos}
+ // A type should be added at most once, so tdoc.decl
+ // should be nil - if it isn't, simply overwrite it.
+ tdoc.decl = fake
+ // Look for anonymous fields that might contribute methods.
var fields *ast.FieldList
switch typ := spec.(*ast.TypeSpec).Type.(type) {
case *ast.StructType:
@@ -261,11 +254,13 @@ func (doc *docReader) addDecl(decl ast.Decl) {
if fields != nil {
for _, field := range fields.List {
if len(field.Names) == 0 {
- // anonymous field
+ // anonymous field - add corresponding type
+ // to the tdoc and collect it in doc
name := baseTypeName(field.Type, true)
- edoc := doc.lookupEmbeddedDoc(name)
+ edoc := doc.lookupTypeDoc(name)
if edoc != nil {
- tdoc.embedded = append(tdoc.embedded, edoc)
+ _, ptr := field.Type.(*ast.StarExpr)
+ tdoc.embedded = append(tdoc.embedded, embeddedType{edoc, ptr})
}
}
}
@@ -430,6 +425,25 @@ func makeFuncDocs(m map[string]*ast.FuncDecl) []*FuncDoc {
return d
}
+type methodSet map[string]*FuncDoc
+
+func (mset methodSet) add(m *FuncDoc) {
+ if mset[m.Name] == nil {
+ mset[m.Name] = m
+ }
+}
+
+func (mset methodSet) sortedList() []*FuncDoc {
+ list := make([]*FuncDoc, len(mset))
+ i := 0
+ for _, m := range mset {
+ list[i] = m
+ i++
+ }
+ sort.Sort(sortFuncDoc(list))
+ return list
+}
+
// TypeDoc is the documentation for a declared type.
// Consts and Vars are sorted lists of constants and variables of (mostly) that type.
// Factories is a sorted list of factory functions that return that type.
@@ -440,8 +454,9 @@ type TypeDoc struct {
Consts []*ValueDoc
Vars []*ValueDoc
Factories []*FuncDoc
- Methods []*FuncDoc
- Embedded []*FuncDoc
+ methods []*FuncDoc // top-level methods only
+ embedded methodSet // embedded methods only
+ Methods []*FuncDoc // all methods including embedded ones
Decl *ast.GenDecl
order int
}
@@ -464,7 +479,13 @@ func (p sortTypeDoc) Less(i, j int) bool {
// blocks, but the doc extractor above has split them into
// individual declarations.
func (doc *docReader) makeTypeDocs(m map[string]*typeDoc) []*TypeDoc {
- d := make([]*TypeDoc, len(m))
+ // TODO(gri) Consider computing the embedded method information
+ // before calling makeTypeDocs. Then this function can
+ // be single-phased again. Also, it might simplify some
+ // of the logic.
+ //
+ // phase 1: associate collected declarations with TypeDocs
+ list := make([]*TypeDoc, len(m))
i := 0
for _, old := range m {
// all typeDocs should have a declaration associated with
@@ -485,11 +506,16 @@ func (doc *docReader) makeTypeDocs(m map[string]*typeDoc) []*TypeDoc {
t.Consts = makeValueDocs(old.values, token.CONST)
t.Vars = makeValueDocs(old.values, token.VAR)
t.Factories = makeFuncDocs(old.factories)
- t.Methods = makeFuncDocs(old.methods)
- // TODO(gri) compute list of embedded methods
+ t.methods = makeFuncDocs(old.methods)
+ // The list of embedded types' methods is computed from the list
+ // of embedded types, some of which may not have been processed
+ // yet (i.e., their forward link is nil) - do this in a 2nd phase.
+ // The final list of methods can only be computed after that -
+ // do this in a 3rd phase.
t.Decl = old.decl
t.order = i
- d[i] = t
+ old.forward = t // old has been processed
+ list[i] = t
i++
} else {
// no corresponding type declaration found - move any associated
@@ -512,9 +538,99 @@ func (doc *docReader) makeTypeDocs(m map[string]*typeDoc) []*TypeDoc {
}
}
}
- d = d[0:i] // some types may have been ignored
- sort.Sort(sortTypeDoc(d))
- return d
+ list = list[0:i] // some types may have been ignored
+
+ // phase 2: collect embedded methods for each processed typeDoc
+ for _, old := range m {
+ if t := old.forward; t != nil {
+ // old has been processed into t; collect embedded
+ // methods for t from the list of processed embedded
+ // types in old (and thus for which the methods are known)
+ typ := t.Type
+ if _, ok := typ.Type.(*ast.StructType); ok {
+ // struct
+ t.embedded = make(methodSet)
+ collectEmbeddedMethods(t.embedded, old, typ.Name.Name)
+ } else {
+ // interface
+ // TODO(gri) fix this
+ }
+ }
+ }
+
+ // phase 3: compute final method set for each TypeDoc
+ for _, d := range list {
+ if len(d.embedded) > 0 {
+ // there are embedded methods - exclude
+ // the ones with names conflicting with
+ // non-embedded methods
+ mset := make(methodSet)
+ // top-level methods have priority
+ for _, m := range d.methods {
+ mset.add(m)
+ }
+ // add non-conflicting embedded methods
+ for _, m := range d.embedded {
+ mset.add(m)
+ }
+ d.Methods = mset.sortedList()
+ } else {
+ // no embedded methods
+ d.Methods = d.methods
+ }
+ }
+
+ sort.Sort(sortTypeDoc(list))
+ return list
+}
+
+// collectEmbeddedMethods collects the embedded methods from all
+// processed embedded types found in tdoc in mset. It considers
+// embedded types at the most shallow level first so that more
+// deeply nested embedded methods with conflicting names are
+// excluded.
+//
+func collectEmbeddedMethods(mset methodSet, tdoc *typeDoc, recvTypeName string) {
+ for _, e := range tdoc.embedded {
+ if e.typ.forward != nil { // == e was processed
+ for _, m := range e.typ.forward.methods {
+ mset.add(customizeRecv(m, e.ptr, recvTypeName))
+ }
+ collectEmbeddedMethods(mset, e.typ, recvTypeName)
+ }
+ }
+}
+
+func customizeRecv(m *FuncDoc, embeddedIsPtr bool, recvTypeName string) *FuncDoc {
+ if m == nil || m.Decl == nil || m.Decl.Recv == nil || len(m.Decl.Recv.List) != 1 {
+ return m // shouldn't happen, but be safe
+ }
+
+ // copy existing receiver field and set new type
+ // TODO(gri) is receiver type computation correct?
+ // what about deeply nested embeddings?
+ newField := *m.Decl.Recv.List[0]
+ _, origRecvIsPtr := newField.Type.(*ast.StarExpr)
+ var typ ast.Expr = ast.NewIdent(recvTypeName)
+ if embeddedIsPtr || origRecvIsPtr {
+ typ = &ast.StarExpr{token.NoPos, typ}
+ }
+ newField.Type = typ
+
+ // copy existing receiver field list and set new receiver field
+ newFieldList := *m.Decl.Recv
+ newFieldList.List = []*ast.Field{&newField}
+
+ // copy existing function declaration and set new receiver field list
+ newFuncDecl := *m.Decl
+ newFuncDecl.Recv = &newFieldList
+
+ // copy existing function documentation and set new declaration
+ newM := *m
+ newM.Decl = &newFuncDecl
+ newM.Recv = typ
+
+ return &newM
}
func makeBugDocs(list []*ast.CommentGroup) []string {