]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/vet: accept package config from go command
authorRuss Cox <rsc@golang.org>
Mon, 30 Oct 2017 15:16:09 +0000 (11:16 -0400)
committerRuss Cox <rsc@golang.org>
Wed, 1 Nov 2017 13:44:56 +0000 (13:44 +0000)
This CL adds support for accepting package config from
the go command. Paired with CL 74356 this lets us make
sure vet has complete information about package sources.
This fixes many issues (see CL 74356 for the list), including
mishandling of cgo and vendoring.

Change-Id: Ia4a1dce6f9b1b0a8ef5fdf9005a20a8b294969f1
Reviewed-on: https://go-review.googlesource.com/74355
Run-TryBot: Russ Cox <rsc@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
Reviewed-by: Rob Pike <r@golang.org>
src/cmd/vet/main.go

index f0309cba944c9db8ce16bbf5590adee6ec5ca84d..055deb9b67d7c4c07fecb41de33765f6c7f6ca1c 100644 (file)
@@ -8,14 +8,17 @@ package main
 
 import (
        "bytes"
+       "encoding/json"
        "flag"
        "fmt"
        "go/ast"
        "go/build"
+       "go/importer"
        "go/parser"
        "go/printer"
        "go/token"
        "go/types"
+       "io"
        "io/ioutil"
        "os"
        "path/filepath"
@@ -30,6 +33,8 @@ var (
        source  = flag.Bool("source", false, "import from source instead of compiled object files")
        tags    = flag.String("tags", "", "space-separated list of build tags to apply when parsing")
        tagList = []string{} // exploded version of tags flag; set in main
+
+       mustTypecheck bool
 )
 
 var exitCode = 0
@@ -226,6 +231,18 @@ func main() {
        if flag.NArg() == 0 {
                Usage()
        }
+
+       // Special case for "go vet" passing an explicit configuration:
+       // single argument ending in vet.cfg.
+       // Once we have a more general mechanism for obtaining this
+       // information from build tools like the go command,
+       // vet should be changed to use it. This vet.cfg hack is an
+       // experiment to learn about what form that information should take.
+       if flag.NArg() == 1 && strings.HasSuffix(flag.Arg(0), "vet.cfg") {
+               doPackageCfg(flag.Arg(0))
+               os.Exit(exitCode)
+       }
+
        for _, name := range flag.Args() {
                // Is it a directory?
                fi, err := os.Stat(name)
@@ -266,6 +283,65 @@ func prefixDirectory(directory string, names []string) {
        }
 }
 
+// vetConfig is the JSON config struct prepared by the Go command.
+type vetConfig struct {
+       Compiler    string
+       Dir         string
+       GoFiles     []string
+       ImportMap   map[string]string
+       PackageFile map[string]string
+
+       imp types.Importer
+}
+
+func (v *vetConfig) Import(path string) (*types.Package, error) {
+       if v.imp == nil {
+               v.imp = importer.For(v.Compiler, v.openPackageFile)
+       }
+       if path == "unsafe" {
+               return v.imp.Import("unsafe")
+       }
+       p := v.ImportMap[path]
+       if p == "" {
+               return nil, fmt.Errorf("unknown import path %q", path)
+       }
+       if v.PackageFile[p] == "" {
+               return nil, fmt.Errorf("unknown package file for import %q", path)
+       }
+       return v.imp.Import(p)
+}
+
+func (v *vetConfig) openPackageFile(path string) (io.ReadCloser, error) {
+       file := v.PackageFile[path]
+       if file == "" {
+               // Note that path here has been translated via v.ImportMap,
+               // unlike in the error in Import above. We prefer the error in
+               // Import, but it's worth diagnosing this one too, just in case.
+               return nil, fmt.Errorf("unknown package file for %q", path)
+       }
+       f, err := os.Open(file)
+       if err != nil {
+               return nil, err
+       }
+       return f, nil
+}
+
+// doPackageCfg analyzes a single package described in a config file.
+func doPackageCfg(cfgFile string) {
+       js, err := ioutil.ReadFile(cfgFile)
+       if err != nil {
+               errorf("%v", err)
+       }
+       var vcfg vetConfig
+       if err := json.Unmarshal(js, &vcfg); err != nil {
+               errorf("parsing vet config %s: %v", cfgFile, err)
+       }
+       stdImporter = &vcfg
+       inittypes()
+       mustTypecheck = true
+       doPackage(vcfg.GoFiles, nil)
+}
+
 // doPackageDir analyzes the single package found in the directory, if there is one,
 // plus a test package, if there is one.
 func doPackageDir(directory string) {
@@ -353,6 +429,9 @@ func doPackage(names []string, basePkg *Package) *Package {
        if err != nil {
                // Note that we only report this error when *verbose.
                Println(err)
+               if mustTypecheck {
+                       errorf("%v", err)
+               }
        }
 
        // Check.