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"
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
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)
}
}
+// 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) {
if err != nil {
// Note that we only report this error when *verbose.
Println(err)
+ if mustTypecheck {
+ errorf("%v", err)
+ }
}
// Check.