diff options
author | Joe Tsai <joetsai@digital-static.net> | 2016-08-01 14:33:19 -0700 |
---|---|---|
committer | Joe Tsai <thebrokentoaster@gmail.com> | 2016-08-02 01:58:14 +0000 |
commit | f5758739a8f011c1d146a7736ab8f0d2834e1783 (patch) | |
tree | 63e9b2cfc3869db3c79d176795b416b908fd366e | |
parent | 28ee17965703c4ef81cc97e5088539fe3e8e541f (diff) | |
download | go-f5758739a8f011c1d146a7736ab8f0d2834e1783.tar.gz go-f5758739a8f011c1d146a7736ab8f0d2834e1783.zip |
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 <r@golang.org>
Run-TryBot: Joe Tsai <thebrokentoaster@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
-rw-r--r-- | src/cmd/doc/doc_test.go | 8 | ||||
-rw-r--r-- | src/cmd/doc/pkg.go | 24 | ||||
-rw-r--r-- | src/cmd/doc/testdata/pkg.go | 4 |
3 files changed, 32 insertions, 4 deletions
diff --git a/src/cmd/doc/doc_test.go b/src/cmd/doc/doc_test.go index 5cb1ec990e..bfb9099dd2 100644 --- a/src/cmd/doc/doc_test.go +++ b/src/cmd/doc/doc_test.go @@ -221,6 +221,7 @@ var tests = []test{ `func \(ExportedType\) ExportedMethod\(a int\) bool`, `const ExportedTypedConstant ExportedType = iota`, // Must include associated constant. `func ExportedTypeConstructor\(\) \*ExportedType`, // Must include constructor. + `io.Reader.*Comment on line with embedded Reader.`, }, []string{ `unexportedField`, // No unexported field. @@ -228,6 +229,7 @@ var tests = []test{ `Comment about exported method.`, // No comment about exported method. `unexportedMethod`, // No unexported method. `unexportedTypedConstant`, // No unexported constant. + `error`, // No embedded error. }, }, // Type -u with unexported fields. @@ -243,6 +245,8 @@ var tests = []test{ `\*ExportedEmbeddedType.*Comment on line with exported embedded \*field.`, `unexportedType.*Comment on line with unexported embedded field.`, `\*unexportedType.*Comment on line with unexported embedded \*field.`, + `io.Reader.*Comment on line with embedded Reader.`, + `error.*Comment on line with embedded error.`, `func \(ExportedType\) unexportedMethod\(a int\) bool`, `unexportedTypedConstant`, }, @@ -274,6 +278,8 @@ var tests = []test{ `type ExportedInterface interface`, // Interface definition. `Comment before exported method.*\n.*ExportedMethod\(\)` + `.*Comment on line with exported method`, + `io.Reader.*Comment on line with embedded Reader.`, + `error.*Comment on line with embedded error.`, `Has unexported methods`, }, []string{ @@ -293,6 +299,8 @@ var tests = []test{ `Comment before exported method.*\n.*ExportedMethod\(\)` + `.*Comment on line with exported method`, `unexportedMethod\(\).*Comment on line with unexported method.`, + `io.Reader.*Comment on line with embedded Reader.`, + `error.*Comment on line with embedded error.`, }, []string{ `Has unexported methods`, diff --git a/src/cmd/doc/pkg.go b/src/cmd/doc/pkg.go index efd681d514..eec9f1e803 100644 --- a/src/cmd/doc/pkg.go +++ b/src/cmd/doc/pkg.go @@ -494,14 +494,19 @@ func trimUnexportedElems(spec *ast.TypeSpec) { } switch typ := spec.Type.(type) { case *ast.StructType: - typ.Fields = trimUnexportedFields(typ.Fields, "fields") + typ.Fields = trimUnexportedFields(typ.Fields, false) case *ast.InterfaceType: - typ.Methods = trimUnexportedFields(typ.Methods, "methods") + typ.Methods = trimUnexportedFields(typ.Methods, true) } } // trimUnexportedFields returns the field list trimmed of unexported fields. -func trimUnexportedFields(fields *ast.FieldList, what string) *ast.FieldList { +func trimUnexportedFields(fields *ast.FieldList, isInterface bool) *ast.FieldList { + what := "methods" + if !isInterface { + what = "fields" + } + trimmed := false list := make([]*ast.Field, 0, len(fields.List)) for _, field := range fields.List { @@ -511,12 +516,23 @@ func trimUnexportedFields(fields *ast.FieldList, what string) *ast.FieldList { // Nothing else is allowed. switch ident := field.Type.(type) { case *ast.Ident: + if isInterface && ident.Name == "error" && ident.Obj == nil { + // For documentation purposes, we consider the builtin error + // type special when embedded in an interface, such that it + // always gets shown publicly. + list = append(list, field) + continue + } names = []*ast.Ident{ident} case *ast.StarExpr: // Must have the form *identifier. - if ident, ok := ident.X.(*ast.Ident); ok { + // This is only valid on embedded types in structs. + if ident, ok := ident.X.(*ast.Ident); ok && !isInterface { names = []*ast.Ident{ident} } + case *ast.SelectorExpr: + // An embedded type may refer to a type in another package. + names = []*ast.Ident{ident.Sel} } if names == nil { // Can only happen if AST is incorrect. Safe to continue with a nil list. diff --git a/src/cmd/doc/testdata/pkg.go b/src/cmd/doc/testdata/pkg.go index 5f79414b33..9c5cf8f557 100644 --- a/src/cmd/doc/testdata/pkg.go +++ b/src/cmd/doc/testdata/pkg.go @@ -66,6 +66,8 @@ type ExportedType struct { *ExportedEmbeddedType // Comment on line with exported embedded *field. unexportedType // Comment on line with unexported embedded field. *unexportedType // Comment on line with unexported embedded *field. + io.Reader // Comment on line with embedded Reader. + error // Comment on line with embedded error. } // Comment about exported method. @@ -96,6 +98,8 @@ type ExportedInterface interface { // Comment before exported method. ExportedMethod() // Comment on line with exported method. unexportedMethod() // Comment on line with unexported method. + io.Reader // Comment on line with embedded Reader. + error // Comment on line with embedded error. } // Comment about unexported type. |