github.com/hikaru7719/go@v0.0.0-20181025140707-c8b2ac68906a/src/cmd/vet/main.go (about)

     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.
     4  
     5  // Vet is a simple checker for static errors in Go source code.
     6  // See doc.go for more information.
     7  
     8  package main
     9  
    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  	"log"
    26  	"os"
    27  	"path/filepath"
    28  	"sort"
    29  	"strconv"
    30  	"strings"
    31  
    32  	"cmd/internal/objabi"
    33  )
    34  
    35  var (
    36  	verbose = flag.Bool("v", false, "verbose")
    37  	flags   = flag.Bool("flags", false, "print flags in JSON")
    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
    41  
    42  	vcfg          vetConfig
    43  	mustTypecheck bool
    44  )
    45  
    46  var exitCode = 0
    47  
    48  // "-all" flag enables all non-experimental checks
    49  var all = triStateFlag("all", unset, "enable all non-experimental checks")
    50  
    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"),
    57  }
    58  
    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{}
    62  
    63  // setTrueCount record how many flags are explicitly set to true.
    64  var setTrueCount int
    65  
    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
    69  
    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
    73  
    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
    78  
    79  const (
    80  	unset triState = iota
    81  	setTrue
    82  	setFalse
    83  )
    84  
    85  func triStateFlag(name string, value triState, usage string) *triState {
    86  	flag.Var(&value, name, usage)
    87  	return &value
    88  }
    89  
    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
    94  }
    95  
    96  func (ts triState) isTrue() bool {
    97  	return ts == setTrue
    98  }
    99  
   100  func (ts *triState) Set(value string) error {
   101  	b, err := strconv.ParseBool(value)
   102  	if err != nil {
   103  		return err
   104  	}
   105  	if b {
   106  		*ts = setTrue
   107  		setTrueCount++
   108  	} else {
   109  		*ts = setFalse
   110  	}
   111  	return nil
   112  }
   113  
   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"
   122  	}
   123  	panic("not reached")
   124  }
   125  
   126  func (ts triState) IsBoolFlag() bool {
   127  	return true
   128  }
   129  
   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()
   133  }
   134  
   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
   140  	}
   141  }
   142  
   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
   158  
   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{})
   165  )
   166  
   167  // The exporters data as written to the vetx output file.
   168  type vetxExport struct {
   169  	Name string
   170  	Data interface{}
   171  }
   172  
   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.
   183  
   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
   194  		}
   195  		m[name] = fn
   196  	}
   197  }
   198  
   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
   207  }
   208  
   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
   216  }
   217  
   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)
   229  }
   230  
   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
   240  
   241  	// Parsed package "foo" when checking package "foo_test"
   242  	basePkg *Package
   243  
   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
   249  
   250  	// Registered checkers to run.
   251  	checkers map[ast.Node][]func(*File, ast.Node)
   252  
   253  	// Unreachable nodes; can be ignored in shift check.
   254  	dead map[ast.Node]bool
   255  }
   256  
   257  func main() {
   258  	objabi.AddVersionFlag()
   259  	flag.Usage = Usage
   260  	flag.Parse()
   261  
   262  	// -flags: print flags as JSON. Used by go vet.
   263  	if *flags {
   264  		type jsonFlag struct {
   265  			Name  string
   266  			Bool  bool
   267  			Usage string
   268  		}
   269  		var jsonFlags []jsonFlag
   270  		flag.VisitAll(func(f *flag.Flag) {
   271  			isBool := false
   272  			switch v := f.Value.(type) {
   273  			case interface{ BoolFlag() bool }:
   274  				isBool = v.BoolFlag()
   275  			case *triState:
   276  				isBool = true // go vet should treat it as boolean
   277  			}
   278  			jsonFlags = append(jsonFlags, jsonFlag{f.Name, isBool, f.Usage})
   279  		})
   280  		data, err := json.MarshalIndent(jsonFlags, "", "\t")
   281  		if err != nil {
   282  			log.Fatal(err)
   283  		}
   284  		os.Stdout.Write(data)
   285  		os.Exit(0)
   286  	}
   287  
   288  	// If any flag is set, we run only those checks requested.
   289  	// If all flag is set true or if no flags are set true, set all the non-experimental ones
   290  	// not explicitly set (in effect, set the "-all" flag).
   291  	if setTrueCount == 0 || *all == setTrue {
   292  		for name, setting := range report {
   293  			if *setting == unset && !experimental[name] {
   294  				*setting = setTrue
   295  			}
   296  		}
   297  	}
   298  
   299  	// Accept space-separated tags because that matches
   300  	// the go command's other subcommands.
   301  	// Accept commas because go tool vet traditionally has.
   302  	tagList = strings.Fields(strings.ReplaceAll(*tags, ",", " "))
   303  
   304  	initPrintFlags()
   305  	initUnusedFlags()
   306  
   307  	if flag.NArg() == 0 {
   308  		Usage()
   309  	}
   310  
   311  	// Special case for "go vet" passing an explicit configuration:
   312  	// single argument ending in vet.cfg.
   313  	// Once we have a more general mechanism for obtaining this
   314  	// information from build tools like the go command,
   315  	// vet should be changed to use it. This vet.cfg hack is an
   316  	// experiment to learn about what form that information should take.
   317  	if flag.NArg() == 1 && strings.HasSuffix(flag.Arg(0), "vet.cfg") {
   318  		doPackageCfg(flag.Arg(0))
   319  		os.Exit(exitCode)
   320  	}
   321  
   322  	for _, name := range flag.Args() {
   323  		// Is it a directory?
   324  		fi, err := os.Stat(name)
   325  		if err != nil {
   326  			warnf("error walking tree: %s", err)
   327  			continue
   328  		}
   329  		if fi.IsDir() {
   330  			dirsRun = true
   331  		} else {
   332  			filesRun = true
   333  			if !strings.HasSuffix(name, "_test.go") {
   334  				includesNonTest = true
   335  			}
   336  		}
   337  	}
   338  	if dirsRun && filesRun {
   339  		Usage()
   340  	}
   341  	if dirsRun {
   342  		for _, name := range flag.Args() {
   343  			walkDir(name)
   344  		}
   345  		os.Exit(exitCode)
   346  	}
   347  	if doPackage(flag.Args(), nil) == nil {
   348  		warnf("no files checked")
   349  	}
   350  	os.Exit(exitCode)
   351  }
   352  
   353  // prefixDirectory places the directory name on the beginning of each name in the list.
   354  func prefixDirectory(directory string, names []string) {
   355  	if directory != "." {
   356  		for i, name := range names {
   357  			names[i] = filepath.Join(directory, name)
   358  		}
   359  	}
   360  }
   361  
   362  // vetConfig is the JSON config struct prepared by the Go command.
   363  type vetConfig struct {
   364  	Compiler    string
   365  	Dir         string
   366  	ImportPath  string
   367  	GoFiles     []string
   368  	ImportMap   map[string]string
   369  	PackageFile map[string]string
   370  	Standard    map[string]bool
   371  	PackageVetx map[string]string // map from import path to vetx data file
   372  	VetxOnly    bool              // only compute vetx output; don't run ordinary checks
   373  	VetxOutput  string            // file where vetx output should be written
   374  
   375  	SucceedOnTypecheckFailure bool
   376  
   377  	imp types.Importer
   378  }
   379  
   380  func (v *vetConfig) Import(path string) (*types.Package, error) {
   381  	if v.imp == nil {
   382  		v.imp = importer.For(v.Compiler, v.openPackageFile)
   383  	}
   384  	if path == "unsafe" {
   385  		return v.imp.Import("unsafe")
   386  	}
   387  	p := v.ImportMap[path]
   388  	if p == "" {
   389  		return nil, fmt.Errorf("unknown import path %q", path)
   390  	}
   391  	if v.PackageFile[p] == "" {
   392  		if v.Compiler == "gccgo" && v.Standard[path] {
   393  			// gccgo doesn't have sources for standard library packages,
   394  			// but the importer will do the right thing.
   395  			return v.imp.Import(path)
   396  		}
   397  		return nil, fmt.Errorf("unknown package file for import %q", path)
   398  	}
   399  	return v.imp.Import(p)
   400  }
   401  
   402  func (v *vetConfig) openPackageFile(path string) (io.ReadCloser, error) {
   403  	file := v.PackageFile[path]
   404  	if file == "" {
   405  		if v.Compiler == "gccgo" && v.Standard[path] {
   406  			// The importer knows how to handle this.
   407  			return nil, nil
   408  		}
   409  		// Note that path here has been translated via v.ImportMap,
   410  		// unlike in the error in Import above. We prefer the error in
   411  		// Import, but it's worth diagnosing this one too, just in case.
   412  		return nil, fmt.Errorf("unknown package file for %q", path)
   413  	}
   414  	f, err := os.Open(file)
   415  	if err != nil {
   416  		return nil, err
   417  	}
   418  	return f, nil
   419  }
   420  
   421  // doPackageCfg analyzes a single package described in a config file.
   422  func doPackageCfg(cfgFile string) {
   423  	js, err := ioutil.ReadFile(cfgFile)
   424  	if err != nil {
   425  		errorf("%v", err)
   426  	}
   427  	if err := json.Unmarshal(js, &vcfg); err != nil {
   428  		errorf("parsing vet config %s: %v", cfgFile, err)
   429  	}
   430  	stdImporter = &vcfg
   431  	inittypes()
   432  	mustTypecheck = true
   433  	doPackage(vcfg.GoFiles, nil)
   434  	if vcfg.VetxOutput != "" {
   435  		out := make([]vetxExport, 0, len(exporters))
   436  		for name, fn := range exporters {
   437  			out = append(out, vetxExport{
   438  				Name: name,
   439  				Data: fn(),
   440  			})
   441  		}
   442  		// Sort the data so that it is consistent across builds.
   443  		sort.Slice(out, func(i, j int) bool {
   444  			return out[i].Name < out[j].Name
   445  		})
   446  		var buf bytes.Buffer
   447  		if err := gob.NewEncoder(&buf).Encode(out); err != nil {
   448  			errorf("encoding vet output: %v", err)
   449  			return
   450  		}
   451  		if err := ioutil.WriteFile(vcfg.VetxOutput, buf.Bytes(), 0666); err != nil {
   452  			errorf("saving vet output: %v", err)
   453  			return
   454  		}
   455  	}
   456  }
   457  
   458  // doPackageDir analyzes the single package found in the directory, if there is one,
   459  // plus a test package, if there is one.
   460  func doPackageDir(directory string) {
   461  	context := build.Default
   462  	if len(context.BuildTags) != 0 {
   463  		warnf("build tags %s previously set", context.BuildTags)
   464  	}
   465  	context.BuildTags = append(tagList, context.BuildTags...)
   466  
   467  	pkg, err := context.ImportDir(directory, 0)
   468  	if err != nil {
   469  		// If it's just that there are no go source files, that's fine.
   470  		if _, nogo := err.(*build.NoGoError); nogo {
   471  			return
   472  		}
   473  		// Non-fatal: we are doing a recursive walk and there may be other directories.
   474  		warnf("cannot process directory %s: %s", directory, err)
   475  		return
   476  	}
   477  	var names []string
   478  	names = append(names, pkg.GoFiles...)
   479  	names = append(names, pkg.CgoFiles...)
   480  	names = append(names, pkg.TestGoFiles...) // These are also in the "foo" package.
   481  	names = append(names, pkg.SFiles...)
   482  	prefixDirectory(directory, names)
   483  	basePkg := doPackage(names, nil)
   484  	// Is there also a "foo_test" package? If so, do that one as well.
   485  	if len(pkg.XTestGoFiles) > 0 {
   486  		names = pkg.XTestGoFiles
   487  		prefixDirectory(directory, names)
   488  		doPackage(names, basePkg)
   489  	}
   490  }
   491  
   492  type Package struct {
   493  	path      string
   494  	defs      map[*ast.Ident]types.Object
   495  	uses      map[*ast.Ident]types.Object
   496  	implicits map[ast.Node]types.Object
   497  	selectors map[*ast.SelectorExpr]*types.Selection
   498  	types     map[ast.Expr]types.TypeAndValue
   499  	spans     map[types.Object]Span
   500  	files     []*File
   501  	typesPkg  *types.Package
   502  }
   503  
   504  // doPackage analyzes the single package constructed from the named files.
   505  // It returns the parsed Package or nil if none of the files have been checked.
   506  func doPackage(names []string, basePkg *Package) *Package {
   507  	var files []*File
   508  	var astFiles []*ast.File
   509  	fs := token.NewFileSet()
   510  	for _, name := range names {
   511  		data, err := ioutil.ReadFile(name)
   512  		if err != nil {
   513  			// Warn but continue to next package.
   514  			warnf("%s: %s", name, err)
   515  			return nil
   516  		}
   517  		var parsedFile *ast.File
   518  		if strings.HasSuffix(name, ".go") {
   519  			parsedFile, err = parser.ParseFile(fs, name, data, parser.ParseComments)
   520  			if err != nil {
   521  				warnf("%s: %s", name, err)
   522  				return nil
   523  			}
   524  			astFiles = append(astFiles, parsedFile)
   525  		}
   526  		file := &File{
   527  			fset:    fs,
   528  			content: data,
   529  			name:    name,
   530  			file:    parsedFile,
   531  			dead:    make(map[ast.Node]bool),
   532  		}
   533  		files = append(files, file)
   534  	}
   535  	if len(astFiles) == 0 {
   536  		return nil
   537  	}
   538  	pkg := new(Package)
   539  	pkg.path = astFiles[0].Name.Name
   540  	pkg.files = files
   541  	// Type check the package.
   542  	errs := pkg.check(fs, astFiles)
   543  	if errs != nil {
   544  		if vcfg.SucceedOnTypecheckFailure {
   545  			os.Exit(0)
   546  		}
   547  		if *verbose || mustTypecheck {
   548  			for _, err := range errs {
   549  				fmt.Fprintf(os.Stderr, "%v\n", err)
   550  			}
   551  			if mustTypecheck {
   552  				// This message could be silenced, and we could just exit,
   553  				// but it might be helpful at least at first to make clear that the
   554  				// above errors are coming from vet and not the compiler
   555  				// (they often look like compiler errors, such as "declared but not used").
   556  				errorf("typecheck failures")
   557  			}
   558  		}
   559  	}
   560  
   561  	// Check.
   562  	for _, file := range files {
   563  		file.pkg = pkg
   564  		file.basePkg = basePkg
   565  	}
   566  	for name, fn := range pkgCheckers {
   567  		if vet(name) {
   568  			fn(pkg)
   569  		}
   570  	}
   571  	if vcfg.VetxOnly {
   572  		return pkg
   573  	}
   574  
   575  	chk := make(map[ast.Node][]func(*File, ast.Node))
   576  	for typ, set := range checkers {
   577  		for name, fn := range set {
   578  			if vet(name) {
   579  				chk[typ] = append(chk[typ], fn)
   580  			}
   581  		}
   582  	}
   583  	for _, file := range files {
   584  		checkBuildTag(file)
   585  		file.checkers = chk
   586  		if file.file != nil {
   587  			file.walkFile(file.name, file.file)
   588  		}
   589  	}
   590  	return pkg
   591  }
   592  
   593  func visit(path string, f os.FileInfo, err error) error {
   594  	if err != nil {
   595  		warnf("walk error: %s", err)
   596  		return err
   597  	}
   598  	// One package per directory. Ignore the files themselves.
   599  	if !f.IsDir() {
   600  		return nil
   601  	}
   602  	doPackageDir(path)
   603  	return nil
   604  }
   605  
   606  func (pkg *Package) hasFileWithSuffix(suffix string) bool {
   607  	for _, f := range pkg.files {
   608  		if strings.HasSuffix(f.name, suffix) {
   609  			return true
   610  		}
   611  	}
   612  	return false
   613  }
   614  
   615  // walkDir recursively walks the tree looking for Go packages.
   616  func walkDir(root string) {
   617  	filepath.Walk(root, visit)
   618  }
   619  
   620  // errorf formats the error to standard error, adding program
   621  // identification and a newline, and exits.
   622  func errorf(format string, args ...interface{}) {
   623  	fmt.Fprintf(os.Stderr, "vet: "+format+"\n", args...)
   624  	os.Exit(2)
   625  }
   626  
   627  // warnf formats the error to standard error, adding program
   628  // identification and a newline, but does not exit.
   629  func warnf(format string, args ...interface{}) {
   630  	fmt.Fprintf(os.Stderr, "vet: "+format+"\n", args...)
   631  	setExit(1)
   632  }
   633  
   634  // Println is fmt.Println guarded by -v.
   635  func Println(args ...interface{}) {
   636  	if !*verbose {
   637  		return
   638  	}
   639  	fmt.Println(args...)
   640  }
   641  
   642  // Printf is fmt.Printf guarded by -v.
   643  func Printf(format string, args ...interface{}) {
   644  	if !*verbose {
   645  		return
   646  	}
   647  	fmt.Printf(format+"\n", args...)
   648  }
   649  
   650  // Bad reports an error and sets the exit code..
   651  func (f *File) Bad(pos token.Pos, args ...interface{}) {
   652  	f.Warn(pos, args...)
   653  	setExit(1)
   654  }
   655  
   656  // Badf reports a formatted error and sets the exit code.
   657  func (f *File) Badf(pos token.Pos, format string, args ...interface{}) {
   658  	f.Warnf(pos, format, args...)
   659  	setExit(1)
   660  }
   661  
   662  // loc returns a formatted representation of the position.
   663  func (f *File) loc(pos token.Pos) string {
   664  	if pos == token.NoPos {
   665  		return ""
   666  	}
   667  	// Do not print columns. Because the pos often points to the start of an
   668  	// expression instead of the inner part with the actual error, the
   669  	// precision can mislead.
   670  	posn := f.fset.Position(pos)
   671  	return fmt.Sprintf("%s:%d", posn.Filename, posn.Line)
   672  }
   673  
   674  // locPrefix returns a formatted representation of the position for use as a line prefix.
   675  func (f *File) locPrefix(pos token.Pos) string {
   676  	if pos == token.NoPos {
   677  		return ""
   678  	}
   679  	return fmt.Sprintf("%s: ", f.loc(pos))
   680  }
   681  
   682  // Warn reports an error but does not set the exit code.
   683  func (f *File) Warn(pos token.Pos, args ...interface{}) {
   684  	fmt.Fprintf(os.Stderr, "%s%s", f.locPrefix(pos), fmt.Sprintln(args...))
   685  }
   686  
   687  // Warnf reports a formatted error but does not set the exit code.
   688  func (f *File) Warnf(pos token.Pos, format string, args ...interface{}) {
   689  	fmt.Fprintf(os.Stderr, "%s%s\n", f.locPrefix(pos), fmt.Sprintf(format, args...))
   690  }
   691  
   692  // walkFile walks the file's tree.
   693  func (f *File) walkFile(name string, file *ast.File) {
   694  	Println("Checking file", name)
   695  	ast.Walk(f, file)
   696  }
   697  
   698  // Visit implements the ast.Visitor interface.
   699  func (f *File) Visit(node ast.Node) ast.Visitor {
   700  	f.updateDead(node)
   701  	var key ast.Node
   702  	switch node.(type) {
   703  	case *ast.AssignStmt:
   704  		key = assignStmt
   705  	case *ast.BinaryExpr:
   706  		key = binaryExpr
   707  	case *ast.CallExpr:
   708  		key = callExpr
   709  	case *ast.CompositeLit:
   710  		key = compositeLit
   711  	case *ast.ExprStmt:
   712  		key = exprStmt
   713  	case *ast.ForStmt:
   714  		key = forStmt
   715  	case *ast.FuncDecl:
   716  		key = funcDecl
   717  	case *ast.FuncLit:
   718  		key = funcLit
   719  	case *ast.GenDecl:
   720  		key = genDecl
   721  	case *ast.InterfaceType:
   722  		key = interfaceType
   723  	case *ast.RangeStmt:
   724  		key = rangeStmt
   725  	case *ast.ReturnStmt:
   726  		key = returnStmt
   727  	case *ast.StructType:
   728  		key = structType
   729  	}
   730  	for _, fn := range f.checkers[key] {
   731  		fn(f, node)
   732  	}
   733  	return f
   734  }
   735  
   736  // gofmt returns a string representation of the expression.
   737  func (f *File) gofmt(x ast.Expr) string {
   738  	f.b.Reset()
   739  	printer.Fprint(&f.b, f.fset, x)
   740  	return f.b.String()
   741  }
   742  
   743  // imported[path][key] is previously written export data.
   744  var imported = make(map[string]map[string]interface{})
   745  
   746  // readVetx reads export data written by a previous
   747  // invocation of vet on an imported package (path).
   748  // The key is the name passed to registerExport
   749  // when the data was originally generated.
   750  // readVetx returns nil if the data is unavailable.
   751  func readVetx(path, key string) interface{} {
   752  	if path == "unsafe" || vcfg.ImportPath == "" {
   753  		return nil
   754  	}
   755  	m := imported[path]
   756  	if m == nil {
   757  		file := vcfg.PackageVetx[path]
   758  		if file == "" {
   759  			return nil
   760  		}
   761  		data, err := ioutil.ReadFile(file)
   762  		if err != nil {
   763  			return nil
   764  		}
   765  		var out []vetxExport
   766  		err = gob.NewDecoder(bytes.NewReader(data)).Decode(&out)
   767  		if err != nil {
   768  			return nil
   769  		}
   770  		m = make(map[string]interface{})
   771  		for _, x := range out {
   772  			m[x.Name] = x.Data
   773  		}
   774  		imported[path] = m
   775  	}
   776  	return m[key]
   777  }