diff options
Diffstat (limited to 'libgo/go/cmd/vet/main.go')
-rw-r--r-- | libgo/go/cmd/vet/main.go | 141 |
1 files changed, 131 insertions, 10 deletions
diff --git a/libgo/go/cmd/vet/main.go b/libgo/go/cmd/vet/main.go index 49c1d32f13d..c50d4885a07 100644 --- a/libgo/go/cmd/vet/main.go +++ b/libgo/go/cmd/vet/main.go @@ -4,10 +4,12 @@ // Vet is a simple checker for static errors in Go source code. // See doc.go for more information. + package main import ( "bytes" + "encoding/gob" "encoding/json" "flag" "fmt" @@ -22,8 +24,11 @@ import ( "io/ioutil" "os" "path/filepath" + "sort" "strconv" "strings" + + "cmd/internal/objabi" ) // Important! If you add flags here, make sure to update cmd/go/internal/vet/vetflag.go. @@ -154,9 +159,31 @@ var ( // checkers is a two-level map. // The outer level is keyed by a nil pointer, one of the AST vars above. // The inner level is keyed by checker name. - checkers = make(map[ast.Node]map[string]func(*File, ast.Node)) + checkers = make(map[ast.Node]map[string]func(*File, ast.Node)) + pkgCheckers = make(map[string]func(*Package)) + exporters = make(map[string]func() interface{}) ) +// The exporters data as written to the vetx output file. +type vetxExport struct { + Name string + Data interface{} +} + +// Vet can provide its own "export information" +// about package A to future invocations of vet +// on packages importing A. If B imports A, +// then running "go vet B" actually invokes vet twice: +// first, it runs vet on A, in "vetx-only" mode, which +// skips most checks and only computes export data +// describing A. Then it runs vet on B, making A's vetx +// data available for consultation. The vet of B +// computes vetx data for B in addition to its +// usual vet checks. + +// register registers the named check function, +// to be called with AST nodes of the given types. +// The registered functions are not called in vetx-only mode. func register(name, usage string, fn func(*File, ast.Node), types ...ast.Node) { report[name] = triStateFlag(name, unset, usage) for _, typ := range types { @@ -169,6 +196,25 @@ func register(name, usage string, fn func(*File, ast.Node), types ...ast.Node) { } } +// registerPkgCheck registers a package-level checking function, +// to be invoked with the whole package being vetted +// before any of the per-node handlers. +// The registered function fn is called even in vetx-only mode +// (see comment above), so fn must take care not to report +// errors when vcfg.VetxOnly is true. +func registerPkgCheck(name string, fn func(*Package)) { + pkgCheckers[name] = fn +} + +// registerExport registers a function to return vetx export data +// that should be saved and provided to future invocations of vet +// when checking packages importing this one. +// The value returned by fn should be nil or else valid to encode using gob. +// Typically a registerExport call is paired with a call to gob.Register. +func registerExport(name string, fn func() interface{}) { + exporters[name] = fn +} + // Usage is a replacement usage function for the flags package. func Usage() { fmt.Fprintf(os.Stderr, "Usage of vet:\n") @@ -195,9 +241,11 @@ type File struct { // Parsed package "foo" when checking package "foo_test" basePkg *Package - // The objects that are receivers of a "String() string" method. + // The keys are the objects that are receivers of a "String() + // string" method. The value reports whether the method has a + // pointer receiver. // This is used by the recursiveStringer method in print.go. - stringers map[*ast.Object]bool + stringerPtrs map[*ast.Object]bool // Registered checkers to run. checkers map[ast.Node][]func(*File, ast.Node) @@ -207,6 +255,7 @@ type File struct { } func main() { + objabi.AddVersionFlag() flag.Usage = Usage flag.Parse() @@ -293,6 +342,9 @@ type vetConfig struct { ImportMap map[string]string PackageFile map[string]string Standard map[string]bool + PackageVetx map[string]string // map from import path to vetx data file + VetxOnly bool // only compute vetx output; don't run ordinary checks + VetxOutput string // file where vetx output should be written SucceedOnTypecheckFailure bool @@ -353,6 +405,28 @@ func doPackageCfg(cfgFile string) { inittypes() mustTypecheck = true doPackage(vcfg.GoFiles, nil) + if vcfg.VetxOutput != "" { + out := make([]vetxExport, 0, len(exporters)) + for name, fn := range exporters { + out = append(out, vetxExport{ + Name: name, + Data: fn(), + }) + } + // Sort the data so that it is consistent across builds. + sort.Slice(out, func(i, j int) bool { + return out[i].Name < out[j].Name + }) + var buf bytes.Buffer + if err := gob.NewEncoder(&buf).Encode(out); err != nil { + errorf("encoding vet output: %v", err) + return + } + if err := ioutil.WriteFile(vcfg.VetxOutput, buf.Bytes(), 0666); err != nil { + errorf("saving vet output: %v", err) + return + } + } } // doPackageDir analyzes the single package found in the directory, if there is one, @@ -413,23 +487,23 @@ func doPackage(names []string, basePkg *Package) *Package { warnf("%s: %s", name, err) return nil } - checkBuildTag(name, data) var parsedFile *ast.File if strings.HasSuffix(name, ".go") { - parsedFile, err = parser.ParseFile(fs, name, data, 0) + parsedFile, err = parser.ParseFile(fs, name, data, parser.ParseComments) if err != nil { warnf("%s: %s", name, err) return nil } astFiles = append(astFiles, parsedFile) } - files = append(files, &File{ + file := &File{ fset: fs, content: data, name: name, file: parsedFile, dead: make(map[ast.Node]bool), - }) + } + files = append(files, file) } if len(astFiles) == 0 { return nil @@ -458,6 +532,19 @@ func doPackage(names []string, basePkg *Package) *Package { } // Check. + for _, file := range files { + file.pkg = pkg + file.basePkg = basePkg + } + for name, fn := range pkgCheckers { + if vet(name) { + fn(pkg) + } + } + if vcfg.VetxOnly { + return pkg + } + chk := make(map[ast.Node][]func(*File, ast.Node)) for typ, set := range checkers { for name, fn := range set { @@ -467,14 +554,12 @@ func doPackage(names []string, basePkg *Package) *Package { } } for _, file := range files { - file.pkg = pkg - file.basePkg = basePkg + checkBuildTag(file) file.checkers = chk if file.file != nil { file.walkFile(file.name, file.file) } } - asmCheck(pkg) return pkg } @@ -627,3 +712,39 @@ func (f *File) gofmt(x ast.Expr) string { printer.Fprint(&f.b, f.fset, x) return f.b.String() } + +// imported[path][key] is previously written export data. +var imported = make(map[string]map[string]interface{}) + +// readVetx reads export data written by a previous +// invocation of vet on an imported package (path). +// The key is the name passed to registerExport +// when the data was originally generated. +// readVetx returns nil if the data is unavailable. +func readVetx(path, key string) interface{} { + if path == "unsafe" || vcfg.ImportPath == "" { + return nil + } + m := imported[path] + if m == nil { + file := vcfg.PackageVetx[path] + if file == "" { + return nil + } + data, err := ioutil.ReadFile(file) + if err != nil { + return nil + } + var out []vetxExport + err = gob.NewDecoder(bytes.NewReader(data)).Decode(&out) + if err != nil { + return nil + } + m = make(map[string]interface{}) + for _, x := range out { + m[x.Name] = x.Data + } + imported[path] = m + } + return m[key] +} |