]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/doc: add a -src flag to show original source
authorRob Pike <r@golang.org>
Wed, 10 Oct 2018 00:50:49 +0000 (11:50 +1100)
committerRob Pike <r@golang.org>
Wed, 10 Oct 2018 23:40:35 +0000 (23:40 +0000)
It's long-desired but was blocked by #26835. That is now fixed, so
it's easy. When -src is off, we behave as before. But with -src
set, initialize the go/doc package to preserve the original AST and
things flow very easily.

With -src, since you're seeing inside the package source anyway it
shows unexported fields and constants: you see the original source.
But you still need -u to ask about them.

Fixes #18807

Change-Id: I473e90323b4eff0735360274dc0d2d9dba16ff8b
Reviewed-on: https://go-review.googlesource.com/c/140959
Reviewed-by: Andrew Gerrand <adg@golang.org>
Run-TryBot: Andrew Gerrand <adg@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>

src/cmd/doc/doc_test.go
src/cmd/doc/main.go
src/cmd/doc/pkg.go
src/cmd/doc/testdata/pkg.go
src/cmd/go/alldocs.go
src/cmd/go/internal/doc/doc.go

index 6010f04b56081994bd93bb868a34c31772d33265..64b1fb596b44e9ed53dc528a5c7125dac39be2af 100644 (file)
@@ -127,24 +127,24 @@ var tests = []test{
                        `type T1 = T2`,                                                 // Type alias
                },
                []string{
-                       `const internalConstant = 2`,        // No internal constants.
-                       `var internalVariable = 2`,          // No internal variables.
-                       `func internalFunc(a int) bool`,     // No internal functions.
-                       `Comment about exported constant`,   // No comment for single constant.
-                       `Comment about exported variable`,   // No comment for single variable.
-                       `Comment about block of constants.`, // No comment for constant block.
-                       `Comment about block of variables.`, // No comment for variable block.
-                       `Comment before ConstOne`,           // No comment for first entry in constant block.
-                       `Comment before VarOne`,             // No comment for first entry in variable block.
-                       `ConstTwo = 2`,                      // No second entry in constant block.
-                       `VarTwo = 2`,                        // No second entry in variable block.
-                       `VarFive = 5`,                       // From block starting with unexported variable.
-                       `type unexportedType`,               // No unexported type.
-                       `unexportedTypedConstant`,           // No unexported typed constant.
-                       `\bField`,                           // No fields.
-                       `Method`,                            // No methods.
-                       `someArgument[5-8]`,                 // No truncated arguments.
-                       `type T1 T2`,                        // Type alias does not display as type declaration.
+                       `const internalConstant = 2`,       // No internal constants.
+                       `var internalVariable = 2`,         // No internal variables.
+                       `func internalFunc(a int) bool`,    // No internal functions.
+                       `Comment about exported constant`,  // No comment for single constant.
+                       `Comment about exported variable`,  // No comment for single variable.
+                       `Comment about block of constants`, // No comment for constant block.
+                       `Comment about block of variables`, // No comment for variable block.
+                       `Comment before ConstOne`,          // No comment for first entry in constant block.
+                       `Comment before VarOne`,            // No comment for first entry in variable block.
+                       `ConstTwo = 2`,                     // No second entry in constant block.
+                       `VarTwo = 2`,                       // No second entry in variable block.
+                       `VarFive = 5`,                      // From block starting with unexported variable.
+                       `type unexportedType`,              // No unexported type.
+                       `unexportedTypedConstant`,          // No unexported typed constant.
+                       `\bField`,                          // No fields.
+                       `Method`,                           // No methods.
+                       `someArgument[5-8]`,                // No truncated arguments.
+                       `type T1 T2`,                       // Type alias does not display as type declaration.
                },
        },
        // Package dump -u
@@ -207,6 +207,18 @@ var tests = []test{
                },
                nil,
        },
+       // Block of constants -src.
+       {
+               "block of constants with -src",
+               []string{"-src", p, `ConstTwo`},
+               []string{
+                       `Comment about block of constants`, // Top comment.
+                       `ConstOne.*=.*1`,                   // Each constant seen.
+                       `ConstTwo.*=.*2.*Comment on line with ConstTwo`,
+                       `constThree`, // Even unexported constants.
+               },
+               nil,
+       },
        // Block of constants with carryover type from unexported field.
        {
                "block of constants with carryover type",
@@ -295,6 +307,17 @@ var tests = []test{
                },
                nil,
        },
