1
0
mirror of https://github.com/golang/go synced 2024-11-18 18:34:40 -07:00

go/analysis: add command-line help

The format of Analyzer.Doc is now specified as a short title followed
by a longer description. This allows us to build a nice
self-documenting command-line interface. Much of the documentation in
vet's doc.go and file-level comments can now be displayed to the user.

Change-Id: I462343e97ac9b743284aaa3e06e7a81d11e9593f
Reviewed-on: https://go-review.googlesource.com/138396
Reviewed-by: Michael Matloob <matloob@golang.org>
This commit is contained in:
Alan Donovan 2018-09-28 10:56:40 -04:00
parent 7b71b077e1
commit 51aacb1402
4 changed files with 118 additions and 5 deletions

View File

@ -32,6 +32,8 @@ type Analyzer struct {
Name string
// Doc is the documentation for the analyzer.
// The part before the first "\n\n" is the title
// (no capital or period, max ~60 letters).
Doc string
// Flags defines any flags accepted by the analyzer.

View File

@ -5,12 +5,21 @@ package multichecker
import (
"flag"
"fmt"
"log"
"os"
"sort"
"strings"
"golang.org/x/tools/go/analysis"
"golang.org/x/tools/go/analysis/internal/checker"
)
const usage = `Analyze is a tool for static analysis of Go programs.
Usage: analyze [-flag] [package]
`
func Main(analyzers ...*analysis.Analyzer) {
if err := analysis.Validate(analyzers); err != nil {
log.Fatal(err)
@ -47,7 +56,86 @@ func Main(analyzers ...*analysis.Analyzer) {
analyzers = keep
}
if err := checker.Run(flag.Args(), analyzers); err != nil {
args := flag.Args()
if len(args) == 0 {
fmt.Fprintln(os.Stderr, usage)
fmt.Fprintln(os.Stderr, `Run 'analyze help' for more detail,
or 'analyze help name' for details and flags of a specific analyzer.`)
os.Exit(1)
}
if args[0] == "help" {
help(analyzers, args[1:])
os.Exit(0)
}
if err := checker.Run(args, analyzers); err != nil {
log.Fatal(err)
}
}
func help(analyzers []*analysis.Analyzer, args []string) {
// No args: show summary of all analyzers.
if len(args) == 0 {
fmt.Println(usage)
fmt.Println("Registered analyzers:")
fmt.Println()
sort.Slice(analyzers, func(i, j int) bool {
return analyzers[i].Name < analyzers[j].Name
})
for _, a := range analyzers {
title := strings.Split(a.Doc, "\n\n")[0]
fmt.Printf(" %-12s %s\n", a.Name, title)
}
fmt.Println("\nBy default all analyzers are run.")
fmt.Println("To select specific analyzers, use the -NAME.enable flag for each one.")
// Show only the core command-line flags.
fmt.Println("\nCore flags:")
fmt.Println()
fs := flag.NewFlagSet("", flag.ExitOnError)
flag.VisitAll(func(f *flag.Flag) {
if !strings.Contains(f.Name, ".") {
fs.Var(f.Value, f.Name, f.Usage)
}
})
fs.PrintDefaults()
fmt.Println("\nTo see details and flags of a specific analyzer, run 'analyze help name'.")
return
}
// Show help on specific analyzer(s).
outer:
for _, arg := range args {
for _, a := range analyzers {
if a.Name == arg {
paras := strings.Split(a.Doc, "\n\n")
title := paras[0]
fmt.Printf("%s: %s\n", a.Name, title)
// Show only the flags relating to this analysis,
// properly prefixed.
first := true
fs := flag.NewFlagSet(a.Name, flag.ExitOnError)
a.Flags.VisitAll(func(f *flag.Flag) {
if first {
first = false
fmt.Println("\nAnalyzer flags:")
fmt.Println()
}
fs.Var(f.Value, a.Name+"."+f.Name, f.Usage)
})
fs.PrintDefaults()
if len(paras) > 1 {
fmt.Printf("\n%s\n", strings.Join(paras[1:], "\n\n"))
}
continue outer
}
}
log.Fatalf("Analyzer %q not registered", arg)
}
}

View File

@ -10,13 +10,16 @@ import (
)
var Analyzer = &analysis.Analyzer{
Name: "findcall",
Doc: "find calls to a particular function",
Name: "findcall",
Doc: `find calls to a particular function
The findcall analysis reports calls to functions or methods
of a particular name.`,
Run: findcall,
RunDespiteErrors: true,
}
var name = "println" // --name flag
var name = "println" // -name flag
func init() {
Analyzer.Flags.StringVar(&name, "name", name, "name of the function to find")

View File

@ -21,7 +21,10 @@ package singlechecker
import (
"flag"
"fmt"
"log"
"os"
"strings"
"golang.org/x/tools/go/analysis"
"golang.org/x/tools/go/analysis/internal/checker"
@ -42,9 +45,26 @@ func Main(a *analysis.Analyzer) {
flag.Var(f.Value, f.Name, f.Usage)
})
flag.Usage = func() {
paras := strings.Split(a.Doc, "\n\n")
fmt.Fprintf(os.Stderr, "%s: %s\n\n", a.Name, paras[0])
fmt.Printf("Usage: %s [-flag] [package]\n\n", a.Name)
if len(paras) > 1 {
fmt.Println(strings.Join(paras[1:], "\n\n"))
}
fmt.Println("\nFlags:")
flag.PrintDefaults()
}
flag.Parse()
if err := checker.Run(flag.Args(), []*analysis.Analyzer{a}); err != nil {
args := flag.Args()
if len(args) == 0 {
flag.Usage()
os.Exit(1)
}
if err := checker.Run(args, []*analysis.Analyzer{a}); err != nil {
log.Fatal(err)
}
}