Commit 97853b46 authored by Robert Griesemer's avatar Robert Griesemer

go/doc: steps towards collecting methods of embedded types

No visible external changes yet. The current approach is
a stop-gap approach: For methods of anonymous fields to be
seen, the anonymous field's types must be exported.

Missing: computing the actual MethodDocs and displaying them.

(Depending on the operation mode of godoc, the input to go/doc
is a pre-filtered AST with all non-exported nodes removed. Non-
exported anonymous fields are not even seen by go/doc in this
case, and it is impossible to collect associated (even exported)
methods. A correct fix will require some more significant re-
engineering; AST filtering will have to happen later, possibly
inside go/doc.)

R=golang-dev, rsc
CC=golang-dev
https://golang.org/cl/5500055
parent 721e19c2
......@@ -18,10 +18,11 @@ 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
// values, factory functions, and methods associated with the type
// 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.
......@@ -36,6 +37,7 @@ type docReader struct {
pkgName string
values []*ast.GenDecl // consts and vars
types map[string]*typeDoc
embedded map[string]*typeDoc // embedded types, possibly not exported
funcs map[string]*ast.FuncDecl
bugs []*ast.CommentGroup
}
......@@ -43,6 +45,7 @@ type docReader struct {
func (doc *docReader) init(pkgName string) {
doc.pkgName = pkgName
doc.types = make(map[string]*typeDoc)
doc.embedded = make(map[string]*typeDoc)
doc.funcs = make(map[string]*ast.FuncDecl)
}
......@@ -52,31 +55,25 @@ func (doc *docReader) addDoc(comments *ast.CommentGroup) {
doc.doc = comments
return
}
// More than one package comment: Usually there will be only
// one file with a package comment, but it's better to collect
// all comments than drop them on the floor.
// (This code isn't particularly clever - no amortized doubling is
// used - but this situation occurs rarely and is not time-critical.)
n1 := len(doc.doc.List)
n2 := len(comments.List)
list := make([]*ast.Comment, n1+1+n2) // + 1 for separator line
copy(list, doc.doc.List)
list[n1] = &ast.Comment{token.NoPos, "//"} // separator line
copy(list[n1+1:], comments.List)
doc.doc = &ast.CommentGroup{list}
}
func (doc *docReader) addType(decl *ast.GenDecl) {
blankComment := &ast.Comment{token.NoPos, "//"}
list := append(doc.doc.List, blankComment)
doc.doc.List = append(list, comments.List...)
}
func (doc *docReader) addType(decl *ast.GenDecl) *typeDoc {
spec := decl.Specs[0].(*ast.TypeSpec)
typ := doc.lookupTypeDoc(spec.Name.Name)
// typ should always be != nil since declared types
tdoc := doc.lookupTypeDoc(spec.Name.Name)
// tdoc should always be != nil since declared types
// are always named - be conservative and check
if typ != nil {
// a type should be added at most once, so typ.decl
if tdoc != nil {
// a type should be added at most once, so tdoc.decl
// should be nil - if it isn't, simply overwrite it
typ.decl = decl
tdoc.decl = decl
}
return tdoc
}
func (doc *docReader) lookupTypeDoc(name string) *typeDoc {
......@@ -87,21 +84,35 @@ func (doc *docReader) lookupTypeDoc(name string) *typeDoc {
return tdoc
}
// type wasn't found - add one without declaration
tdoc := &typeDoc{nil, nil, make(map[string]*ast.FuncDecl), make(map[string]*ast.FuncDecl)}
tdoc := &typeDoc{nil, nil, make(map[string]*ast.FuncDecl), make(map[string]*ast.FuncDecl), nil}
doc.types[name] = tdoc
return tdoc
}
func baseTypeName(typ ast.Expr) string {
func (doc *docReader) lookupEmbeddedDoc(name string) *typeDoc {
if name == "" {
return nil
}
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
return tdoc
}
func baseTypeName(typ ast.Expr, allTypes bool) string {
switch t := typ.(type) {
case *ast.Ident:
// if the type is not exported, the effect to
// a client is as if there were no type name
if t.IsExported() {
if t.IsExported() || allTypes {
return t.Name
}
case *ast.StarExpr:
return baseTypeName(t.X)
return baseTypeName(t.X, allTypes)
}
return ""
}
......@@ -120,7 +131,7 @@ func (doc *docReader) addValue(decl *ast.GenDecl) {
switch {
case v.Type != nil:
// a type is present; determine its name
name = baseTypeName(v.Type)
name = baseTypeName(v.Type, false)
case decl.Tok == token.CONST:
// no type is present but we have a constant declaration;
// use the previous type name (w/o more type information
......@@ -178,7 +189,7 @@ func (doc *docReader) addFunc(fun *ast.FuncDecl) {
// determine if it should be associated with a type
if fun.Recv != nil {
// method
typ := doc.lookupTypeDoc(baseTypeName(fun.Recv.List[0].Type))
typ := doc.lookupTypeDoc(baseTypeName(fun.Recv.List[0].Type, false))
if typ != nil {
// exported receiver type
setFunc(typ.methods, fun)
......@@ -199,7 +210,7 @@ func (doc *docReader) addFunc(fun *ast.FuncDecl) {
// exactly one (named or anonymous) result associated
// with the first type in result signature (there may
// be more than one result)
tname := baseTypeName(res.Type)
tname := baseTypeName(res.Type, false)
typ := doc.lookupTypeDoc(tname)
if typ != nil {
// named and exported result type
......@@ -235,8 +246,30 @@ func (doc *docReader) addDecl(decl ast.Decl) {
// makeTypeDocs below). Simpler data structures, but
// would lose GenDecl documentation if the TypeSpec
// has documentation as well.
doc.addType(&ast.GenDecl{d.Doc, d.Pos(), token.TYPE, token.NoPos, []ast.Spec{spec}, token.NoPos})
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
}
var fields *ast.FieldList
switch typ := spec.(*ast.TypeSpec).Type.(type) {
case *ast.StructType:
fields = typ.Fields
case *ast.InterfaceType:
fields = typ.Methods
}
if fields == nil {
for _, field := range fields.List {
if len(field.Names) == 0 {
// anonymous field
name := baseTypeName(field.Type, true)
edoc := doc.lookupEmbeddedDoc(name)
if edoc != nil {
tdoc.embedded = append(tdoc.embedded, edoc)
}
}
}
}
}
}
}
......@@ -408,6 +441,7 @@ type TypeDoc struct {
Vars []*ValueDoc
Factories []*FuncDoc
Methods []*FuncDoc
Embedded []*FuncDoc
Decl *ast.GenDecl
order int
}
......@@ -452,6 +486,7 @@ func (doc *docReader) makeTypeDocs(m map[string]*typeDoc) []*TypeDoc {
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.Decl = old.decl
t.order = i
d[i] = t
......
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