+       // Function with -src.
+       {
+               "function with -src",
+               []string{"-src", p, `ExportedFunc`},
+               []string{
+                       `Comment about exported function`, // Include comment.
+                       `func ExportedFunc\(a int\) bool`,
+                       `return true != false`, // Include body.
+               },
+               nil,
+       },
 
        // Type.
        {
@@ -304,21 +327,44 @@ var tests = []test{
                        `Comment about exported type`, // Include comment.
                        `type ExportedType struct`,    // Type definition.
                        `Comment before exported field.*\n.*ExportedField +int` +
-                               `.*Comment on line with exported field.`,
-                       `ExportedEmbeddedType.*Comment on line with exported embedded field.`,
+                               `.*Comment on line with exported field`,
+                       `ExportedEmbeddedType.*Comment on line with exported embedded field`,
                        `Has unexported fields`,
                        `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.`,
+                       `io.Reader.*Comment on line with embedded Reader`,
                },
                []string{
-                       `unexportedField`,                // No unexported field.
-                       `int.*embedded`,                  // No unexported embedded field.
-                       `Comment about exported method.`, // No comment about exported method.
-                       `unexportedMethod`,               // No unexported method.
-                       `unexportedTypedConstant`,        // No unexported constant.
-                       `error`,                          // No embedded error.
+                       `unexportedField`,               // No unexported field.
+                       `int.*embedded`,                 // No unexported embedded field.
+                       `Comment about exported method`, // No comment about exported method.
+                       `unexportedMethod`,              // No unexported method.
+                       `unexportedTypedConstant`,       // No unexported constant.
+                       `error`,                         // No embedded error.
+               },
+       },
+       // Type with -src. Will see unexported fields.
+       {
+               "type",
+               []string{"-src", p, `ExportedType`},
+               []string{
+                       `Comment about exported type`, // Include comment.
+                       `type ExportedType struct`,    // Type definition.
+                       `Comment before exported field.*\n.*ExportedField +int` +
+                               `.*Comment on line with exported field`,
+                       `ExportedEmbeddedType.*Comment on line with exported embedded field`,
+                       `unexportedType.*Comment on line with unexported embedded field`,
+                       `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{
+                       `int.*embedded`,                 // No unexported embedded field.
+                       `Comment about exported method`, // No comment about exported method.
+                       `unexportedMethod`,              // No unexported method.
+                       `unexportedTypedConstant`,       // No unexported constant.
                },
        },
        // Type T1 dump (alias).
@@ -341,14 +387,14 @@ var tests = []test{
                        `Comment about exported type`, // Include comment.
                        `type ExportedType struct`,    // Type definition.
                        `Comment before exported field.*\n.*ExportedField +int`,
-                       `unexportedField.*int.*Comment on line with unexported field.`,
-                       `ExportedEmbeddedType.*Comment on line with exported embedded field.`,
-                       `\*ExportedEmbeddedType.*Comment on line with exported embedded \*field.`,
-                       `\*qualified.ExportedEmbeddedType.*Comment on line with exported embedded \*selector.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.`,
+                       `unexportedField.*int.*Comment on line with unexported field`,
+                       `ExportedEmbeddedType.*Comment on line with exported embedded field`,
+                       `\*ExportedEmbeddedType.*Comment on line with exported embedded \*field`,
+                       `\*qualified.ExportedEmbeddedType.*Comment on line with exported embedded \*selector.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`,
                },
@@ -380,8 +426,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.`,
+                       `io.Reader.*Comment on line with embedded Reader`,
+                       `error.*Comment on line with embedded error`,
                        `Has unexported methods`,
                },
                []string{
@@ -400,9 +446,9 @@ var tests = []test{
                        `type ExportedInterface interface`, // Interface definition.
                        `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.`,
+                       `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`,
@@ -418,7 +464,7 @@ var tests = []test{
                                `.*Comment on line with exported method`,
                },
                []string{
-                       `Comment about exported interface.`,
+                       `Comment about exported interface`,
                },
        },
 
@@ -428,7 +474,7 @@ var tests = []test{
                []string{p, `ExportedType.ExportedMethod`},
                []string{
                        `func \(ExportedType\) ExportedMethod\(a int\) bool`,
-                       `Comment about exported method.`,
+                       `Comment about exported method`,
                },
                nil,
        },
@@ -438,7 +484,18 @@ var tests = []test{
                []string{"-u", p, `ExportedType.unexportedMethod`},
                []string{
                        `func \(ExportedType\) unexportedMethod\(a int\) bool`,
-                       `Comment about unexported method.`,
+                       `Comment about unexported method`,
+               },
+               nil,
+       },
+       // Method with -src.
+       {
+               "method with -src",
+               []string{"-src", p, `ExportedType.ExportedMethod`},
+               []string{
+                       `func \(ExportedType\) ExportedMethod\(a int\) bool`,
+                       `Comment about exported method`,
+                       `return true != true`,
                },
                nil,
        },
@@ -450,8 +507,8 @@ var tests = []test{
                []string{
                        `type ExportedType struct`,
                        `ExportedField int`,
-                       `Comment before exported field.`,
-                       `Comment on line with exported field.`,
+                       `Comment before exported field`,
+                       `Comment on line with exported field`,
                        `other fields elided`,
                },
                nil,
@@ -463,7 +520,7 @@ var tests = []test{
                []string{"-u", p, `ExportedType.unexportedField`},
                []string{
                        `unexportedField int`,
-                       `Comment on line with unexported field.`,
+                       `Comment on line with unexported field`,
                },
                nil,
        },
index 982c8e054acacdf66598015193d44c22819bd3e5..a3e09d3f87115fec84538a405790e1ed99dbdcaa 100644 (file)
@@ -28,6 +28,9 @@
 // For commands, unless the -cmd flag is present "go doc command"
 // shows only the package-level docs for the package.
 //
+// The -src flag causes doc to print the full source code for the symbol, such
+// as the body of a struct, function or method.
+//
 // For complete documentation, run "go help doc".
 package main
 
@@ -50,6 +53,7 @@ var (
        unexported bool // -u flag
        matchCase  bool // -c flag
        showCmd    bool // -cmd flag
+       showSrc    bool // -src flag
 )
 
 // usage is a replacement usage function for the flags package.
@@ -85,6 +89,7 @@ func do(writer io.Writer, flagSet *flag.FlagSet, args []string) (err error) {
        flagSet.BoolVar(&unexported, "u", false, "show unexported symbols as well as exported")
        flagSet.BoolVar(&matchCase, "c", false, "symbol matching honors case (paths not affected)")
        flagSet.BoolVar(&showCmd, "cmd", false, "show symbols with package docs even if package is a command")
+       flagSet.BoolVar(&showSrc, "src", false, "show source code for symbol")
        flagSet.Parse(args)
        var paths []string
        var symbol, method string
index 14e41b9106ce163f61565dd227914144663b225d..154fb7b45fcfcd2682f8715ab2afa9e41a71ec3f 100644 (file)
@@ -137,7 +137,11 @@ func parsePackage(writer io.Writer, pkg *build.Package, userPath string) *Packag
        // from finding the symbol. Work around this for now, but we
        // should fix it in go/doc.
        // A similar story applies to factory functions.
-       docPkg := doc.New(astPkg, pkg.ImportPath, doc.AllDecls)
+       mode := doc.AllDecls
+       if showSrc {
+               mode |= doc.PreserveAST // See comment for Package.emit.
+       }
+       docPkg := doc.New(astPkg, pkg.ImportPath, mode)
        for _, typ := range docPkg.Types {
                docPkg.Consts = append(docPkg.Consts, typ.Consts...)
                docPkg.Vars = append(docPkg.Vars, typ.Vars...)
@@ -177,14 +181,16 @@ func (pkg *Package) newlines(n int) {
        }
 }
 
-// emit prints the node.
+// emit prints the node. If showSrc is true, it ignores the provided comment,
+// assuming the comment is in the node itself. Otherwise, the go/doc package
+// clears the stuff we don't want to print anyway. It's a bit of a magic trick.
 func (pkg *Package) emit(comment string, node ast.Node) {
        if node != nil {
                err := format.Node(&pkg.buf, pkg.fs, node)
                if err != nil {
                        log.Fatal(err)
                }
-               if comment != "" {
+               if comment != "" && !showSrc {
                        pkg.newlines(1)
                        doc.ToText(&pkg.buf, comment, "    ", indent, indentedWidth)
                        pkg.newlines(2) // Blank line after comment to separate from next item.
@@ -611,7 +617,6 @@ func (pkg *Package) symbolDoc(symbol string) bool {
                }
                // Symbol is a function.
                decl := fun.Decl
-               decl.Body = nil
                pkg.emit(fun.Doc, decl)
                found = true
        }
@@ -641,7 +646,7 @@ func (pkg *Package) symbolDoc(symbol string) bool {
                        }
 
                        for _, ident := range vspec.Names {
-                               if isExported(ident.Name) {
+                               if showSrc || isExported(ident.Name) {
                                        if vspec.Type == nil && vspec.Values == nil && typ != nil {
                                                // This a standalone identifier, as in the case of iota usage.
                                                // Thus, assume the type comes from the previous type.
@@ -701,9 +706,10 @@ func (pkg *Package) symbolDoc(symbol string) bool {
 }
 
 // trimUnexportedElems modifies spec in place to elide unexported fields from
-// structs and methods from interfaces (unless the unexported flag is set).
+// structs and methods from interfaces (unless the unexported flag is set or we
+// are asked to show the original source).
 func trimUnexportedElems(spec *ast.TypeSpec) {
-       if unexported {
+       if unexported || showSrc {
                return
        }
        switch typ := spec.Type.(type) {
@@ -808,7 +814,6 @@ func (pkg *Package) printMethodDoc(symbol, method string) bool {
                        for _, meth := range typ.Methods {
                                if match(method, meth.Name) {
                                        decl := meth.Decl
-                                       decl.Body = nil
                                        pkg.emit(meth.Doc, decl)
                                        found = true
                                }
index bc069939f81833d12e1450b182b543ba66fba56b..50105b5fccce81a48d5f4c27185782c9cc84e481 100644 (file)
@@ -5,6 +5,8 @@
 // Package comment.
 package pkg
 
+import "io"
+
 // Constants
 
 // Comment about exported constant.
@@ -52,7 +54,9 @@ var (
 )
 
 // Comment about exported function.
-func ExportedFunc(a int) bool
+func ExportedFunc(a int) bool {
+       return true != false
+}
 
 // Comment about internal function.
 func internalFunc(a int) bool
@@ -73,7 +77,7 @@ type ExportedType struct {
 
 // Comment about exported method.
 func (ExportedType) ExportedMethod(a int) bool {
-       return true
+       return true != true
 }
 
 // Comment about unexported method.
index 9528ca29844ba30df928628ea26cbb0ed8423baa..f54f000b0777acb038711c958bbbab8f8648059c 100644 (file)
 //             Treat a command (package main) like a regular package.
 //             Otherwise package main's exported symbols are hidden
 //             when showing the package's top-level documentation.
+//     -src
+//             Show the full source code for the symbol. This will
+//             display the full Go source of its declaration and
+//             definition, such as a function definition (including
+//             the body), type declaration or enclosing const
+//             block. The output may therefore include unexported
+//             details.
 //     -u
 //             Show documentation for unexported as well as exported
 //             symbols, methods, and fields.
index 4e7dca082d7425f8e75d30ea588e75566b95e0a9..262bbb3ecb64b4172a874f00588cb7e3aec20328 100644 (file)
@@ -112,6 +112,13 @@ Flags:
                Treat a command (package main) like a regular package.
                Otherwise package main's exported symbols are hidden
                when showing the package's top-level documentation.
+       -src
+               Show the full source code for the symbol. This will
+               display the full Go source of its declaration and
+               definition, such as a function definition (including
+               the body), type declaration or enclosing const
+               block. The output may therefore include unexported
+               details.
        -u
                Show documentation for unexported as well as exported
                symbols, methods, and fields.