Commit 4fe42799 authored by Russ Cox's avatar Russ Cox

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: default avatarIan Lance Taylor <iant@golang.org>
Reviewed-by: default avatarRob Pike <r@golang.org>
parent f1fa663b
......@@ -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.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment