Skip to content

Commit f575873

Browse files
committed
cmd/doc: handle embedded interfaces properly
Changes made: * Disallow star expression on interfaces as this is not possible. * Show an embedded "error" in an interface as public similar to how godoc does it. * Properly handle selector expressions in both structs and interfaces. This is possible since a type may refer to something defined in another package (e.g. io.Reader). Before: <<< $ go doc runtime.Error type Error interface { // RuntimeError is a no-op function but // serves to distinguish types that are run time // errors from ordinary errors: a type is a // run time error if it has a RuntimeError method. RuntimeError() // Has unexported methods. } $ go doc compress/flate Reader doc: invalid program: unexpected type for embedded field doc: invalid program: unexpected type for embedded field type Reader interface { io.Reader io.ByteReader } >>> After: <<< $ go doc runtime.Error type Error interface { error // RuntimeError is a no-op function but // serves to distinguish types that are run time // errors from ordinary errors: a type is a // run time error if it has a RuntimeError method. RuntimeError() } $ go doc compress/flate Reader type Reader interface { io.Reader io.ByteReader } >>> Fixes #16567 Change-Id: I272dede971eee9f43173966233eb8810e4a8c907 Reviewed-on: https://go-review.googlesource.com/25365 Reviewed-by: Rob Pike <[email protected]> Run-TryBot: Joe Tsai <[email protected]> TryBot-Result: Gobot Gobot <[email protected]>
1 parent 28ee179 commit f575873

File tree

3 files changed

+32
-4
lines changed

3 files changed

+32
-4
lines changed

src/cmd/doc/doc_test.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,13 +221,15 @@ var tests = []test{
221221
`func \(ExportedType\) ExportedMethod\(a int\) bool`,
222222
`const ExportedTypedConstant ExportedType = iota`, // Must include associated constant.
223223
`func ExportedTypeConstructor\(\) \*ExportedType`, // Must include constructor.
224+
`io.Reader.*Comment on line with embedded Reader.`,
224225
},
225226
[]string{
226227
`unexportedField`, // No unexported field.
227228
`int.*embedded`, // No unexported embedded field.
228229
`Comment about exported method.`, // No comment about exported method.
229230
`unexportedMethod`, // No unexported method.
230231
`unexportedTypedConstant`, // No unexported constant.
232+
`error`, // No embedded error.
231233
},
232234
},
233235
// Type -u with unexported fields.
@@ -243,6 +245,8 @@ var tests = []test{
243245
`\*ExportedEmbeddedType.*Comment on line with exported embedded \*field.`,
244246
`unexportedType.*Comment on line with unexported embedded field.`,
245247
`\*unexportedType.*Comment on line with unexported embedded \*field.`,
248+
`io.Reader.*Comment on line with embedded Reader.`,
249+
`error.*Comment on line with embedded error.`,
246250
`func \(ExportedType\) unexportedMethod\(a int\) bool`,
247251
`unexportedTypedConstant`,
248252
},
@@ -274,6 +278,8 @@ var tests = []test{
274278
`type ExportedInterface interface`, // Interface definition.
275279
`Comment before exported method.*\n.*ExportedMethod\(\)` +
276280
`.*Comment on line with exported method`,
281+
`io.Reader.*Comment on line with embedded Reader.`,
282+
`error.*Comment on line with embedded error.`,
277283
`Has unexported methods`,
278284
},
279285
[]string{
@@ -293,6 +299,8 @@ var tests = []test{
293299
`Comment before exported method.*\n.*ExportedMethod\(\)` +
294300
`.*Comment on line with exported method`,
295301
`unexportedMethod\(\).*Comment on line with unexported method.`,
302+
`io.Reader.*Comment on line with embedded Reader.`,
303+
`error.*Comment on line with embedded error.`,
296304
},
297305
[]string{
298306
`Has unexported methods`,

src/cmd/doc/pkg.go

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -494,14 +494,19 @@ func trimUnexportedElems(spec *ast.TypeSpec) {
494494
}
495495
switch typ := spec.Type.(type) {
496496
case *ast.StructType:
497-
typ.Fields = trimUnexportedFields(typ.Fields, "fields")
497+
typ.Fields = trimUnexportedFields(typ.Fields, false)
498498
case *ast.InterfaceType:
499-
typ.Methods = trimUnexportedFields(typ.Methods, "methods")
499+
typ.Methods = trimUnexportedFields(typ.Methods, true)
500500
}
501501
}
502502

503503
// trimUnexportedFields returns the field list trimmed of unexported fields.
504-
func trimUnexportedFields(fields *ast.FieldList, what string) *ast.FieldList {
504+
func trimUnexportedFields(fields *ast.FieldList, isInterface bool) *ast.FieldList {
505+
what := "methods"
506+
if !isInterface {
507+
what = "fields"
508+
}
509+
505510
trimmed := false
506511
list := make([]*ast.Field, 0, len(fields.List))
507512
for _, field := range fields.List {
@@ -511,12 +516,23 @@ func trimUnexportedFields(fields *ast.FieldList, what string) *ast.FieldList {
511516
// Nothing else is allowed.
512517
switch ident := field.Type.(type) {
513518
case *ast.Ident:
519+
if isInterface && ident.Name == "error" && ident.Obj == nil {
520+
// For documentation purposes, we consider the builtin error
521+
// type special when embedded in an interface, such that it
522+
// always gets shown publicly.
523+
list = append(list, field)
524+
continue
525+
}
514526
names = []*ast.Ident{ident}
515527
case *ast.StarExpr:
516528
// Must have the form *identifier.
517-
if ident, ok := ident.X.(*ast.Ident); ok {
529+
// This is only valid on embedded types in structs.
530+
if ident, ok := ident.X.(*ast.Ident); ok && !isInterface {
518531
names = []*ast.Ident{ident}
519532
}
533+
case *ast.SelectorExpr:
534+
// An embedded type may refer to a type in another package.
535+
names = []*ast.Ident{ident.Sel}
520536
}
521537
if names == nil {
522538
// Can only happen if AST is incorrect. Safe to continue with a nil list.

src/cmd/doc/testdata/pkg.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,8 @@ type ExportedType struct {
6666
*ExportedEmbeddedType // Comment on line with exported embedded *field.
6767
unexportedType // Comment on line with unexported embedded field.
6868
*unexportedType // Comment on line with unexported embedded *field.
69+
io.Reader // Comment on line with embedded Reader.
70+
error // Comment on line with embedded error.
6971
}
7072

7173
// Comment about exported method.
@@ -96,6 +98,8 @@ type ExportedInterface interface {
9698
// Comment before exported method.
9799
ExportedMethod() // Comment on line with exported method.
98100
unexportedMethod() // Comment on line with unexported method.
101+
io.Reader // Comment on line with embedded Reader.
102+
error // Comment on line with embedded error.
99103
}
100104

101105
// Comment about unexported type.

0 commit comments

Comments
 (0)