libgo: update to Go 1.11
[official-gcc.git] / libgo / go / cmd / vet / main.go
blobc50d4885a078745ffa2bc10b2a7c59c5469507d9
1 // Copyright 2010 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
5 // Vet is a simple checker for static errors in Go source code.
6 // See doc.go for more information.
8 package main
10 import (
11 "bytes"
12 "encoding/gob"
13 "encoding/json"
14 "flag"
15 "fmt"
16 "go/ast"
17 "go/build"
18 "go/importer"
19 "go/parser"
20 "go/printer"
21 "go/token"
22 "go/types"
23 "io"
24 "io/ioutil"
25 "os"
26 "path/filepath"
27 "sort"
28 "strconv"
29 "strings"
31 "cmd/internal/objabi"
34 // Important! If you add flags here, make sure to update cmd/go/internal/vet/vetflag.go.
36 var (
37 verbose = flag.Bool("v", false, "verbose")
38 source = flag.Bool("source", false, "import from source instead of compiled object files")
39 tags = flag.String("tags", "", "space-separated list of build tags to apply when parsing")
40 tagList = []string{} // exploded version of tags flag; set in main
42 vcfg vetConfig
43 mustTypecheck bool
46 var exitCode = 0
48 // "-all" flag enables all non-experimental checks
49 var all = triStateFlag("all", unset, "enable all non-experimental checks")
51 // Flags to control which individual checks to perform.
52 var report = map[string]*triState{
53 // Only unusual checks are written here.
54 // Most checks that operate during the AST walk are added by register.
55 "asmdecl": triStateFlag("asmdecl", unset, "check assembly against Go declarations"),
56 "buildtags": triStateFlag("buildtags", unset, "check that +build tags are valid"),
59 // experimental records the flags enabling experimental features. These must be
60 // requested explicitly; they are not enabled by -all.
61 var experimental = map[string]bool{}
63 // setTrueCount record how many flags are explicitly set to true.
64 var setTrueCount int
66 // dirsRun and filesRun indicate whether the vet is applied to directory or
67 // file targets. The distinction affects which checks are run.
68 var dirsRun, filesRun bool
70 // includesNonTest indicates whether the vet is applied to non-test targets.
71 // Certain checks are relevant only if they touch both test and non-test files.
72 var includesNonTest bool
74 // A triState is a boolean that knows whether it has been set to either true or false.
75 // It is used to identify if a flag appears; the standard boolean flag cannot
76 // distinguish missing from unset. It also satisfies flag.Value.
77 type triState int
79 const (
80 unset triState = iota
81 setTrue
82 setFalse
85 func triStateFlag(name string, value triState, usage string) *triState {
86 flag.Var(&value, name, usage)
87 return &value
90 // triState implements flag.Value, flag.Getter, and flag.boolFlag.
91 // They work like boolean flags: we can say vet -printf as well as vet -printf=true
92 func (ts *triState) Get() interface{} {
93 return *ts == setTrue
96 func (ts triState) isTrue() bool {
97 return ts == setTrue
100 func (ts *triState) Set(value string) error {
101 b, err := strconv.ParseBool(value)
102 if err != nil {
103 return err
105 if b {
106 *ts = setTrue
107 setTrueCount++
108 } else {
109 *ts = setFalse
111 return nil
114 func (ts *triState) String() string {
115 switch *ts {
116 case unset:
117 return "true" // An unset flag will be set by -all, so defaults to true.
118 case setTrue:
119 return "true"
120 case setFalse:
121 return "false"
123 panic("not reached")
126 func (ts triState) IsBoolFlag() bool {
127 return true
130 // vet tells whether to report errors for the named check, a flag name.
131 func vet(name string) bool {
132 return report[name].isTrue()
135 // setExit sets the value for os.Exit when it is called, later. It
136 // remembers the highest value.
137 func setExit(err int) {
138 if err > exitCode {
139 exitCode = err
143 var (
144 // Each of these vars has a corresponding case in (*File).Visit.
145 assignStmt *ast.AssignStmt
146 binaryExpr *ast.BinaryExpr
147 callExpr *ast.CallExpr
148 compositeLit *ast.CompositeLit
149 exprStmt *ast.ExprStmt
150 forStmt *ast.ForStmt
151 funcDecl *ast.FuncDecl
152 funcLit *ast.FuncLit
153 genDecl *ast.GenDecl
154 interfaceType *ast.InterfaceType
155 rangeStmt *ast.RangeStmt
156 returnStmt *ast.ReturnStmt
157 structType *ast.StructType
159 // checkers is a two-level map.
160 // The outer level is keyed by a nil pointer, one of the AST vars above.
161 // The inner level is keyed by checker name.
162 checkers = make(map[ast.Node]map[string]func(*File, ast.Node))
163 pkgCheckers = make(map[string]func(*Package))
164 exporters = make(map[string]func() interface{})
167 // The exporters data as written to the vetx output file.
168 type vetxExport struct {
169 Name string
170 Data interface{}
173 // Vet can provide its own "export information"
174 // about package A to future invocations of vet
175 // on packages importing A. If B imports A,
176 // then running "go vet B" actually invokes vet twice:
177 // first, it runs vet on A, in "vetx-only" mode, which
178 // skips most checks and only computes export data
179 // describing A. Then it runs vet on B, making A's vetx
180 // data available for consultation. The vet of B
181 // computes vetx data for B in addition to its
182 // usual vet checks.
184 // register registers the named check function,
185 // to be called with AST nodes of the given types.
186 // The registered functions are not called in vetx-only mode.
187 func register(name, usage string, fn func(*File, ast.Node), types ...ast.Node) {
188 report[name] = triStateFlag(name, unset, usage)
189 for _, typ := range types {
190 m := checkers[typ]
191 if m == nil {
192 m = make(map[string]func(*File, ast.Node))
193 checkers[typ] = m
195 m[name] = fn
199 // registerPkgCheck registers a package-level checking function,
200 // to be invoked with the whole package being vetted
201 // before any of the per-node handlers.
202 // The registered function fn is called even in vetx-only mode
203 // (see comment above), so fn must take care not to report
204 // errors when vcfg.VetxOnly is true.
205 func registerPkgCheck(name string, fn func(*Package)) {
206 pkgCheckers[name] = fn
209 // registerExport registers a function to return vetx export data
210 // that should be saved and provided to future invocations of vet
211 // when checking packages importing this one.
212 // The value returned by fn should be nil or else valid to encode using gob.
213 // Typically a registerExport call is paired with a call to gob.Register.
214 func registerExport(name string, fn func() interface{}) {
215 exporters[name] = fn
218 // Usage is a replacement usage function for the flags package.
219 func Usage() {
220 fmt.Fprintf(os.Stderr, "Usage of vet:\n")
221 fmt.Fprintf(os.Stderr, "\tvet [flags] directory...\n")
222 fmt.Fprintf(os.Stderr, "\tvet [flags] files... # Must be a single package\n")
223 fmt.Fprintf(os.Stderr, "By default, -all is set and all non-experimental checks are run.\n")
224 fmt.Fprintf(os.Stderr, "For more information run\n")
225 fmt.Fprintf(os.Stderr, "\tgo doc cmd/vet\n\n")
226 fmt.Fprintf(os.Stderr, "Flags:\n")
227 flag.PrintDefaults()
228 os.Exit(2)
231 // File is a wrapper for the state of a file used in the parser.
232 // The parse tree walkers are all methods of this type.
233 type File struct {
234 pkg *Package
235 fset *token.FileSet
236 name string
237 content []byte
238 file *ast.File
239 b bytes.Buffer // for use by methods
241 // Parsed package "foo" when checking package "foo_test"
242 basePkg *Package
244 // The keys are the objects that are receivers of a "String()
245 // string" method. The value reports whether the method has a
246 // pointer receiver.
247 // This is used by the recursiveStringer method in print.go.
248 stringerPtrs map[*ast.Object]bool
250 // Registered checkers to run.
251 checkers map[ast.Node][]func(*File, ast.Node)
253 // Unreachable nodes; can be ignored in shift check.
254 dead map[ast.Node]bool
257 func main() {
258 objabi.AddVersionFlag()
259 flag.Usage = Usage
260 flag.Parse()
262 // If any flag is set, we run only those checks requested.
263 // If all flag is set true or if no flags are set true, set all the non-experimental ones
264 // not explicitly set (in effect, set the "-all" flag).
265 if setTrueCount == 0 || *all == setTrue {
266 for name, setting := range report {
267 if *setting == unset && !experimental[name] {
268 *setting = setTrue
273 // Accept space-separated tags because that matches
274 // the go command's other subcommands.
275 // Accept commas because go tool vet traditionally has.
276 tagList = strings.Fields(strings.Replace(*tags, ",", " ", -1))
278 initPrintFlags()
279 initUnusedFlags()
281 if flag.NArg() == 0 {
282 Usage()
285 // Special case for "go vet" passing an explicit configuration:
286 // single argument ending in vet.cfg.
287 // Once we have a more general mechanism for obtaining this
288 // information from build tools like the go command,
289 // vet should be changed to use it. This vet.cfg hack is an
290 // experiment to learn about what form that information should take.
291 if flag.NArg() == 1 && strings.HasSuffix(flag.Arg(0), "vet.cfg") {
292 doPackageCfg(flag.Arg(0))
293 os.Exit(exitCode)
296 for _, name := range flag.Args() {
297 // Is it a directory?
298 fi, err := os.Stat(name)
299 if err != nil {
300 warnf("error walking tree: %s", err)
301 continue
303 if fi.IsDir() {
304 dirsRun = true
305 } else {
306 filesRun = true
307 if !strings.HasSuffix(name, "_test.go") {
308 includesNonTest = true
312 if dirsRun && filesRun {
313 Usage()
315 if dirsRun {
316 for _, name := range flag.Args() {
317 walkDir(name)
319 os.Exit(exitCode)
321 if doPackage(flag.Args(), nil) == nil {
322 warnf("no files checked")
324 os.Exit(exitCode)
327 // prefixDirectory places the directory name on the beginning of each name in the list.
328 func prefixDirectory(directory string, names []string) {
329 if directory != "." {
330 for i, name := range names {
331 names[i] = filepath.Join(directory, name)
336 // vetConfig is the JSON config struct prepared by the Go command.
337 type vetConfig struct {
338 Compiler string
339 Dir string
340 ImportPath string
341 GoFiles []string
342 ImportMap map[string]string
343 PackageFile map[string]string
344 Standard map[string]bool
345 PackageVetx map[string]string // map from import path to vetx data file
346 VetxOnly bool // only compute vetx output; don't run ordinary checks
347 VetxOutput string // file where vetx output should be written
349 SucceedOnTypecheckFailure bool
351 imp types.Importer
354 func (v *vetConfig) Import(path string) (*types.Package, error) {
355 if v.imp == nil {
356 v.imp = importer.For(v.Compiler, v.openPackageFile)
358 if path == "unsafe" {
359 return v.imp.Import("unsafe")
361 p := v.ImportMap[path]
362 if p == "" {
363 return nil, fmt.Errorf("unknown import path %q", path)
365 if v.PackageFile[p] == "" {
366 if v.Compiler == "gccgo" && v.Standard[path] {
367 // gccgo doesn't have sources for standard library packages,
368 // but the importer will do the right thing.
369 return v.imp.Import(path)
371 return nil, fmt.Errorf("unknown package file for import %q", path)
373 return v.imp.Import(p)
376 func (v *vetConfig) openPackageFile(path string) (io.ReadCloser, error) {
377 file := v.PackageFile[path]
378 if file == "" {
379 if v.Compiler == "gccgo" && v.Standard[path] {
380 // The importer knows how to handle this.
381 return nil, nil
383 // Note that path here has been translated via v.ImportMap,
384 // unlike in the error in Import above. We prefer the error in
385 // Import, but it's worth diagnosing this one too, just in case.
386 return nil, fmt.Errorf("unknown package file for %q", path)
388 f, err := os.Open(file)
389 if err != nil {
390 return nil, err
392 return f, nil
395 // doPackageCfg analyzes a single package described in a config file.
396 func doPackageCfg(cfgFile string) {
397 js, err := ioutil.ReadFile(cfgFile)
398 if err != nil {
399 errorf("%v", err)
401 if err := json.Unmarshal(js, &vcfg); err != nil {
402 errorf("parsing vet config %s: %v", cfgFile, err)
404 stdImporter = &vcfg
405 inittypes()
406 mustTypecheck = true
407 doPackage(vcfg.GoFiles, nil)
408 if vcfg.VetxOutput != "" {
409 out := make([]vetxExport, 0, len(exporters))
410 for name, fn := range exporters {
411 out = append(out, vetxExport{
412 Name: name,
413 Data: fn(),
416 // Sort the data so that it is consistent across builds.
417 sort.Slice(out, func(i, j int) bool {
418 return out[i].Name < out[j].Name
420 var buf bytes.Buffer
421 if err := gob.NewEncoder(&buf).Encode(out); err != nil {
422 errorf("encoding vet output: %v", err)
423 return
425 if err := ioutil.WriteFile(vcfg.VetxOutput, buf.Bytes(), 0666); err != nil {
426 errorf("saving vet output: %v", err)
427 return
432 // doPackageDir analyzes the single package found in the directory, if there is one,
433 // plus a test package, if there is one.
434 func doPackageDir(directory string) {
435 context := build.Default
436 if len(context.BuildTags) != 0 {
437 warnf("build tags %s previously set", context.BuildTags)
439 context.BuildTags = append(tagList, context.BuildTags...)
441 pkg, err := context.ImportDir(directory, 0)
442 if err != nil {
443 // If it's just that there are no go source files, that's fine.
444 if _, nogo := err.(*build.NoGoError); nogo {
445 return
447 // Non-fatal: we are doing a recursive walk and there may be other directories.
448 warnf("cannot process directory %s: %s", directory, err)
449 return
451 var names []string
452 names = append(names, pkg.GoFiles...)
453 names = append(names, pkg.CgoFiles...)
454 names = append(names, pkg.TestGoFiles...) // These are also in the "foo" package.
455 names = append(names, pkg.SFiles...)
456 prefixDirectory(directory, names)
457 basePkg := doPackage(names, nil)
458 // Is there also a "foo_test" package? If so, do that one as well.
459 if len(pkg.XTestGoFiles) > 0 {
460 names = pkg.XTestGoFiles
461 prefixDirectory(directory, names)
462 doPackage(names, basePkg)
466 type Package struct {
467 path string
468 defs map[*ast.Ident]types.Object
469 uses map[*ast.Ident]types.Object
470 selectors map[*ast.SelectorExpr]*types.Selection
471 types map[ast.Expr]types.TypeAndValue
472 spans map[types.Object]Span
473 files []*File
474 typesPkg *types.Package
477 // doPackage analyzes the single package constructed from the named files.
478 // It returns the parsed Package or nil if none of the files have been checked.
479 func doPackage(names []string, basePkg *Package) *Package {
480 var files []*File
481 var astFiles []*ast.File
482 fs := token.NewFileSet()
483 for _, name := range names {
484 data, err := ioutil.ReadFile(name)
485 if err != nil {
486 // Warn but continue to next package.
487 warnf("%s: %s", name, err)
488 return nil
490 var parsedFile *ast.File
491 if strings.HasSuffix(name, ".go") {
492 parsedFile, err = parser.ParseFile(fs, name, data, parser.ParseComments)
493 if err != nil {
494 warnf("%s: %s", name, err)
495 return nil
497 astFiles = append(astFiles, parsedFile)
499 file := &File{
500 fset: fs,
501 content: data,
502 name: name,
503 file: parsedFile,
504 dead: make(map[ast.Node]bool),
506 files = append(files, file)
508 if len(astFiles) == 0 {
509 return nil
511 pkg := new(Package)
512 pkg.path = astFiles[0].Name.Name
513 pkg.files = files
514 // Type check the package.
515 errs := pkg.check(fs, astFiles)
516 if errs != nil {
517 if vcfg.SucceedOnTypecheckFailure {
518 os.Exit(0)
520 if *verbose || mustTypecheck {
521 for _, err := range errs {
522 fmt.Fprintf(os.Stderr, "%v\n", err)
524 if mustTypecheck {
525 // This message could be silenced, and we could just exit,
526 // but it might be helpful at least at first to make clear that the
527 // above errors are coming from vet and not the compiler
528 // (they often look like compiler errors, such as "declared but not used").
529 errorf("typecheck failures")
534 // Check.
535 for _, file := range files {
536 file.pkg = pkg
537 file.basePkg = basePkg
539 for name, fn := range pkgCheckers {
540 if vet(name) {
541 fn(pkg)
544 if vcfg.VetxOnly {
545 return pkg
548 chk := make(map[ast.Node][]func(*File, ast.Node))
549 for typ, set := range checkers {
550 for name, fn := range set {
551 if vet(name) {
552 chk[typ] = append(chk[typ], fn)
556 for _, file := range files {
557 checkBuildTag(file)
558 file.checkers = chk
559 if file.file != nil {
560 file.walkFile(file.name, file.file)
563 return pkg
566 func visit(path string, f os.FileInfo, err error) error {
567 if err != nil {
568 warnf("walk error: %s", err)
569 return err
571 // One package per directory. Ignore the files themselves.
572 if !f.IsDir() {
573 return nil
575 doPackageDir(path)
576 return nil
579 func (pkg *Package) hasFileWithSuffix(suffix string) bool {
580 for _, f := range pkg.files {
581 if strings.HasSuffix(f.name, suffix) {
582 return true
585 return false
588 // walkDir recursively walks the tree looking for Go packages.
589 func walkDir(root string) {
590 filepath.Walk(root, visit)
593 // errorf formats the error to standard error, adding program
594 // identification and a newline, and exits.
595 func errorf(format string, args ...interface{}) {
596 fmt.Fprintf(os.Stderr, "vet: "+format+"\n", args...)
597 os.Exit(2)
600 // warnf formats the error to standard error, adding program
601 // identification and a newline, but does not exit.
602 func warnf(format string, args ...interface{}) {
603 fmt.Fprintf(os.Stderr, "vet: "+format+"\n", args...)
604 setExit(1)
607 // Println is fmt.Println guarded by -v.
608 func Println(args ...interface{}) {
609 if !*verbose {
610 return
612 fmt.Println(args...)
615 // Printf is fmt.Printf guarded by -v.
616 func Printf(format string, args ...interface{}) {
617 if !*verbose {
618 return
620 fmt.Printf(format+"\n", args...)
623 // Bad reports an error and sets the exit code..
624 func (f *File) Bad(pos token.Pos, args ...interface{}) {
625 f.Warn(pos, args...)
626 setExit(1)
629 // Badf reports a formatted error and sets the exit code.
630 func (f *File) Badf(pos token.Pos, format string, args ...interface{}) {
631 f.Warnf(pos, format, args...)
632 setExit(1)
635 // loc returns a formatted representation of the position.
636 func (f *File) loc(pos token.Pos) string {
637 if pos == token.NoPos {
638 return ""
640 // Do not print columns. Because the pos often points to the start of an
641 // expression instead of the inner part with the actual error, the
642 // precision can mislead.
643 posn := f.fset.Position(pos)
644 return fmt.Sprintf("%s:%d", posn.Filename, posn.Line)
647 // locPrefix returns a formatted representation of the position for use as a line prefix.
648 func (f *File) locPrefix(pos token.Pos) string {
649 if pos == token.NoPos {
650 return ""
652 return fmt.Sprintf("%s: ", f.loc(pos))
655 // Warn reports an error but does not set the exit code.
656 func (f *File) Warn(pos token.Pos, args ...interface{}) {
657 fmt.Fprintf(os.Stderr, "%s%s", f.locPrefix(pos), fmt.Sprintln(args...))
660 // Warnf reports a formatted error but does not set the exit code.
661 func (f *File) Warnf(pos token.Pos, format string, args ...interface{}) {
662 fmt.Fprintf(os.Stderr, "%s%s\n", f.locPrefix(pos), fmt.Sprintf(format, args...))
665 // walkFile walks the file's tree.
666 func (f *File) walkFile(name string, file *ast.File) {
667 Println("Checking file", name)
668 ast.Walk(f, file)
671 // Visit implements the ast.Visitor interface.
672 func (f *File) Visit(node ast.Node) ast.Visitor {
673 f.updateDead(node)
674 var key ast.Node
675 switch node.(type) {
676 case *ast.AssignStmt:
677 key = assignStmt
678 case *ast.BinaryExpr:
679 key = binaryExpr
680 case *ast.CallExpr:
681 key = callExpr
682 case *ast.CompositeLit:
683 key = compositeLit
684 case *ast.ExprStmt:
685 key = exprStmt
686 case *ast.ForStmt:
687 key = forStmt
688 case *ast.FuncDecl:
689 key = funcDecl
690 case *ast.FuncLit:
691 key = funcLit
692 case *ast.GenDecl:
693 key = genDecl
694 case *ast.InterfaceType:
695 key = interfaceType
696 case *ast.RangeStmt:
697 key = rangeStmt
698 case *ast.ReturnStmt:
699 key = returnStmt
700 case *ast.StructType:
701 key = structType
703 for _, fn := range f.checkers[key] {
704 fn(f, node)
706 return f
709 // gofmt returns a string representation of the expression.
710 func (f *File) gofmt(x ast.Expr) string {
711 f.b.Reset()
712 printer.Fprint(&f.b, f.fset, x)
713 return f.b.String()
716 // imported[path][key] is previously written export data.
717 var imported = make(map[string]map[string]interface{})
719 // readVetx reads export data written by a previous
720 // invocation of vet on an imported package (path).
721 // The key is the name passed to registerExport
722 // when the data was originally generated.
723 // readVetx returns nil if the data is unavailable.
724 func readVetx(path, key string) interface{} {
725 if path == "unsafe" || vcfg.ImportPath == "" {
726 return nil
728 m := imported[path]
729 if m == nil {
730 file := vcfg.PackageVetx[path]
731 if file == "" {
732 return nil
734 data, err := ioutil.ReadFile(file)
735 if err != nil {
736 return nil
738 var out []vetxExport
739 err = gob.NewDecoder(bytes.NewReader(data)).Decode(&out)
740 if err != nil {
741 return nil
743 m = make(map[string]interface{})
744 for _, x := range out {
745 m[x.Name] = x.Data
747 imported[path] = m
749 return m[key]