Commit 2c47c3e2 authored by Rob Pike's avatar Rob Pike

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/38438Reviewed-by: 's avatarBrad Fitzpatrick <bradfitz@golang.org>
Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
parent 19040ac8
...@@ -390,6 +390,29 @@ var tests = []test{ ...@@ -390,6 +390,29 @@ var tests = []test{
nil, nil,
}, },
// Field.
{
"field",
[]string{p, `ExportedType.ExportedField`},
[]string{
`ExportedField int`,
`Comment before exported field.`,
`Comment on line with exported field.`,
},
nil,
},
// Field with -u.
{
"method with -u",
[]string{"-u", p, `ExportedType.unexportedField`},
[]string{
`unexportedField int`,
`Comment on line with unexported field.`,
},
nil,
},
// Case matching off. // Case matching off.
{ {
"case matching off", "case matching off",
......
...@@ -10,18 +10,18 @@ ...@@ -10,18 +10,18 @@
// //
// One argument: // One argument:
// go doc <pkg> // go doc <pkg>
// go doc <sym>[.<method>] // go doc <sym>[.<methodOrField>]
// go doc [<pkg>.]<sym>[.<method>] // go doc [<pkg>.]<sym>[.<methodOrField>]
// go doc [<pkg>.][<sym>.]<method> // go doc [<pkg>.][<sym>.]<methodOrField>
// The first item in this list that succeeds is the one whose documentation // The first item in this list that succeeds is the one whose documentation
// is printed. If there is a symbol but no package, the package in the current // is printed. If there is a symbol but no package, the package in the current
// directory is chosen. However, if the argument begins with a capital // directory is chosen. However, if the argument begins with a capital
// letter it is always assumed to be a symbol in the current directory. // letter it is always assumed to be a symbol in the current directory.
// //
// Two arguments: // Two arguments:
// go doc <pkg> <sym>[.<method>] // go doc <pkg> <sym>[.<methodOrField>]
// //
// Show the documentation for the package, symbol, and method. The // Show the documentation for the package, symbol, and method or field. The
// first argument must be a full package path. This is similar to the // first argument must be a full package path. This is similar to the
// command-line usage for the godoc command. // command-line usage for the godoc command.
// //
...@@ -129,6 +129,9 @@ func do(writer io.Writer, flagSet *flag.FlagSet, args []string) (err error) { ...@@ -129,6 +129,9 @@ func do(writer io.Writer, flagSet *flag.FlagSet, args []string) (err error) {
if pkg.methodDoc(symbol, method) { if pkg.methodDoc(symbol, method) {
return return
} }
if pkg.fieldDoc(symbol, method) {
return
}
} }
} }
} }
...@@ -149,7 +152,7 @@ func failMessage(paths []string, symbol, method string) error { ...@@ -149,7 +152,7 @@ func failMessage(paths []string, symbol, method string) error {
if method == "" { if method == "" {
return fmt.Errorf("no symbol %s in package%s", symbol, &b) return fmt.Errorf("no symbol %s in package%s", symbol, &b)
} }
return fmt.Errorf("no method %s.%s in package%s", symbol, method, &b) return fmt.Errorf("no method or field %s.%s in package%s", symbol, method, &b)
} }
// parseArgs analyzes the arguments (if any) and returns the package // parseArgs analyzes the arguments (if any) and returns the package
......
...@@ -790,7 +790,6 @@ func (pkg *Package) printMethodDoc(symbol, method string) bool { ...@@ -790,7 +790,6 @@ func (pkg *Package) printMethodDoc(symbol, method string) bool {
inter, ok := spec.Type.(*ast.InterfaceType) inter, ok := spec.Type.(*ast.InterfaceType)
if !ok { if !ok {
// Not an interface type. // Not an interface type.
// TODO? Maybe handle struct fields here.
continue continue
} }
for _, iMethod := range inter.Methods.List { for _, iMethod := range inter.Methods.List {
...@@ -821,12 +820,68 @@ func (pkg *Package) printMethodDoc(symbol, method string) bool { ...@@ -821,12 +820,68 @@ func (pkg *Package) printMethodDoc(symbol, method string) bool {
return found 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. // methodDoc prints the docs for matches of symbol.method.
func (pkg *Package) methodDoc(symbol, method string) bool { func (pkg *Package) methodDoc(symbol, method string) bool {
defer pkg.flush() defer pkg.flush()
return pkg.printMethodDoc(symbol, method) 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. // 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. // A lower-case character in the user's string matches either case in the program's.
// The program string must be exported. // The program string must be exported.
......
...@@ -209,12 +209,13 @@ ...@@ -209,12 +209,13 @@
// //
// Usage: // Usage:
// //
// go doc [-u] [-c] [package|[package.]symbol[.method]] // go doc [-u] [-c] [package|[package.]symbol[.methodOrField]]
// //
// Doc prints the documentation comments associated with the item identified by its // Doc prints the documentation comments associated with the item identified by its
// arguments (a package, const, func, type, var, or method) followed by a one-line // arguments (a package, const, func, type, var, method, or struct field)
// summary of each of the first-level items "under" that item (package-level // followed by a one-line summary of each of the first-level items "under"
// declarations for a package, methods for a type, etc.). // that item (package-level declarations for a package, methods for a type,
// etc.).
// //
// Doc accepts zero, one, or two arguments. // Doc accepts zero, one, or two arguments.
// //
...@@ -232,9 +233,9 @@ ...@@ -232,9 +233,9 @@
// which is schematically one of these: // which is schematically one of these:
// //
// go doc <pkg> // go doc <pkg>
// go doc <sym>[.<method>] // go doc <sym>[.<methodOrField>]
// go doc [<pkg>.]<sym>[.<method>] // go doc [<pkg>.]<sym>[.<methodOrField>]
// go doc [<pkg>.][<sym>.]<method> // go doc [<pkg>.][<sym>.]<methodOrField>
// //
// The first item in this list matched by the argument is the one whose documentation // The first item in this list matched by the argument is the one whose documentation
// is printed. (See the examples below.) However, if the argument starts with a capital // is printed. (See the examples below.) However, if the argument starts with a capital
...@@ -254,10 +255,10 @@ ...@@ -254,10 +255,10 @@
// elements like . and ... are not implemented by go doc. // elements like . and ... are not implemented by go doc.
// //
// When run with two arguments, the first must be a full package path (not just a // When run with two arguments, the first must be a full package path (not just a
// suffix), and the second is a symbol or symbol and method; this is similar to the // suffix), and the second is a symbol, or symbol with method or struct field.
// syntax accepted by godoc: // This is similar to the syntax accepted by godoc:
// //
// go doc <pkg> <sym>[.<method>] // go doc <pkg> <sym>[.<methodOrField>]
// //
// In all forms, when matching symbols, lower-case letters in the argument match // In all forms, when matching symbols, lower-case letters in the argument match
// either case but upper-case letters match exactly. This means that there may be // either case but upper-case letters match exactly. This means that there may be
...@@ -308,7 +309,7 @@ ...@@ -308,7 +309,7 @@
// when showing the package's top-level documentation. // when showing the package's top-level documentation.
// -u // -u
// Show documentation for unexported as well as exported // Show documentation for unexported as well as exported
// symbols and methods. // symbols, methods, and fields.
// //
// //
// Print Go environment information // Print Go environment information
......
...@@ -12,14 +12,15 @@ import ( ...@@ -12,14 +12,15 @@ import (
var CmdDoc = &base.Command{ var CmdDoc = &base.Command{
Run: runDoc, Run: runDoc,
UsageLine: "doc [-u] [-c] [package|[package.]symbol[.method]]", UsageLine: "doc [-u] [-c] [package|[package.]symbol[.methodOrField]]",
CustomFlags: true, CustomFlags: true,
Short: "show documentation for package or symbol", Short: "show documentation for package or symbol",
Long: ` Long: `
Doc prints the documentation comments associated with the item identified by its Doc prints the documentation comments associated with the item identified by its
arguments (a package, const, func, type, var, or method) followed by a one-line arguments (a package, const, func, type, var, method, or struct field)
summary of each of the first-level items "under" that item (package-level followed by a one-line summary of each of the first-level items "under"
declarations for a package, methods for a type, etc.). that item (package-level declarations for a package, methods for a type,
etc.).
Doc accepts zero, one, or two arguments. Doc accepts zero, one, or two arguments.
...@@ -37,9 +38,9 @@ on what is installed in GOROOT and GOPATH, as well as the form of the argument, ...@@ -37,9 +38,9 @@ on what is installed in GOROOT and GOPATH, as well as the form of the argument,
which is schematically one of these: which is schematically one of these:
go doc <pkg> go doc <pkg>
go doc <sym>[.<method>] go doc <sym>[.<methodOrField>]
go doc [<pkg>.]<sym>[.<method>] go doc [<pkg>.]<sym>[.<methodOrField>]
go doc [<pkg>.][<sym>.]<method> go doc [<pkg>.][<sym>.]<methodOrField>
The first item in this list matched by the argument is the one whose documentation The first item in this list matched by the argument is the one whose documentation
is printed. (See the examples below.) However, if the argument starts with a capital is printed. (See the examples below.) However, if the argument starts with a capital
...@@ -59,10 +60,10 @@ path. The go tool's usual package mechanism does not apply: package path ...@@ -59,10 +60,10 @@ path. The go tool's usual package mechanism does not apply: package path
elements like . and ... are not implemented by go doc. elements like . and ... are not implemented by go doc.
When run with two arguments, the first must be a full package path (not just a When run with two arguments, the first must be a full package path (not just a
suffix), and the second is a symbol or symbol and method; this is similar to the suffix), and the second is a symbol, or symbol with method or struct field.
syntax accepted by godoc: This is similar to the syntax accepted by godoc:
go doc <pkg> <sym>[.<method>] go doc <pkg> <sym>[.<methodOrField>]
In all forms, when matching symbols, lower-case letters in the argument match In all forms, when matching symbols, lower-case letters in the argument match
either case but upper-case letters match exactly. This means that there may be either case but upper-case letters match exactly. This means that there may be
...@@ -113,7 +114,7 @@ Flags: ...@@ -113,7 +114,7 @@ Flags:
when showing the package's top-level documentation. when showing the package's top-level documentation.
-u -u
Show documentation for unexported as well as exported Show documentation for unexported as well as exported
symbols and methods. symbols, methods, and fields.
`, `,
} }
......
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