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.
34 // Important! If you add flags here, make sure to update cmd/go/internal/vet/vetflag.go.
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
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.
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.
85 func triStateFlag(name string, value triState, usage string) *triState {
86 flag.Var(&value, name, usage)
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{} {
96 func (ts triState) isTrue() bool {
100 func (ts *triState) Set(value string) error {
101 b, err := strconv.ParseBool(value)
114 func (ts *triState) String() string {
117 return "true" // An unset flag will be set by -all, so defaults to true.
126 func (ts triState) IsBoolFlag() bool {
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) {
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
151 funcDecl *ast.FuncDecl
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 {
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
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 {
192 m = make(map[string]func(*File, ast.Node))
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{}) {
218 // Usage is a replacement usage function for the flags package.
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")
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.
239 b bytes.Buffer // for use by methods
241 // Parsed package "foo" when checking package "foo_test"
244 // The keys are the objects that are receivers of a "String()
245 // string" method. The value reports whether the method has a
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
258 objabi.AddVersionFlag()
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] {
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))
281 if flag.NArg() == 0 {
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))
296 for _, name := range flag.Args() {
297 // Is it a directory?
298 fi, err := os.Stat(name)
300 warnf("error walking tree: %s", err)
307 if !strings.HasSuffix(name, "_test.go") {
308 includesNonTest = true
312 if dirsRun && filesRun {
316 for _, name := range flag.Args() {
321 if doPackage(flag.Args(), nil) == nil {
322 warnf("no files checked")
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 {
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
354 func (v *vetConfig) Import(path string) (*types.Package, error) {
356 v.imp = importer.For(v.Compiler, v.openPackageFile)
358 if path == "unsafe" {
359 return v.imp.Import("unsafe")
361 p := v.ImportMap[path]
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]
379 if v.Compiler == "gccgo" && v.Standard[path] {
380 // The importer knows how to handle this.
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)
395 // doPackageCfg analyzes a single package described in a config file.
396 func doPackageCfg(cfgFile string) {
397 js, err := ioutil.ReadFile(cfgFile)
401 if err := json.Unmarshal(js, &vcfg); err != nil {
402 errorf("parsing vet config %s: %v", cfgFile, err)
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{
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
421 if err := gob.NewEncoder(&buf).Encode(out); err != nil {
422 errorf("encoding vet output: %v", err)
425 if err := ioutil.WriteFile(vcfg.VetxOutput, buf.Bytes(), 0666); err != nil {
426 errorf("saving vet output: %v", err)
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)
443 // If it's just that there are no go source files, that's fine.
444 if _, nogo := err.(*build.NoGoError); nogo {
447 // Non-fatal: we are doing a recursive walk and there may be other directories.
448 warnf("cannot process directory %s: %s", directory, err)
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 {
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
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 {
481 var astFiles []*ast.File
482 fs := token.NewFileSet()
483 for _, name := range names {
484 data, err := ioutil.ReadFile(name)
486 // Warn but continue to next package.
487 warnf("%s: %s", name, err)
490 var parsedFile *ast.File
491 if strings.HasSuffix(name, ".go") {
492 parsedFile, err = parser.ParseFile(fs, name, data, parser.ParseComments)
494 warnf("%s: %s", name, err)
497 astFiles = append(astFiles, parsedFile)
504 dead: make(map[ast.Node]bool),
506 files = append(files, file)
508 if len(astFiles) == 0 {
512 pkg.path = astFiles[0].Name.Name
514 // Type check the package.
515 errs := pkg.check(fs, astFiles)
517 if vcfg.SucceedOnTypecheckFailure {
520 if *verbose || mustTypecheck {
521 for _, err := range errs {
522 fmt.Fprintf(os.Stderr, "%v\n", err)
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")
535 for _, file := range files {
537 file.basePkg = basePkg
539 for name, fn := range pkgCheckers {
548 chk := make(map[ast.Node][]func(*File, ast.Node))
549 for typ, set := range checkers {
550 for name, fn := range set {
552 chk[typ] = append(chk[typ], fn)
556 for _, file := range files {
559 if file.file != nil {
560 file.walkFile(file.name, file.file)
566 func visit(path string, f os.FileInfo, err error) error {
568 warnf("walk error: %s", err)
571 // One package per directory. Ignore the files themselves.
579 func (pkg *Package) hasFileWithSuffix(suffix string) bool {
580 for _, f := range pkg.files {
581 if strings.HasSuffix(f.name, suffix) {
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...)
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...)
607 // Println is fmt.Println guarded by -v.
608 func Println(args ...interface{}) {
615 // Printf is fmt.Printf guarded by -v.
616 func Printf(format string, args ...interface{}) {
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{}) {
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...)
635 // loc returns a formatted representation of the position.
636 func (f *File) loc(pos token.Pos) string {
637 if pos == token.NoPos {
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 {
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)
671 // Visit implements the ast.Visitor interface.
672 func (f *File) Visit(node ast.Node) ast.Visitor {
676 case *ast.AssignStmt:
678 case *ast.BinaryExpr:
682 case *ast.CompositeLit:
694 case *ast.InterfaceType:
698 case *ast.ReturnStmt:
700 case *ast.StructType:
703 for _, fn := range f.checkers[key] {
709 // gofmt returns a string representation of the expression.
710 func (f *File) gofmt(x ast.Expr) string {
712 printer.Fprint(&f.b, f.fset, x)
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 == "" {
730 file := vcfg.PackageVetx[path]
734 data, err := ioutil.ReadFile(file)
739 err = gob.NewDecoder(bytes.NewReader(data)).Decode(&out)
743 m = make(map[string]interface{})
744 for _, x := range out {