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 {
return false
}
if len(f.Names) == 0 {
// anonymous field
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 {
n := len(f.Names)
f.Names = filterIdentList(f.Names)
keepField = len(f.Names) > 0
}
if keepField {
- doc.filterType(f.Type)
+ doc.filterType(nil, f.Type)
list[j] = f
j++
}
}
var b bool
for _, f := range fields.List {
- if doc.filterType(f.Type) {
+ if doc.filterType(nil, f.Type) {
b = true
}
}
return b
}
-func (doc *docReader) filterType(typ ast.Expr) bool {
+func (doc *docReader) filterType(tinfo *typeInfo, typ ast.Expr) bool {
switch t := typ.(type) {
case *ast.Ident:
return ast.IsExported(t.Name)
case *ast.ParenExpr:
- return doc.filterType(t.X)
+ return doc.filterType(nil, t.X)
case *ast.ArrayType:
- return doc.filterType(t.Elt)
+ return doc.filterType(nil, t.Elt)
case *ast.StructType:
- if doc.filterFieldList(t.Fields) {
+ if doc.filterFieldList(tinfo, t.Fields) {
t.Incomplete = true
}
return len(t.Fields.List) > 0
b2 := doc.filterParamList(t.Results)
return b1 || b2
case *ast.InterfaceType:
- if doc.filterFieldList(t.Methods) {
+ if doc.filterFieldList(tinfo, t.Methods) {
t.Incomplete = true
}
return len(t.Methods.List) > 0
case *ast.MapType:
- b1 := doc.filterType(t.Key)
- b2 := doc.filterType(t.Value)
+ b1 := doc.filterType(nil, t.Key)
+ b2 := doc.filterType(nil, t.Value)
return b1 || b2
case *ast.ChanType:
- return doc.filterType(t.Value)
+ return doc.filterType(nil, t.Value)
}
return false
}
case *ast.ValueSpec:
s.Names = filterIdentList(s.Names)
if len(s.Names) > 0 {
- doc.filterType(s.Type)
+ doc.filterType(nil, s.Type)
return true
}
case *ast.TypeSpec:
if ast.IsExported(s.Name.Name) {
- doc.filterType(s.Type)
+ doc.filterType(doc.lookupTypeInfo(s.Name.Name), s.Type)
return true
}
}
}
type typeInfo struct {
+ name string // base type name
+ isStruct bool
// 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
methods map[string]*ast.FuncDecl
}
+func (info *typeInfo) exported() bool {
+ return ast.IsExported(info.name)
+}
+
func (info *typeInfo) addEmbeddedType(embedded *typeInfo, isPtr bool) {
info.embedded = append(info.embedded, embeddedType{embedded, isPtr})
}
// printing the corresponding AST node).
//
type docReader struct {
- doc *ast.CommentGroup // package documentation, if any
- pkgName string
- values []*ast.GenDecl // consts and vars
- types map[string]*typeInfo
- embedded map[string]*typeInfo // embedded types, possibly not exported
- funcs map[string]*ast.FuncDecl
- bugs []*ast.CommentGroup
+ doc *ast.CommentGroup // package documentation, if any
+ pkgName string
+ exportsOnly bool
+ values []*ast.GenDecl // consts and vars
+ types map[string]*typeInfo
+ embedded map[string]*typeInfo // embedded types, possibly not exported
+ 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.exportsOnly = exportsOnly
doc.types = make(map[string]*typeInfo)
doc.embedded = make(map[string]*typeInfo)
doc.funcs = make(map[string]*ast.FuncDecl)
}
// type wasn't found - add one without declaration
info := &typeInfo{
+ name: name,
factories: make(map[string]*ast.FuncDecl),
methods: make(map[string]*ast.FuncDecl),
}
// determine if it should be associated with a type
if fun.Recv != nil {
// 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 {
// 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)
}
// otherwise don't show the method
switch typ := spec.(*ast.TypeSpec).Type.(type) {
case *ast.StructType:
fields = typ.Fields
+ info.isStruct = true
case *ast.InterfaceType:
fields = typ.Methods
}
list := make([]*TypeDoc, len(m))
i := 0
for _, old := range m {
- // all typeInfos should have a declaration associated with
- // them after processing an entire package - be conservative
- // and check
- if decl := old.decl; decl != nil {
- typespec := decl.Specs[0].(*ast.TypeSpec)
+ // old typeInfos may not have a declaration associated with them
+ // if they are not exported but embedded, or because the package
+ // is incomplete.
+ if decl := old.decl; decl != nil || !old.exported() {
+ // process the type even if not exported so that we have
+ // its methods in case they are embedded somewhere
t := new(TypeDoc)
- 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
+ if decl != nil {
+ typespec := decl.Specs[0].(*ast.TypeSpec)
+ 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 = 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.Vars = makeValueDocs(old.values, token.VAR)
t.Factories = makeFuncDocs(old.factories)
t.Decl = old.decl
t.order = i
old.forward = t // old has been processed
- list[i] = t
- i++
+ // only add the type to the final type list if it
+ // is exported or if we want to see all types
+ if old.exported() || !doc.exportsOnly {
+ list[i] = t
+ i++
+ }
} else {
// no corresponding type declaration found - move any associated
// values, factory functions, and methods back to the top-level
// 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 {
+ if old.isStruct {
// struct
t.embedded = make(methodSet)
- collectEmbeddedMethods(t.embedded, old, typ.Name.Name)
+ collectEmbeddedMethods(t.embedded, old, old.name, false)
} else {
// interface
// TODO(gri) fix this
// deeply nested embedded methods with conflicting names are
// excluded.
//
-func collectEmbeddedMethods(mset methodSet, info *typeInfo, recvTypeName string) {
+func collectEmbeddedMethods(mset methodSet, info *typeInfo, recvTypeName string, embeddedIsPtr bool) {
for _, e := range info.embedded {
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 {
- mset.add(customizeRecv(m, e.ptr, recvTypeName))
+ mset.add(customizeRecv(m, thisEmbeddedIsPtr, recvTypeName))
}
- collectEmbeddedMethods(mset, e.typ, recvTypeName)
+ collectEmbeddedMethods(mset, e.typ, recvTypeName, thisEmbeddedIsPtr)
}
}
}
}
// 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 {
+ if !embeddedIsPtr && origRecvIsPtr {
typ = &ast.StarExpr{token.NoPos, typ}
}
newField.Type = typ
+++ /dev/null
-// 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...)
-}