cmd/doc: implement "go doc struct.field"

By analogy with the handling of methods on types, show the documentation
for a single field of a struct.

	% go doc ast.structtype.fields
	struct StructType {
	    Fields *FieldList  // list of field declarations
	}
	%

Fixes #19169.

Change-Id: I002f992e4aa64bee667e2e4bccc7082486149842
Reviewed-on: https://go-review.googlesource.com/38438
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
This commit is contained in:
Rob Pike 2017-03-21 20:27:47 -07:00
parent 19040ac871
commit 2c47c3e22e
5 changed files with 112 additions and 29 deletions

View file

@ -790,7 +790,6 @@ func (pkg *Package) printMethodDoc(symbol, method string) bool {
inter, ok := spec.Type.(*ast.InterfaceType)
if !ok {
// Not an interface type.
// TODO? Maybe handle struct fields here.
continue
}
for _, iMethod := range inter.Methods.List {
@ -821,12 +820,68 @@ func (pkg *Package) printMethodDoc(symbol, method string) bool {
return found
}
// printFieldDoc prints the docs for matches of symbol.fieldName.
// It reports whether it found any field.
// Both symbol and fieldName must be non-empty or it returns false.
func (pkg *Package) printFieldDoc(symbol, fieldName string) bool {
if symbol == "" || fieldName == "" {
return false
}
defer pkg.flush()
types := pkg.findTypes(symbol)
if types == nil {
pkg.Fatalf("symbol %s is not a type in package %s installed in %q", symbol, pkg.name, pkg.build.ImportPath)
}
found := false
for _, typ := range types {
// Type must be a struct.
spec := pkg.findTypeSpec(typ.Decl, typ.Name)
structType, ok := spec.Type.(*ast.StructType)
if !ok {
// Not a struct type.
continue
}
for _, field := range structType.Fields.List {
// TODO: Anonymous fields.
for _, name := range field.Names {
if match(fieldName, name.Name) {
if !found {
pkg.Printf("struct %s {\n", typ.Name)
}
if field.Doc != nil {
for _, comment := range field.Doc.List {
doc.ToText(&pkg.buf, comment.Text, indent, indent, indentedWidth)
}
}
s := pkg.oneLineNode(field.Type)
lineComment := ""
if field.Comment != nil {
lineComment = fmt.Sprintf(" %s", field.Comment.List[0].Text)
}
pkg.Printf("%s%s %s%s\n", indent, name, s, lineComment)
found = true
}
}
}
}
if found {
pkg.Printf("}\n")
}
return found
}
// methodDoc prints the docs for matches of symbol.method.
func (pkg *Package) methodDoc(symbol, method string) bool {
defer pkg.flush()
return pkg.printMethodDoc(symbol, method)
}
// fieldDoc prints the docs for matches of symbol.field.
func (pkg *Package) fieldDoc(symbol, field string) bool {
defer pkg.flush()
return pkg.printFieldDoc(symbol, field)
}
// match reports whether the user's symbol matches the program's.
// A lower-case character in the user's string matches either case in the program's.
// The program string must be exported.