Commit 10821845 authored by Rob Pike's avatar Rob Pike

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/19701Reviewed-by: default avatarRobert Griesemer <gri@golang.org>
parent a4b83394
...@@ -221,6 +221,7 @@ var tests = []test{ ...@@ -221,6 +221,7 @@ var tests = []test{
`type ExportedType struct`, // Type definition. `type ExportedType struct`, // Type definition.
`Comment before exported field.*\n.*ExportedField +int` + `Comment before exported field.*\n.*ExportedField +int` +
`.*Comment on line with exported field.`, `.*Comment on line with exported field.`,
`ExportedEmbeddedType.*Comment on line with exported embedded field.`,
`Has unexported fields`, `Has unexported fields`,
`func \(ExportedType\) ExportedMethod\(a int\) bool`, `func \(ExportedType\) ExportedMethod\(a int\) bool`,
`const ExportedTypedConstant ExportedType = iota`, // Must include associated constant. `const ExportedTypedConstant ExportedType = iota`, // Must include associated constant.
...@@ -228,6 +229,7 @@ var tests = []test{ ...@@ -228,6 +229,7 @@ var tests = []test{
}, },
[]string{ []string{
`unexportedField`, // No unexported field. `unexportedField`, // No unexported field.
`int.*embedded`, // No unexported embedded field.
`Comment about exported method.`, // No comment about exported method. `Comment about exported method.`, // No comment about exported method.
`unexportedMethod`, // No unexported method. `unexportedMethod`, // No unexported method.
`unexportedTypedConstant`, // No unexported constant. `unexportedTypedConstant`, // No unexported constant.
...@@ -241,7 +243,11 @@ var tests = []test{ ...@@ -241,7 +243,11 @@ var tests = []test{
`Comment about exported type`, // Include comment. `Comment about exported type`, // Include comment.
`type ExportedType struct`, // Type definition. `type ExportedType struct`, // Type definition.
`Comment before exported field.*\n.*ExportedField +int`, `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`, `func \(ExportedType\) unexportedMethod\(a int\) bool`,
`unexportedTypedConstant`, `unexportedTypedConstant`,
}, },
...@@ -448,7 +454,6 @@ var trimTests = []trimTest{ ...@@ -448,7 +454,6 @@ var trimTests = []trimTest{
{"", "", "", true}, {"", "", "", true},
{"/usr/gopher", "/usr/gopher", "/usr/gopher", true}, {"/usr/gopher", "/usr/gopher", "/usr/gopher", true},
{"/usr/gopher/bar", "/usr/gopher", "bar", true}, {"/usr/gopher/bar", "/usr/gopher", "bar", true},
{"/usr/gopher", "/usr/gopher", "/usr/gopher", true},
{"/usr/gopherflakes", "/usr/gopher", "/usr/gopherflakes", false}, {"/usr/gopherflakes", "/usr/gopher", "/usr/gopherflakes", false},
{"/usr/gopher/bar", "/usr/zot", "/usr/gopher/bar", false}, {"/usr/gopher/bar", "/usr/zot", "/usr/gopher/bar", false},
} }
......
...@@ -487,9 +487,27 @@ func trimUnexportedFields(fields *ast.FieldList, what string) *ast.FieldList { ...@@ -487,9 +487,27 @@ func trimUnexportedFields(fields *ast.FieldList, what string) *ast.FieldList {
trimmed := false trimmed := false
list := make([]*ast.Field, 0, len(fields.List)) list := make([]*ast.Field, 0, len(fields.List))
for _, field := range 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. // Trims if any is unexported. Good enough in practice.
ok := true ok := true
for _, name := range field.Names { for _, name := range names {
if !isExported(name.Name) { if !isExported(name.Name) {
trimmed = true trimmed = true
ok = false ok = false
......
...@@ -60,8 +60,12 @@ func internalFunc(a int) bool ...@@ -60,8 +60,12 @@ func internalFunc(a int) bool
// Comment about exported type. // Comment about exported type.
type ExportedType struct { type ExportedType struct {
// Comment before exported field. // Comment before exported field.
ExportedField int // Comment on line with exported field. ExportedField int // Comment on line with exported field.
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.
} }
// Comment about exported method. // Comment about exported method.
......
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