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
{