cmd/doc: handle embedded fields properly

The structure of the code meant that an embedded field was never
checked for export status. We need to check the name of the type,
which is either of type T or type *T, and T might be unexported.

Fixes #14356.

Change-Id: I56f468e9b8ae67e9ed7509ed0b91d860507baed2
Reviewed-on: https://go-review.googlesource.com/19701
Reviewed-by: Robert Griesemer <gri@golang.org>
This commit is contained in:
Rob Pike 2016-02-19 16:36:03 +11:00
parent a4b833940d
commit 108218453a
3 changed files with 32 additions and 5 deletions

View File

@ -221,6 +221,7 @@ var tests = []test{
`type ExportedType struct`, // Type definition.
`Comment before exported field.*\n.*ExportedField +int` +
`.*Comment on line with exported field.`,
`ExportedEmbeddedType.*Comment on line with exported embedded field.`,
`Has unexported fields`,
`func \(ExportedType\) ExportedMethod\(a int\) bool`,
`const ExportedTypedConstant ExportedType = iota`, // Must include associated constant.
@ -228,6 +229,7 @@ var tests = []test{
},
[]string{
`unexportedField`, // No unexported field.
`int.*embedded`, // No unexported embedded field.
`Comment about exported method.`, // No comment about exported method.
`unexportedMethod`, // No unexported method.
`unexportedTypedConstant`, // No unexported constant.
@ -241,7 +243,11 @@ var tests = []test{
`Comment about exported type`, // Include comment.
`type ExportedType struct`, // Type definition.
`Comment before exported field.*\n.*ExportedField +int`,
`unexportedField int.*Comment on line with unexported field.`,
`unexportedField.*int.*Comment on line with unexported field.`,
`ExportedEmbeddedType.*Comment on line with exported embedded field.`,
`\*ExportedEmbeddedType.*Comment on line with exported embedded \*field.`,
`unexportedType.*Comment on line with unexported embedded field.`,
`\*unexportedType.*Comment on line with unexported embedded \*field.`,
`func \(ExportedType\) unexportedMethod\(a int\) bool`,
`unexportedTypedConstant`,
},
@ -448,7 +454,6 @@ var trimTests = []trimTest{
{"", "", "", true},
{"/usr/gopher", "/usr/gopher", "/usr/gopher", true},
{"/usr/gopher/bar", "/usr/gopher", "bar", true},
{"/usr/gopher", "/usr/gopher", "/usr/gopher", true},
{"/usr/gopherflakes", "/usr/gopher", "/usr/gopherflakes", false},
{"/usr/gopher/bar", "/usr/zot", "/usr/gopher/bar", false},
}

View File

@ -487,9 +487,27 @@ func trimUnexportedFields(fields *ast.FieldList, what string) *ast.FieldList {
trimmed := false
list := make([]*ast.Field, 0, len(fields.List))
for _, field := range fields.List {
names := field.Names
if len(names) == 0 {
// Embedded type. Use the name of the type. It must be of type ident or *ident.
// Nothing else is allowed.
switch ident := field.Type.(type) {
case *ast.Ident:
names = []*ast.Ident{ident}
case *ast.StarExpr:
// Must have the form *identifier.
if ident, ok := ident.X.(*ast.Ident); ok {
names = []*ast.Ident{ident}
}
}
if names == nil {
// Can only happen if AST is incorrect. Safe to continue with a nil list.
log.Print("invalid program: unexpected type for embedded field")
}
}
// Trims if any is unexported. Good enough in practice.
ok := true
for _, name := range field.Names {
for _, name := range names {
if !isExported(name.Name) {
trimmed = true
ok = false

View File

@ -60,8 +60,12 @@ func internalFunc(a int) bool
// Comment about exported type.
type ExportedType struct {
// Comment before exported field.
ExportedField int // Comment on line with exported field.
unexportedField int // Comment on line with unexported field.
ExportedField int // Comment on line with exported field.
unexportedField int // Comment on line with unexported field.
ExportedEmbeddedType // Comment on line with exported embedded field.
*ExportedEmbeddedType // Comment on line with exported embedded *field.
unexportedType // Comment on line with unexported embedded field.
*unexportedType // Comment on line with unexported embedded *field.
}
// Comment about exported method.