1
0
mirror of https://github.com/golang/go synced 2024-11-21 23:24:41 -07:00

go/doc: don't ignore anonymous non-exported fields

- remove wrapper.go from testing package (not needed anymore)

Fixes #1000.

R=rsc, golang-dev, n13m3y3r
CC=golang-dev
https://golang.org/cl/5502074
This commit is contained in:
Robert Griesemer 2012-01-12 16:05:05 -08:00
parent 74cb963225
commit 9535b86a27
6 changed files with 95 additions and 155 deletions

View File

@ -56,7 +56,7 @@ type FuncDoc struct {
// included in the documentation. // included in the documentation.
func NewPackageDoc(pkg *ast.Package, importpath string, exportsOnly bool) *PackageDoc { func NewPackageDoc(pkg *ast.Package, importpath string, exportsOnly bool) *PackageDoc {
var r docReader var r docReader
r.init(pkg.Name) r.init(pkg.Name, exportsOnly)
filenames := make([]string, len(pkg.Files)) filenames := make([]string, len(pkg.Files))
i := 0 i := 0
for filename, f := range pkg.Files { for filename, f := range pkg.Files {

View File

@ -33,7 +33,7 @@ func baseName(x ast.Expr) *ast.Ident {
return nil return nil
} }
func (doc *docReader) filterFieldList(fields *ast.FieldList) (removedFields bool) { func (doc *docReader) filterFieldList(tinfo *typeInfo, fields *ast.FieldList) (removedFields bool) {
if fields == nil { if fields == nil {
return false return false
} }
@ -44,7 +44,18 @@ func (doc *docReader) filterFieldList(fields *ast.FieldList) (removedFields bool
if len(f.Names) == 0 { if len(f.Names) == 0 {
// anonymous field // anonymous field
name := baseName(f.Type) name := baseName(f.Type)
keepField = name != nil && name.IsExported() if name != nil && name.IsExported() {
// we keep the field - in this case doc.addDecl
// will take care of adding the embedded type
keepField = true
} else if tinfo != nil {
// we don't keep the field - add it as an embedded
// type so we won't loose its methods, if any
if embedded := doc.lookupTypeInfo(name.Name); embedded != nil {
_, ptr := f.Type.(*ast.StarExpr)
tinfo.addEmbeddedType(embedded, ptr)
}
}
} else { } else {
n := len(f.Names) n := len(f.Names)
f.Names = filterIdentList(f.Names) f.Names = filterIdentList(f.Names)
@ -54,7 +65,7 @@ func (doc *docReader) filterFieldList(fields *ast.FieldList) (removedFields bool
keepField = len(f.Names) > 0 keepField = len(f.Names) > 0
} }
if keepField { if keepField {
doc.filterType(f.Type) doc.filterType(nil, f.Type)
list[j] = f list[j] = f
j++ j++
} }
@ -72,23 +83,23 @@ func (doc *docReader) filterParamList(fields *ast.FieldList) bool {
} }
var b bool var b bool
for _, f := range fields.List { for _, f := range fields.List {
if doc.filterType(f.Type) { if doc.filterType(nil, f.Type) {
b = true b = true
} }
} }
return b return b
} }
func (doc *docReader) filterType(typ ast.Expr) bool { func (doc *docReader) filterType(tinfo *typeInfo, typ ast.Expr) bool {
switch t := typ.(type) { switch t := typ.(type) {
case *ast.Ident: case *ast.Ident:
return ast.IsExported(t.Name) return ast.IsExported(t.Name)
case *ast.ParenExpr: case *ast.ParenExpr:
return doc.filterType(t.X) return doc.filterType(nil, t.X)
case *ast.ArrayType: case *ast.ArrayType:
return doc.filterType(t.Elt) return doc.filterType(nil, t.Elt)
case *ast.StructType: case *ast.StructType:
if doc.filterFieldList(t.Fields) { if doc.filterFieldList(tinfo, t.Fields) {
t.Incomplete = true t.Incomplete = true
} }
return len(t.Fields.List) > 0 return len(t.Fields.List) > 0
@ -97,16 +108,16 @@ func (doc *docReader) filterType(typ ast.Expr) bool {
b2 := doc.filterParamList(t.Results) b2 := doc.filterParamList(t.Results)
return b1 || b2 return b1 || b2
case *ast.InterfaceType: case *ast.InterfaceType:
if doc.filterFieldList(t.Methods) { if doc.filterFieldList(tinfo, t.Methods) {
t.Incomplete = true t.Incomplete = true
} }
return len(t.Methods.List) > 0 return len(t.Methods.List) > 0
case *ast.MapType: case *ast.MapType:
b1 := doc.filterType(t.Key) b1 := doc.filterType(nil, t.Key)
b2 := doc.filterType(t.Value) b2 := doc.filterType(nil, t.Value)
return b1 || b2 return b1 || b2
case *ast.ChanType: case *ast.ChanType:
return doc.filterType(t.Value) return doc.filterType(nil, t.Value)
} }
return false return false
} }
@ -116,12 +127,12 @@ func (doc *docReader) filterSpec(spec ast.Spec) bool {
case *ast.ValueSpec: case *ast.ValueSpec:
s.Names = filterIdentList(s.Names) s.Names = filterIdentList(s.Names)
if len(s.Names) > 0 { if len(s.Names) > 0 {
doc.filterType(s.Type) doc.filterType(nil, s.Type)
return true return true
} }
case *ast.TypeSpec: case *ast.TypeSpec:
if ast.IsExported(s.Name.Name) { if ast.IsExported(s.Name.Name) {
doc.filterType(s.Type) doc.filterType(doc.lookupTypeInfo(s.Name.Name), s.Type)
return true return true
} }
} }

View File

@ -23,6 +23,8 @@ type embeddedType struct {
} }
type typeInfo struct { type typeInfo struct {
name string // base type name
isStruct bool
// 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 // if the type declaration hasn't been seen yet, decl is nil
decl *ast.GenDecl decl *ast.GenDecl
@ -35,6 +37,10 @@ type typeInfo struct {
methods map[string]*ast.FuncDecl methods map[string]*ast.FuncDecl
} }
func (info *typeInfo) exported() bool {
return ast.IsExported(info.name)
}
func (info *typeInfo) addEmbeddedType(embedded *typeInfo, isPtr bool) { func (info *typeInfo) addEmbeddedType(embedded *typeInfo, isPtr bool) {
info.embedded = append(info.embedded, embeddedType{embedded, isPtr}) info.embedded = append(info.embedded, embeddedType{embedded, isPtr})
} }
@ -47,17 +53,19 @@ func (info *typeInfo) addEmbeddedType(embedded *typeInfo, isPtr bool) {
// printing the corresponding AST node). // printing the corresponding AST node).
// //
type docReader struct { type docReader struct {
doc *ast.CommentGroup // package documentation, if any doc *ast.CommentGroup // package documentation, if any
pkgName string pkgName string
values []*ast.GenDecl // consts and vars exportsOnly bool
types map[string]*typeInfo values []*ast.GenDecl // consts and vars
embedded map[string]*typeInfo // embedded types, possibly not exported types map[string]*typeInfo
funcs map[string]*ast.FuncDecl embedded map[string]*typeInfo // embedded types, possibly not exported
bugs []*ast.CommentGroup funcs map[string]*ast.FuncDecl
bugs []*ast.CommentGroup
} }
func (doc *docReader) init(pkgName string) { func (doc *docReader) init(pkgName string, exportsOnly bool) {
doc.pkgName = pkgName doc.pkgName = pkgName
doc.exportsOnly = exportsOnly
doc.types = make(map[string]*typeInfo) doc.types = make(map[string]*typeInfo)
doc.embedded = make(map[string]*typeInfo) doc.embedded = make(map[string]*typeInfo)
doc.funcs = make(map[string]*ast.FuncDecl) doc.funcs = make(map[string]*ast.FuncDecl)
@ -86,6 +94,7 @@ func (doc *docReader) lookupTypeInfo(name string) *typeInfo {
} }
// type wasn't found - add one without declaration // type wasn't found - add one without declaration
info := &typeInfo{ info := &typeInfo{
name: name,
factories: make(map[string]*ast.FuncDecl), factories: make(map[string]*ast.FuncDecl),
methods: make(map[string]*ast.FuncDecl), methods: make(map[string]*ast.FuncDecl),
} }
@ -182,9 +191,23 @@ func (doc *docReader) addFunc(fun *ast.FuncDecl) {
// determine if it should be associated with a type // determine if it should be associated with a type
if fun.Recv != nil { if fun.Recv != nil {
// method // method
typ := doc.lookupTypeInfo(baseTypeName(fun.Recv.List[0].Type, false)) recvTypeName := baseTypeName(fun.Recv.List[0].Type, true /* exported or not */ )
var typ *typeInfo
if ast.IsExported(recvTypeName) {
// exported recv type: if not found, add it to doc.types
typ = doc.lookupTypeInfo(recvTypeName)
} else {
// unexported recv type: if not found, do not add it
// (unexported embedded types are added before this
// phase, so if the type doesn't exist yet, we don't
// care about this method)
typ = doc.types[recvTypeName]
}
if typ != nil { if typ != nil {
// exported receiver type // exported receiver type
// associate method with the type
// (if the type is not exported, it may be embedded
// somewhere so we need to collect the method anyway)
setFunc(typ.methods, fun) setFunc(typ.methods, fun)
} }
// otherwise don't show the method // otherwise don't show the method
@ -256,6 +279,7 @@ func (doc *docReader) addDecl(decl ast.Decl) {
switch typ := spec.(*ast.TypeSpec).Type.(type) { switch typ := spec.(*ast.TypeSpec).Type.(type) {
case *ast.StructType: case *ast.StructType:
fields = typ.Fields fields = typ.Fields
info.isStruct = true
case *ast.InterfaceType: case *ast.InterfaceType:
fields = typ.Methods fields = typ.Methods
} }
@ -439,21 +463,25 @@ func (doc *docReader) makeTypeDocs(m map[string]*typeInfo) []*TypeDoc {
list := make([]*TypeDoc, len(m)) list := make([]*TypeDoc, len(m))
i := 0 i := 0
for _, old := range m { for _, old := range m {
// all typeInfos should have a declaration associated with // old typeInfos may not have a declaration associated with them
// them after processing an entire package - be conservative // if they are not exported but embedded, or because the package
// and check // is incomplete.
if decl := old.decl; decl != nil { if decl := old.decl; decl != nil || !old.exported() {
typespec := decl.Specs[0].(*ast.TypeSpec) // process the type even if not exported so that we have
// its methods in case they are embedded somewhere
t := new(TypeDoc) t := new(TypeDoc)
doc := typespec.Doc if decl != nil {
typespec.Doc = nil // doc consumed - remove from ast.TypeSpec node typespec := decl.Specs[0].(*ast.TypeSpec)
if doc == nil { doc := typespec.Doc
// no doc associated with the spec, use the declaration doc, if any typespec.Doc = nil // doc consumed - remove from ast.TypeSpec node
doc = decl.Doc 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 = doc.Text()
t.Type = typespec
} }
decl.Doc = nil // doc consumed - remove from ast.Decl node
t.Doc = doc.Text()
t.Type = typespec
t.Consts = makeValueDocs(old.values, token.CONST) t.Consts = makeValueDocs(old.values, token.CONST)
t.Vars = makeValueDocs(old.values, token.VAR) t.Vars = makeValueDocs(old.values, token.VAR)
t.Factories = makeFuncDocs(old.factories) t.Factories = makeFuncDocs(old.factories)
@ -466,8 +494,12 @@ func (doc *docReader) makeTypeDocs(m map[string]*typeInfo) []*TypeDoc {
t.Decl = old.decl t.Decl = old.decl
t.order = i t.order = i
old.forward = t // old has been processed old.forward = t // old has been processed
list[i] = t // only add the type to the final type list if it
i++ // is exported or if we want to see all types
if old.exported() || !doc.exportsOnly {
list[i] = t
i++
}
} else { } else {
// no corresponding type declaration found - move any associated // no corresponding type declaration found - move any associated
// values, factory functions, and methods back to the top-level // values, factory functions, and methods back to the top-level
@ -497,11 +529,10 @@ func (doc *docReader) makeTypeDocs(m map[string]*typeInfo) []*TypeDoc {
// old has been processed into t; collect embedded // old has been processed into t; collect embedded
// methods for t from the list of processed embedded // methods for t from the list of processed embedded
// types in old (and thus for which the methods are known) // types in old (and thus for which the methods are known)
typ := t.Type if old.isStruct {
if _, ok := typ.Type.(*ast.StructType); ok {
// struct // struct
t.embedded = make(methodSet) t.embedded = make(methodSet)
collectEmbeddedMethods(t.embedded, old, typ.Name.Name) collectEmbeddedMethods(t.embedded, old, old.name, false)
} else { } else {
// interface // interface
// TODO(gri) fix this // TODO(gri) fix this
@ -541,13 +572,19 @@ func (doc *docReader) makeTypeDocs(m map[string]*typeInfo) []*TypeDoc {
// deeply nested embedded methods with conflicting names are // deeply nested embedded methods with conflicting names are
// excluded. // excluded.
// //
func collectEmbeddedMethods(mset methodSet, info *typeInfo, recvTypeName string) { func collectEmbeddedMethods(mset methodSet, info *typeInfo, recvTypeName string, embeddedIsPtr bool) {
for _, e := range info.embedded { for _, e := range info.embedded {
if e.typ.forward != nil { // == e was processed if e.typ.forward != nil { // == e was processed
// Once an embedded type was embedded as a pointer type
// all embedded types in those types are treated like
// pointer types for the purpose of the receiver type
// computation; i.e., embeddedIsPtr is sticky for this
// embedding hierarchy.
thisEmbeddedIsPtr := embeddedIsPtr || e.ptr
for _, m := range e.typ.forward.methods { for _, m := range e.typ.forward.methods {
mset.add(customizeRecv(m, e.ptr, recvTypeName)) mset.add(customizeRecv(m, thisEmbeddedIsPtr, recvTypeName))
} }
collectEmbeddedMethods(mset, e.typ, recvTypeName) collectEmbeddedMethods(mset, e.typ, recvTypeName, thisEmbeddedIsPtr)
} }
} }
} }
@ -558,12 +595,10 @@ func customizeRecv(m *FuncDoc, embeddedIsPtr bool, recvTypeName string) *FuncDoc
} }
// copy existing receiver field and set new type // 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] newField := *m.Decl.Recv.List[0]
_, origRecvIsPtr := newField.Type.(*ast.StarExpr) _, origRecvIsPtr := newField.Type.(*ast.StarExpr)
var typ ast.Expr = ast.NewIdent(recvTypeName) var typ ast.Expr = ast.NewIdent(recvTypeName)
if embeddedIsPtr || origRecvIsPtr { if !embeddedIsPtr && origRecvIsPtr {
typ = &ast.StarExpr{token.NoPos, typ} typ = &ast.StarExpr{token.NoPos, typ}
} }
newField.Type = typ newField.Type = typ

View File

@ -9,6 +9,5 @@ GOFILES=\
benchmark.go\ benchmark.go\
example.go\ example.go\
testing.go\ testing.go\
wrapper.go\
include ../../Make.pkg include ../../Make.pkg

View File

@ -90,7 +90,7 @@ func Short() bool {
// If addFileLine is true, it also prefixes the string with the file and line of the call site. // If addFileLine is true, it also prefixes the string with the file and line of the call site.
func decorate(s string, addFileLine bool) string { func decorate(s string, addFileLine bool) string {
if addFileLine { if addFileLine {
_, file, line, ok := runtime.Caller(4) // decorate + log + public function. _, file, line, ok := runtime.Caller(3) // decorate + log + public function.
if ok { if ok {
// Truncate file name at last file name separator. // Truncate file name at last file name separator.
if index := strings.LastIndex(file, "/"); index >= 0 { if index := strings.LastIndex(file, "/"); index >= 0 {

View File

@ -1,105 +0,0 @@
// 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.
// This file contains wrappers so t.Errorf etc. have documentation.
// TODO: delete when godoc shows exported methods for unexported embedded fields.
// TODO: need to change the argument to runtime.Caller in testing.go from 4 to 3 at that point.
package testing
// Fail marks the function as having failed but continues execution.
func (b *B) Fail() {
b.common.Fail()
}
// Failed returns whether the function has failed.
func (b *B) Failed() bool {
return b.common.Failed()
}
// FailNow marks the function as having failed and stops its execution.
// Execution will continue at the next Test.
func (b *B) FailNow() {
b.common.FailNow()
}
// Log formats its arguments using default formatting, analogous to Println(),
// and records the text in the error log.
func (b *B) Log(args ...interface{}) {
b.common.Log(args...)
}
// Logf formats its arguments according to the format, analogous to Printf(),
// and records the text in the error log.
func (b *B) Logf(format string, args ...interface{}) {
b.common.Logf(format, args...)
}
// Error is equivalent to Log() followed by Fail().
func (b *B) Error(args ...interface{}) {
b.common.Error(args...)
}
// Errorf is equivalent to Logf() followed by Fail().
func (b *B) Errorf(format string, args ...interface{}) {
b.common.Errorf(format, args...)
}
// Fatal is equivalent to Log() followed by FailNow().
func (b *B) Fatal(args ...interface{}) {
b.common.Fatal(args...)
}
// Fatalf is equivalent to Logf() followed by FailNow().
func (b *B) Fatalf(format string, args ...interface{}) {
b.common.Fatalf(format, args...)
}
// Fail marks the function as having failed but continues execution.
func (t *T) Fail() {
t.common.Fail()
}
// Failed returns whether the function has failed.
func (t *T) Failed() bool {
return t.common.Failed()
}
// FailNow marks the function as having failed and stops its execution.
// Execution will continue at the next Test.
func (t *T) FailNow() {
t.common.FailNow()
}
// Log formats its arguments using default formatting, analogous to Println(),
// and records the text in the error log.
func (t *T) Log(args ...interface{}) {
t.common.Log(args...)
}
// Logf formats its arguments according to the format, analogous to Printf(),
// and records the text in the error log.
func (t *T) Logf(format string, args ...interface{}) {
t.common.Logf(format, args...)
}
// Error is equivalent to Log() followed by Fail().
func (t *T) Error(args ...interface{}) {
t.common.Error(args...)
}
// Errorf is equivalent to Logf() followed by Fail().
func (t *T) Errorf(format string, args ...interface{}) {
t.common.Errorf(format, args...)
}
// Fatal is equivalent to Log() followed by FailNow().
func (t *T) Fatal(args ...interface{}) {
t.common.Fatal(args...)
}
// Fatalf is equivalent to Logf() followed by FailNow().
func (t *T) Fatalf(format string, args ...interface{}) {
t.common.Fatalf(format, args...)
}