Commit 636cdc76 authored by Robert Griesemer's avatar Robert Griesemer

- collect consts and vars in one list

- handle absence of forward-decls correctly
  (cannot assume a type was declared before it was used)

R=rsc
DELTA=112  (32 added, 38 deleted, 42 changed)
OCL=34008
CL=34027
parent ee31157e
...@@ -19,7 +19,10 @@ import ( ...@@ -19,7 +19,10 @@ import (
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
type typeDoc struct { type typeDoc struct {
decl *ast.GenDecl; // len(decl.Specs) == 1, and the element type is *ast.TypeSpec // 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;
// factory functions and methods associated with the type
factories map[string] *ast.FuncDecl; factories map[string] *ast.FuncDecl;
methods map[string] *ast.FuncDecl; methods map[string] *ast.FuncDecl;
} }
...@@ -34,23 +37,34 @@ type typeDoc struct { ...@@ -34,23 +37,34 @@ type typeDoc struct {
// //
type docReader struct { type docReader struct {
doc *ast.CommentGroup; // package documentation, if any doc *ast.CommentGroup; // package documentation, if any
consts *vector.Vector; // list of *ast.GenDecl values *vector.Vector; // list of *ast.GenDecl (consts and vars)
types map[string] *typeDoc; types map[string] *typeDoc;
vars *vector.Vector; // list of *ast.GenDecl
funcs map[string] *ast.FuncDecl; funcs map[string] *ast.FuncDecl;
bugs *vector.Vector; // list of *ast.CommentGroup bugs *vector.Vector; // list of *ast.CommentGroup
} }
func (doc *docReader) init() { func (doc *docReader) init() {
doc.consts = vector.New(0); doc.values = vector.New(0);
doc.types = make(map[string] *typeDoc); doc.types = make(map[string] *typeDoc);
doc.vars = vector.New(0);
doc.funcs = make(map[string] *ast.FuncDecl); doc.funcs = make(map[string] *ast.FuncDecl);
doc.bugs = vector.New(0); doc.bugs = vector.New(0);
} }
func (doc *docReader) addType(decl *ast.GenDecl) {
spec := decl.Specs[0].(*ast.TypeSpec);
typ := doc.lookupTypeDoc(spec.Name.Value);
// typ 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
// should be nil - if it isn't, simply overwrite it
typ.decl = decl;
}
}
func baseTypeName(typ ast.Expr) string { func baseTypeName(typ ast.Expr) string {
switch t := typ.(type) { switch t := typ.(type) {
case *ast.Ident: case *ast.Ident:
...@@ -62,39 +76,17 @@ func baseTypeName(typ ast.Expr) string { ...@@ -62,39 +76,17 @@ func baseTypeName(typ ast.Expr) string {
} }
func (doc *docReader) lookupTypeDoc(typ ast.Expr) *typeDoc { func (doc *docReader) lookupTypeDoc(name string) *typeDoc {
tdoc, found := doc.types[baseTypeName(typ)]; if name == "" {
if found { return nil; // no type docs for anonymous types
return tdoc;
} }
return nil;
}
func isForwardDecl(typ ast.Expr) bool {
switch t := typ.(type) {
case *ast.StructType:
return t.Fields == nil;
case *ast.InterfaceType:
return t.Methods == nil;
}
return false;
}
func (doc *docReader) addType(decl *ast.GenDecl) {
spec := decl.Specs[0].(*ast.TypeSpec);
name := spec.Name.Value;
if tdoc, found := doc.types[name]; found { if tdoc, found := doc.types[name]; found {
if !isForwardDecl(tdoc.decl.Specs[0].(*ast.TypeSpec).Type) || isForwardDecl(spec.Type) { return tdoc;
// existing type was not a forward-declaration or the
// new type is a forward declaration - leave it alone
return;
}
// replace existing type
} }
tdoc := &typeDoc{decl, make(map[string] *ast.FuncDecl), make(map[string] *ast.FuncDecl)}; // type wasn't found - add one without declaration
tdoc := &typeDoc{nil, make(map[string] *ast.FuncDecl), make(map[string] *ast.FuncDecl)};
doc.types[name] = tdoc; doc.types[name] = tdoc;
return tdoc;
} }
...@@ -102,21 +94,15 @@ func (doc *docReader) addFunc(fun *ast.FuncDecl) { ...@@ -102,21 +94,15 @@ func (doc *docReader) addFunc(fun *ast.FuncDecl) {
name := fun.Name.Value; name := fun.Name.Value;
// determine if it should be associated with a type // determine if it should be associated with a type
var typ *typeDoc;
if fun.Recv != nil { if fun.Recv != nil {
// method // method
// (all receiver types must be declared before they are used) typ := doc.lookupTypeDoc(baseTypeName(fun.Recv.Type));
// TODO(gri) Reconsider this logic if no forward-declarations // typ should always be != nil since receiver base
// are required anymore. // types must be named - be conservative and check
typ = doc.lookupTypeDoc(fun.Recv.Type);
if typ != nil { if typ != nil {
// type found (i.e., exported)
typ.methods[name] = fun; typ.methods[name] = fun;
return;
} }
// if the type wasn't found, it wasn't exported
// TODO(gri): a non-exported type may still have exported functions
// determine what to do in that case
return;
} }
// perhaps a factory function // perhaps a factory function
...@@ -125,8 +111,9 @@ func (doc *docReader) addFunc(fun *ast.FuncDecl) { ...@@ -125,8 +111,9 @@ func (doc *docReader) addFunc(fun *ast.FuncDecl) {
res := fun.Type.Results[0]; res := fun.Type.Results[0];
if len(res.Names) <= 1 { if len(res.Names) <= 1 {
// exactly one (named or anonymous) result type // exactly one (named or anonymous) result type
typ = doc.lookupTypeDoc(res.Type); typ := doc.lookupTypeDoc(baseTypeName(res.Type));
if typ != nil { if typ != nil {
// named result type
typ.factories[name] = fun; typ.factories[name] = fun;
return; return;
} }
...@@ -134,6 +121,7 @@ func (doc *docReader) addFunc(fun *ast.FuncDecl) { ...@@ -134,6 +121,7 @@ func (doc *docReader) addFunc(fun *ast.FuncDecl) {
} }
// ordinary function // ordinary function
// (or method that was not associated to a type for some reason)
doc.funcs[name] = fun; doc.funcs[name] = fun;
} }
...@@ -143,11 +131,9 @@ func (doc *docReader) addDecl(decl ast.Decl) { ...@@ -143,11 +131,9 @@ func (doc *docReader) addDecl(decl ast.Decl) {
case *ast.GenDecl: case *ast.GenDecl:
if len(d.Specs) > 0 { if len(d.Specs) > 0 {
switch d.Tok { switch d.Tok {
case token.IMPORT: case token.CONST, token.VAR:
// ignore // constants and variables are always handled as a group
case token.CONST: doc.values.Push(d);
// constants are always handled as a group
doc.consts.Push(d);
case token.TYPE: case token.TYPE:
// types are handled individually // types are handled individually
var noPos token.Position; var noPos token.Position;
...@@ -166,9 +152,6 @@ func (doc *docReader) addDecl(decl ast.Decl) { ...@@ -166,9 +152,6 @@ func (doc *docReader) addDecl(decl ast.Decl) {
doc.addType(&ast.GenDecl{d.Doc, d.Pos(), token.TYPE, noPos, []ast.Spec{spec}, noPos}); doc.addType(&ast.GenDecl{d.Doc, d.Pos(), token.TYPE, noPos, []ast.Spec{spec}, noPos});
// A new GenDecl node is created, no need to nil out d.Doc. // A new GenDecl node is created, no need to nil out d.Doc.
} }
case token.VAR:
// variables are always handled as a group
doc.vars.Push(d);
} }
} }
case *ast.FuncDecl: case *ast.FuncDecl:
...@@ -314,13 +297,18 @@ func (p sortValueDoc) Less(i, j int) bool { ...@@ -314,13 +297,18 @@ func (p sortValueDoc) Less(i, j int) bool {
} }
func makeValueDocs(v *vector.Vector) []*ValueDoc { func makeValueDocs(v *vector.Vector, tok token.Token) []*ValueDoc {
d := make([]*ValueDoc, v.Len()); d := make([]*ValueDoc, v.Len()); // big enough in any case
n := 0;
for i := range d { for i := range d {
decl := v.At(i).(*ast.GenDecl); decl := v.At(i).(*ast.GenDecl);
d[i] = &ValueDoc{astComment(decl.Doc), decl, i}; if decl.Tok == tok {
decl.Doc = nil; // doc consumed - removed from AST d[n] = &ValueDoc{astComment(decl.Doc), decl, i};
n++;
decl.Doc = nil; // doc consumed - removed from AST
}
} }
d = d[0 : n];
sort.Sort(sortValueDoc(d)); sort.Sort(sortValueDoc(d));
return d; return d;
} }
...@@ -395,24 +383,30 @@ func makeTypeDocs(m map[string] *typeDoc) []*TypeDoc { ...@@ -395,24 +383,30 @@ func makeTypeDocs(m map[string] *typeDoc) []*TypeDoc {
d := make([]*TypeDoc, len(m)); d := make([]*TypeDoc, len(m));
i := 0; i := 0;
for _, old := range m { for _, old := range m {
typespec := old.decl.Specs[0].(*ast.TypeSpec); // all typeDocs should have a declaration associated with
t := new(TypeDoc); // them after processing an entire package - be conservative
doc := typespec.Doc; // and check
typespec.Doc = nil; // doc consumed - remove from ast.TypeSpec node if decl := old.decl; decl != nil {
if doc == nil { typespec := decl.Specs[0].(*ast.TypeSpec);
// no doc associated with the spec, use the declaration doc, if any t := new(TypeDoc);
doc = old.decl.Doc; doc := typespec.Doc;
typespec.Doc = nil; // doc consumed - remove from ast.TypeSpec node
if doc == nil {
// no doc associated with the spec, use the declaration doc, if any
doc = decl.Doc;
}
decl.Doc = nil; // doc consumed - remove from ast.Decl node
t.Doc = astComment(doc);
t.Type = typespec;
t.Factories = makeFuncDocs(old.factories);
t.Methods = makeFuncDocs(old.methods);
t.Decl = old.decl;
t.order = i;
d[i] = t;
i++;
} }
old.decl.Doc = nil; // doc consumed - remove from ast.Decl node
t.Doc = astComment(doc);
t.Type = typespec;
t.Factories = makeFuncDocs(old.factories);
t.Methods = makeFuncDocs(old.methods);
t.Decl = old.decl;
t.order = i;
d[i] = t;
i++;
} }
d = d[0 : i]; // some types may have been ignored
sort.Sort(sortTypeDoc(d)); sort.Sort(sortTypeDoc(d));
return d; return d;
} }
...@@ -453,8 +447,8 @@ func (doc *docReader) newDoc(pkgname, importpath, filepath string, filenames []s ...@@ -453,8 +447,8 @@ func (doc *docReader) newDoc(pkgname, importpath, filepath string, filenames []s
sort.SortStrings(filenames); sort.SortStrings(filenames);
p.Filenames = filenames; p.Filenames = filenames;
p.Doc = astComment(doc.doc); p.Doc = astComment(doc.doc);
p.Consts = makeValueDocs(doc.consts); p.Consts = makeValueDocs(doc.values, token.CONST);
p.Vars = makeValueDocs(doc.vars); p.Vars = makeValueDocs(doc.values, token.VAR);
p.Types = makeTypeDocs(doc.types); p.Types = makeTypeDocs(doc.types);
p.Funcs = makeFuncDocs(doc.funcs); p.Funcs = makeFuncDocs(doc.funcs);
p.Bugs = makeBugDocs(doc.bugs); p.Bugs = makeBugDocs(doc.bugs);
......
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