1
0
mirror of https://github.com/golang/go synced 2024-11-19 17:04:41 -07:00

cmd/vet: accept package config from go command

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>
This commit is contained in:
Russ Cox 2017-10-30 11:16:09 -04:00
parent f1fa663b6d
commit 4fe42799a8

View 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.