Skip to content

Commit 294d16c

Browse files
committed
cmd/doc: add a -src flag to show original source
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 <[email protected]> Run-TryBot: Andrew Gerrand <[email protected]> TryBot-Result: Gobot Gobot <[email protected]>
1 parent d5e7220 commit 294d16c

File tree

6 files changed

+141
-56
lines changed

6 files changed

+141
-56
lines changed

src/cmd/doc/doc_test.go

Lines changed: 103 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -127,24 +127,24 @@ var tests = []test{
127127
`type T1 = T2`, // Type alias
128128
},
129129
[]string{
130-
`const internalConstant = 2`, // No internal constants.
131-
`var internalVariable = 2`, // No internal variables.
132-
`func internalFunc(a int) bool`, // No internal functions.
133-
`Comment about exported constant`, // No comment for single constant.
134-
`Comment about exported variable`, // No comment for single variable.
135-
`Comment about block of constants.`, // No comment for constant block.
136-
`Comment about block of variables.`, // No comment for variable block.
137-
`Comment before ConstOne`, // No comment for first entry in constant block.
138-
`Comment before VarOne`, // No comment for first entry in variable block.
139-
`ConstTwo = 2`, // No second entry in constant block.
140-
`VarTwo = 2`, // No second entry in variable block.
141-
`VarFive = 5`, // From block starting with unexported variable.
142-
`type unexportedType`, // No unexported type.
143-
`unexportedTypedConstant`, // No unexported typed constant.
144-
`\bField`, // No fields.
145-
`Method`, // No methods.
146-
`someArgument[5-8]`, // No truncated arguments.
147-
`type T1 T2`, // Type alias does not display as type declaration.
130+
`const internalConstant = 2`, // No internal constants.
131+
`var internalVariable = 2`, // No internal variables.
132+
`func internalFunc(a int) bool`, // No internal functions.
133+
`Comment about exported constant`, // No comment for single constant.
134+
`Comment about exported variable`, // No comment for single variable.
135+
`Comment about block of constants`, // No comment for constant block.
136+
`Comment about block of variables`, // No comment for variable block.
137+
`Comment before ConstOne`, // No comment for first entry in constant block.
138+
`Comment before VarOne`, // No comment for first entry in variable block.
139+
`ConstTwo = 2`, // No second entry in constant block.
140+
`VarTwo = 2`, // No second entry in variable block.
141+
`VarFive = 5`, // From block starting with unexported variable.
142+
`type unexportedType`, // No unexported type.
143+
`unexportedTypedConstant`, // No unexported typed constant.
144+
`\bField`, // No fields.
145+
`Method`, // No methods.
146+
`someArgument[5-8]`, // No truncated arguments.
147+
`type T1 T2`, // Type alias does not display as type declaration.
148148
},
149149
},
150150
// Package dump -u
@@ -207,6 +207,18 @@ var tests = []test{
207207
},
208208
nil,
209209
},
210+
// Block of constants -src.
211+
{
212+
"block of constants with -src",
213+
[]string{"-src", p, `ConstTwo`},
214+
[]string{
215+
`Comment about block of constants`, // Top comment.
216+
`ConstOne.*=.*1`, // Each constant seen.
217+
`ConstTwo.*=.*2.*Comment on line with ConstTwo`,
218+
`constThree`, // Even unexported constants.
219+
},
220+
nil,
221+
},
210222
// Block of constants with carryover type from unexported field.
211223
{
212224
"block of constants with carryover type",
@@ -295,6 +307,17 @@ var tests = []test{
295307
},
296308
nil,
297309
},
310+
// Function with -src.
311+
{
312+
"function with -src",
313+
[]string{"-src", p, `ExportedFunc`},
314+
[]string{
315+
`Comment about exported function`, // Include comment.
316+
`func ExportedFunc\(a int\) bool`,
317+
`return true != false`, // Include body.
318+
},
319+
nil,
320+
},
298321

