golang / go

The Go programming language
https://go.dev
BSD 3-Clause "New" or "Revised" License
122.91k stars 17.52k forks source link

cmd/go: go doc -h is inconsistent and slightly broken #55985

Open mvdan opened 1 year ago

mvdan commented 1 year ago
$ go version
go version devel go1.20-5f566d35bf Sat Oct 1 08:32:46 2022 +0000 linux/amd64

In general, go command -h is very short at just a few lines, pointing instead to go help command for the longer help text. However, go doc -h is an outlier.

$ for cmd in bug build clean doc env fix fmt generate get install list mod work run test tool version vet; do echo "\$ go $cmd -h"; go $cmd -h; ech
o; done
$ go bug -h
usage: go bug
Run 'go help bug' for details.

$ go build -h
usage: go build [-o output] [build flags] [packages]
Run 'go help build' for details.

$ go clean -h
usage: go clean [clean flags] [build flags] [packages]
Run 'go help clean' for details.

$ go doc -h
Usage of [go] doc:
    go doc
    go doc <pkg>
    go doc <sym>[.<methodOrField>]
    go doc [<pkg>.]<sym>[.<methodOrField>]
    go doc [<pkg>.][<sym>.]<methodOrField>
    go doc <pkg> <sym>[.<methodOrField>]
For more information run
    go help doc

Flags:
  -all
        show all documentation for package
  -c    symbol matching honors case (paths not affected)
  -cmd
        show symbols with package docs even if package is a command
  -short
        one-line representation for each symbol
  -src
        show source code for symbol
  -u    show unexported symbols as well as exported
exit status 2

$ go env -h
usage: go env [-json] [-u] [-w] [var ...]
Run 'go help env' for details.

$ go fix -h
usage: go fix [-fix list] [packages]
Run 'go help fix' for details.

$ go fmt -h
usage: go fmt [-n] [-x] [packages]
Run 'go help fmt' for details.

$ go generate -h
usage: go generate [-run regexp] [-n] [-v] [-x] [build flags] [file.go... | packages]
Run 'go help generate' for details.

$ go get -h
usage: go get [-t] [-u] [-v] [build flags] [packages]
Run 'go help get' for details.

$ go install -h
usage: go install [build flags] [packages]
Run 'go help install' for details.

$ go list -h
usage: go list [-f format] [-json] [-m] [list flags] [build flags] [packages]
Run 'go help list' for details.

$ go mod -h
go mod -h: unknown command
Run 'go help mod' for usage.

$ go work -h
go work -h: unknown command
Run 'go help work' for usage.

$ go run -h
usage: go run [build flags] [-exec xprog] package [arguments...]
Run 'go help run' for details.

$ go test -h
usage: go test [build/test flags] [packages] [build/test flags & test binary flags]
Run 'go help test' and 'go help testflag' for details.

$ go tool -h
usage: go tool [-n] command [args...]
Run 'go help tool' for details.

$ go version -h
usage: go version [-m] [-v] [file ...]
Run 'go help version' for details.

$ go vet -h
usage: go vet [-n] [-x] [-vettool prog] [build flags] [vet flags] [packages]
Run 'go help vet' for details.
Run 'go tool vet help' for a full list of flags and analyzers.
Run 'go tool vet -help' for an overview.

Note that go doc -h prints many more lines than the others, but also, it prints an exit status 2 line which seems wrong. The latter seems to be because it combines CustomFlags: true with executing go tool doc -h, which means that the flag parsing happens in the child process, and the parent process simply shows the non-zero exit status.

We probably should:

Also note that go tool doc -h is slightly confusing; unlike others such as go tool fix -h, it shows the help for go doc rather than go tool doc.

cc @robpike @bcmills @matloob

robpike commented 1 year ago

Doesn't seem like much of a problem to me. The output is helpful, arguably more so than the others'. I wouldn't call it "broken".

Is vet problematic for you too? Because it's also different, and harmlessly so.

mvdan commented 1 year ago

At a minimum, the exit status 2 seems wrong.

I wouldn't mind if go doc -h resembled go vet -h. But I do think we want to be consistent for the sake of not confusing our users. I don't claim that one way is better than the other, but go doc -h is the outlier today.

mvdan commented 1 year ago

I was just stumped at go doc -h hanging for multiple seconds when my current directory was a Go package with a large tree of dependencies. I thought to myself that perhaps it was loading the entire package tree and filling the build cache, which would certainly be a bug.

Here's a small repro, using https://github.com/mvdan/sh/tree/master/interp as a sample Go package inside a third party module with some imports:

$ go clean -cache -modcache
$ time go doc -h
[...]

real    0m0.348s
user    0m0.069s
sys 0m0.020s
$ go clean -cache -modcache
$ time go help doc
[...]

real    0m0.005s
user    0m0.007s
sys 0m0.000s

In this particular case, the wait was about a third of a second, because the package and its dependencies are smaller - go list -deps shows 63 packages. With the larger package, where go doc -h with an empty cache took a staggering 17s, go list -deps shows 627 packages.

nickjwhite commented 1 year ago

I wouldn't mind if go doc -h resembled go vet -h. But I do think we want to be consistent for the sake of not confusing our users. I don't claim that one way is better than the other, but go doc -h is the outlier today.

I'm not sure which way is best, but thought I'd share my experience. I hadn't noticed the usage() part of doc recently when writing a change (not having considered someone running go doc -h), and just focused on updating the documentation in src/cmd/go/internal/doc/doc.go. In my case it wouldn't have made any difference, but having duplicated documentation in different places does increase the chance of documentation differing in different places.

One alternative option would be to grab some documentation (perhaps just the UsageLine straight from the base.Command defined in src/cmd/go/internal/doc/doc.go.

Consistency between tools would be nice, but I'd lean towards making the others at least print their basic usage, rather than just essentially remove -h from all tools.

nickjwhite commented 1 year ago

I was just stumped at go doc -h hanging for multiple seconds when my current directory was a Go package with a large tree of dependencies. I thought to myself that perhaps it was loading the entire package tree and filling the build cache, which would certainly be a bug.

That looks to be because dirsInit() is called before the flag parsing. From a brief read of the code it looks like it could be started after flag parsing (though the extras would need to be passed to do() in order for the tests to be set up appropriately), which would solve your particular case of go doc -h being too slow.

For the wider issue of initial go doc running being slow from a package with many dependencies, you'd know better than me but I'd imagine that deserves a separate issue.