299322
// Type.
300323
{
@@ -304,21 +327,44 @@ var tests = []test{
304327
`Comment about exported type`, // Include comment.
305328
`type ExportedType struct`, // Type definition.
306329
`Comment before exported field.*\n.*ExportedField +int` +
307-
`.*Comment on line with exported field.`,
308-
`ExportedEmbeddedType.*Comment on line with exported embedded field.`,
330+
`.*Comment on line with exported field`,
331+
`ExportedEmbeddedType.*Comment on line with exported embedded field`,
309332
`Has unexported fields`,
310333
`func \(ExportedType\) ExportedMethod\(a int\) bool`,
311334
`const ExportedTypedConstant ExportedType = iota`, // Must include associated constant.
312335
`func ExportedTypeConstructor\(\) \*ExportedType`, // Must include constructor.
313-
`io.Reader.*Comment on line with embedded Reader.`,
336+
`io.Reader.*Comment on line with embedded Reader`,
314337
},
315338
[]string{
316-
`unexportedField`, // No unexported field.
317-
`int.*embedded`, // No unexported embedded field.
318-
`Comment about exported method.`, // No comment about exported method.
319-
`unexportedMethod`, // No unexported method.
320-
`unexportedTypedConstant`, // No unexported constant.
321-
`error`, // No embedded error.
339+
`unexportedField`, // No unexported field.
340+
`int.*embedded`, // No unexported embedded field.
341+
`Comment about exported method`, // No comment about exported method.
342+
`unexportedMethod`, // No unexported method.
343+
`unexportedTypedConstant`, // No unexported constant.
344+
`error`, // No embedded error.
345+
},
346+
},
347+
// Type with -src. Will see unexported fields.
348+
{
349+
"type",
350+
[]string{"-src", p, `ExportedType`},
351+
[]string{
352+
`Comment about exported type`, // Include comment.
353+
`type ExportedType struct`, // Type definition.
354+
`Comment before exported field.*\n.*ExportedField +int` +
355+
`.*Comment on line with exported field`,
356+
`ExportedEmbeddedType.*Comment on line with exported embedded field`,
357+
`unexportedType.*Comment on line with unexported embedded field`,
358+
`func \(ExportedType\) ExportedMethod\(a int\) bool`,
359+
`const ExportedTypedConstant ExportedType = iota`, // Must include associated constant.
360+
`func ExportedTypeConstructor\(\) \*ExportedType`, // Must include constructor.
361+
`io.Reader.*Comment on line with embedded Reader`,
362+
},
363+
[]string{
364+
`int.*embedded`, // No unexported embedded field.
365+
`Comment about exported method`, // No comment about exported method.
366+
`unexportedMethod`, // No unexported method.
367+
`unexportedTypedConstant`, // No unexported constant.
322368
},
323369
},
324370
// Type T1 dump (alias).
@@ -341,14 +387,14 @@ var tests = []test{
341387
`Comment about exported type`, // Include comment.
342388
`type ExportedType struct`, // Type definition.
343389
`Comment before exported field.*\n.*ExportedField +int`,
344-
`unexportedField.*int.*Comment on line with unexported field.`,
345-
`ExportedEmbeddedType.*Comment on line with exported embedded field.`,
346-
`\*ExportedEmbeddedType.*Comment on line with exported embedded \*field.`,
347-
`\*qualified.ExportedEmbeddedType.*Comment on line with exported embedded \*selector.field.`,
348-
`unexportedType.*Comment on line with unexported embedded field.`,
349-
`\*unexportedType.*Comment on line with unexported embedded \*field.`,
350-
`io.Reader.*Comment on line with embedded Reader.`,
351-
`error.*Comment on line with embedded error.`,
390+
`unexportedField.*int.*Comment on line with unexported field`,
391+
`ExportedEmbeddedType.*Comment on line with exported embedded field`,
392+
`\*ExportedEmbeddedType.*Comment on line with exported embedded \*field`,
393+
`\*qualified.ExportedEmbeddedType.*Comment on line with exported embedded \*selector.field`,
394+
`unexportedType.*Comment on line with unexported embedded field`,
395+
`\*unexportedType.*Comment on line with unexported embedded \*field`,
396+
`io.Reader.*Comment on line with embedded Reader`,
397+
`error.*Comment on line with embedded error`,
352398
`func \(ExportedType\) unexportedMethod\(a int\) bool`,
353399
`unexportedTypedConstant`,
354400
},
@@ -380,8 +426,8 @@ var tests = []test{
380426
`type ExportedInterface interface`, // Interface definition.
381427
`Comment before exported method.*\n.*ExportedMethod\(\)` +
382428
`.*Comment on line with exported method`,
383-
`io.Reader.*Comment on line with embedded Reader.`,
384-
`error.*Comment on line with embedded error.`,
429+
`io.Reader.*Comment on line with embedded Reader`,
430+
`error.*Comment on line with embedded error`,
385431
`Has unexported methods`,
386432
},
387433
[]string{
@@ -400,9 +446,9 @@ var tests = []test{
400446
`type ExportedInterface interface`, // Interface definition.
401447
`Comment before exported method.*\n.*ExportedMethod\(\)` +
402448
`.*Comment on line with exported method`,
403-
`unexportedMethod\(\).*Comment on line with unexported method.`,
404-
`io.Reader.*Comment on line with embedded Reader.`,
405-
`error.*Comment on line with embedded error.`,
449+
`unexportedMethod\(\).*Comment on line with unexported method`,
450+
`io.Reader.*Comment on line with embedded Reader`,
451+
`error.*Comment on line with embedded error`,
406452
},
407453
[]string{
408454
`Has unexported methods`,
@@ -418,7 +464,7 @@ var tests = []test{
418464
`.*Comment on line with exported method`,
419465
},
420466
[]string{
421-
`Comment about exported interface.`,
467+
`Comment about exported interface`,
422468
},
423469
},
424470

@@ -428,7 +474,7 @@ var tests = []test{
428474
[]string{p, `ExportedType.ExportedMethod`},
429475
[]string{
430476
`func \(ExportedType\) ExportedMethod\(a int\) bool`,
431-
`Comment about exported method.`,
477+
`Comment about exported method`,
432478
},
433479
nil,
434480
},
@@ -438,7 +484,18 @@ var tests = []test{
438484
[]string{"-u", p, `ExportedType.unexportedMethod`},
439485
[]string{
440486
`func \(ExportedType\) unexportedMethod\(a int\) bool`,
441-
`Comment about unexported method.`,
487+
`Comment about unexported method`,
488+
},
489+
nil,
490+
},
491+
// Method with -src.
492+
{
493+
"method with -src",
494+
[]string{"-src", p, `ExportedType.ExportedMethod`},
495+
[]string{
496+
`func \(ExportedType\) ExportedMethod\(a int\) bool`,
497+
`Comment about exported method`,
498+
`return true != true`,
442499
},
443500
nil,
444501
},
@@ -450,8 +507,8 @@ var tests = []test{
450507
[]string{
451508
`type ExportedType struct`,
452509
`ExportedField int`,
453-
`Comment before exported field.`,
454-
`Comment on line with exported field.`,
510+
`Comment before exported field`,
511+
`Comment on line with exported field`,
455512
`other fields elided`,
456513
},
457514
nil,
@@ -463,7 +520,7 @@ var tests = []test{
463520
[]string{"-u", p, `ExportedType.unexportedField`},
464521
[]string{
465522
`unexportedField int`,
466-
`Comment on line with unexported field.`,
523+
`Comment on line with unexported field`,
467524
},
468525
nil,
469526
},

src/cmd/doc/main.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@
2828
// For commands, unless the -cmd flag is present "go doc command"
2929
// shows only the package-level docs for the package.
3030
//
31+
// The -src flag causes doc to print the full source code for the symbol, such
32+
// as the body of a struct, function or method.
33+
//
3134
// For complete documentation, run "go help doc".
3235
package main
3336

@@ -50,6 +53,7 @@ var (
5053
unexported bool // -u flag
5154
matchCase bool // -c flag
5255
showCmd bool // -cmd flag
56+
showSrc bool // -src flag
5357
)
5458

5559
// 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) {
8589
flagSet.BoolVar(&unexported, "u", false, "show unexported symbols as well as exported")
8690
flagSet.BoolVar(&matchCase, "c", false, "symbol matching honors case (paths not affected)")
8791
flagSet.BoolVar(&showCmd, "cmd", false, "show symbols with package docs even if package is a command")
92+
flagSet.BoolVar(&showSrc, "src", false, "show source code for symbol")
8893
flagSet.Parse(args)
8994
var paths []string
9095
var symbol, method string

src/cmd/doc/pkg.go

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,11 @@ func parsePackage(writer io.Writer, pkg *build.Package, userPath string) *Packag
137137
// from finding the symbol. Work around this for now, but we
138138
// should fix it in go/doc.
139139
// A similar story applies to factory functions.
140-
docPkg := doc.New(astPkg, pkg.ImportPath, doc.AllDecls)
140+
mode := doc.AllDecls
141+
if showSrc {
142+
mode |= doc.PreserveAST // See comment for Package.emit.
143+
}
144+
docPkg := doc.New(astPkg, pkg.ImportPath, mode)
141145
for _, typ := range docPkg.Types {
142146
docPkg.Consts = append(docPkg.Consts, typ.Consts...)
143147
docPkg.Vars = append(docPkg.Vars, typ.Vars...)
@@ -177,14 +181,16 @@ func (pkg *Package) newlines(n int) {
177181
}
178182
}
179183

180-
// emit prints the node.
184+
// emit prints the node. If showSrc is true, it ignores the provided comment,
185+
// assuming the comment is in the node itself. Otherwise, the go/doc package
186+
// clears the stuff we don't want to print anyway. It's a bit of a magic trick.
181187
func (pkg *Package) emit(comment string, node ast.Node) {
182188
if node != nil {
183189
err := format.Node(&pkg.buf, pkg.fs, node)
184190
if err != nil {
185191
log.Fatal(err)
186192
}
187-
if comment != "" {
193+
if comment != "" && !showSrc {
188194
pkg.newlines(1)
189195
doc.ToText(&pkg.buf, comment, " ", indent, indentedWidth)
190196
pkg.newlines(2) // Blank line after comment to separate from next item.
@@ -611,7 +617,6 @@ func (pkg *Package) symbolDoc(symbol string) bool {
611617
}
612618
// Symbol is a function.
613619
decl := fun.Decl
614-
decl.Body = nil
615620
pkg.emit(fun.Doc, decl)
616621
found = true
617622
}
@@ -641,7 +646,7 @@ func (pkg *Package) symbolDoc(symbol string) bool {
641646
}
642647

643648
for _, ident := range vspec.Names {
644-
if isExported(ident.Name) {
649+
if showSrc || isExported(ident.Name) {
645650
if vspec.Type == nil && vspec.Values == nil && typ != nil {
646651
// This a standalone identifier, as in the case of iota usage.
647652
// Thus, assume the type comes from the previous type.
@@ -701,9 +706,10 @@ func (pkg *Package) symbolDoc(symbol string) bool {
701706
}
702707

703708
// trimUnexportedElems modifies spec in place to elide unexported fields from
704-
// structs and methods from interfaces (unless the unexported flag is set).
709+
// structs and methods from interfaces (unless the unexported flag is set or we
710+
// are asked to show the original source).
705711
func trimUnexportedElems(spec *ast.TypeSpec) {
706-
if unexported {
712+
if unexported || showSrc {
707713
return
708714
}
709715
switch typ := spec.Type.(type) {
@@ -808,7 +814,6 @@ func (pkg *Package) printMethodDoc(symbol, method string) bool {
808814
for _, meth := range typ.Methods {
809815
if match(method, meth.Name) {
810816
decl := meth.Decl
811-
decl.Body = nil
812817
pkg.emit(meth.Doc, decl)
813818
found = true
814819
}

src/cmd/doc/testdata/pkg.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
// Package comment.
66
package pkg
77

8+
import "io"
9+
810
// Constants
911

1012
// Comment about exported constant.
@@ -52,7 +54,9 @@ var (
5254
)
5355

5456
// Comment about exported function.
55-
func ExportedFunc(a int) bool
57+
func ExportedFunc(a int) bool {
58+
return true != false
59+
}
5660

5761
// Comment about internal function.
5862
func internalFunc(a int) bool
@@ -73,7 +77,7 @@ type ExportedType struct {
7377

7478
// Comment about exported method.
7579
func (ExportedType) ExportedMethod(a int) bool {
76-
return true
80+
return true != true
7781
}
7882

7983
// Comment about unexported method.

src/cmd/go/alldocs.go

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/cmd/go/internal/doc/doc.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,13 @@ Flags:
112112
Treat a command (package main) like a regular package.
113113
Otherwise package main's exported symbols are hidden
114114
when showing the package's top-level documentation.
115+
-src
116+
Show the full source code for the symbol. This will
117+
display the full Go source of its declaration and
118+
definition, such as a function definition (including
119+
the body), type declaration or enclosing const
120+
block. The output may therefore include unexported
121+
details.
115122
-u
116123
Show documentation for unexported as well as exported
117124
symbols, methods, and fields.

0 commit comments

Comments
 (0)