diff --git a/go.mod b/go.mod index 64f8e6c07292..9f0944f53b94 100644 --- a/go.mod +++ b/go.mod @@ -65,7 +65,7 @@ require ( github.com/spf13/viper v1.0.2 github.com/stretchr/testify v1.2.1 golang.org/x/crypto v0.0.0-20180505025534-4ec37c66abab // indirect - golang.org/x/tools v0.0.0-20180831211245-6c7e314b6563 + golang.org/x/tools v0.0.0-20181107225058-a28dfb48e06b gopkg.in/airbrake/gobrake.v2 v2.0.9 // indirect gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2 // indirect sourcegraph.com/sourcegraph/go-diff v0.0.0-20171119081133-3f415a150aec diff --git a/go.sum b/go.sum index bcababcfa642..d439141aa0de 100644 --- a/go.sum +++ b/go.sum @@ -149,6 +149,8 @@ golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGm golang.org/x/tools v0.0.0-20180826000951-f6ba57429505/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180831211245-6c7e314b6563 h1:O7esB7nCcy+y8pwGEyEYSegt07MF0357oRStgxd2TDo= golang.org/x/tools v0.0.0-20180831211245-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181107225058-a28dfb48e06b h1:r3OgSCBAsWlqM826Ncqr14c4bAHcby43dhAHYrr6F5s= +golang.org/x/tools v0.0.0-20181107225058-a28dfb48e06b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= gopkg.in/airbrake/gobrake.v2 v2.0.9 h1:7z2uVWwn7oVeeugY1DtlPAy5H+KYgB1KeKTnqjNatLo= gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= diff --git a/vendor/golang.org/x/tools/go/ast/astutil/imports.go b/vendor/golang.org/x/tools/go/ast/astutil/imports.go index 04ad6795d921..3e4b195368b3 100644 --- a/vendor/golang.org/x/tools/go/ast/astutil/imports.go +++ b/vendor/golang.org/x/tools/go/ast/astutil/imports.go @@ -14,26 +14,26 @@ import ( ) // AddImport adds the import path to the file f, if absent. -func AddImport(fset *token.FileSet, f *ast.File, ipath string) (added bool) { - return AddNamedImport(fset, f, "", ipath) +func AddImport(fset *token.FileSet, f *ast.File, path string) (added bool) { + return AddNamedImport(fset, f, "", path) } -// AddNamedImport adds the import path to the file f, if absent. +// AddNamedImport adds the import with the given name and path to the file f, if absent. // If name is not empty, it is used to rename the import. // // For example, calling // AddNamedImport(fset, f, "pathpkg", "path") // adds // import pathpkg "path" -func AddNamedImport(fset *token.FileSet, f *ast.File, name, ipath string) (added bool) { - if imports(f, ipath) { +func AddNamedImport(fset *token.FileSet, f *ast.File, name, path string) (added bool) { + if imports(f, name, path) { return false } newImport := &ast.ImportSpec{ Path: &ast.BasicLit{ Kind: token.STRING, - Value: strconv.Quote(ipath), + Value: strconv.Quote(path), }, } if name != "" { @@ -43,14 +43,14 @@ func AddNamedImport(fset *token.FileSet, f *ast.File, name, ipath string) (added // Find an import decl to add to. // The goal is to find an existing import // whose import path has the longest shared - // prefix with ipath. + // prefix with path. var ( bestMatch = -1 // length of longest shared prefix lastImport = -1 // index in f.Decls of the file's final import decl impDecl *ast.GenDecl // import decl containing the best match impIndex = -1 // spec index in impDecl containing the best match - isThirdPartyPath = isThirdParty(ipath) + isThirdPartyPath = isThirdParty(path) ) for i, decl := range f.Decls { gen, ok := decl.(*ast.GenDecl) @@ -81,7 +81,7 @@ func AddNamedImport(fset *token.FileSet, f *ast.File, name, ipath string) (added for j, spec := range gen.Specs { impspec := spec.(*ast.ImportSpec) p := importPath(impspec) - n := matchLen(p, ipath) + n := matchLen(p, path) if n > bestMatch || (bestMatch == 0 && !seenAnyThirdParty && isThirdPartyPath) { bestMatch = n impDecl = gen @@ -197,11 +197,13 @@ func isThirdParty(importPath string) bool { } // DeleteImport deletes the import path from the file f, if present. +// If there are duplicate import declarations, all matching ones are deleted. func DeleteImport(fset *token.FileSet, f *ast.File, path string) (deleted bool) { return DeleteNamedImport(fset, f, "", path) } // DeleteNamedImport deletes the import with the given name and path from the file f, if present. +// If there are duplicate import declarations, all matching ones are deleted. func DeleteNamedImport(fset *token.FileSet, f *ast.File, name, path string) (deleted bool) { var delspecs []*ast.ImportSpec var delcomments []*ast.CommentGroup @@ -216,13 +218,7 @@ func DeleteNamedImport(fset *token.FileSet, f *ast.File, name, path string) (del for j := 0; j < len(gen.Specs); j++ { spec := gen.Specs[j] impspec := spec.(*ast.ImportSpec) - if impspec.Name == nil && name != "" { - continue - } - if impspec.Name != nil && impspec.Name.Name != name { - continue - } - if importPath(impspec) != path { + if importName(impspec) != name || importPath(impspec) != path { continue } @@ -383,9 +379,14 @@ func (fn visitFn) Visit(node ast.Node) ast.Visitor { return fn } -// imports returns true if f imports path. -func imports(f *ast.File, path string) bool { - return importSpec(f, path) != nil +// imports reports whether f has an import with the specified name and path. +func imports(f *ast.File, name, path string) bool { + for _, s := range f.Imports { + if importName(s) == name && importPath(s) == path { + return true + } + } + return false } // importSpec returns the import spec if f imports path, @@ -399,14 +400,23 @@ func importSpec(f *ast.File, path string) *ast.ImportSpec { return nil } +// importName returns the name of s, +// or "" if the import is not named. +func importName(s *ast.ImportSpec) string { + if s.Name == nil { + return "" + } + return s.Name.Name +} + // importPath returns the unquoted import path of s, // or "" if the path is not properly quoted. func importPath(s *ast.ImportSpec) string { t, err := strconv.Unquote(s.Path.Value) - if err == nil { - return t + if err != nil { + return "" } - return "" + return t } // declImports reports whether gen contains an import of path. diff --git a/vendor/golang.org/x/tools/go/packages/golist.go b/vendor/golang.org/x/tools/go/packages/golist.go index 6f336c864f55..dfaeed850213 100644 --- a/vendor/golang.org/x/tools/go/packages/golist.go +++ b/vendor/golang.org/x/tools/go/packages/golist.go @@ -8,19 +8,25 @@ import ( "bytes" "encoding/json" "fmt" + "go/types" "io/ioutil" "log" "os" "os/exec" "path/filepath" + "reflect" "regexp" "strings" "sync" + "time" "golang.org/x/tools/internal/gopathwalk" "golang.org/x/tools/internal/semver" ) +// debug controls verbose logging. +const debug = false + // A goTooOldError reports that the go command // found by exec.LookPath is too old to use the new go list behavior. type goTooOldError struct { @@ -31,6 +37,17 @@ type goTooOldError struct { // the build system package structure. // See driver for more details. func goListDriver(cfg *Config, patterns ...string) (*driverResponse, error) { + var sizes types.Sizes + var sizeserr error + var sizeswg sync.WaitGroup + if cfg.Mode >= LoadTypes { + sizeswg.Add(1) + go func() { + sizes, sizeserr = getSizes(cfg) + sizeswg.Done() + }() + } + // Determine files requested in contains patterns var containFiles []string var packagesNamed []string @@ -66,18 +83,6 @@ extractQueries: } } patterns = restPatterns - // Look for the deprecated contains: syntax. - // TODO(matloob): delete this around mid-October 2018. - restPatterns = restPatterns[:0] - for _, pattern := range patterns { - if strings.HasPrefix(pattern, "contains:") { - containFile := strings.TrimPrefix(pattern, "contains:") - containFiles = append(containFiles, containFile) - } else { - restPatterns = append(restPatterns, pattern) - } - } - containFiles = absJoin(cfg.Dir, containFiles) // TODO(matloob): Remove the definition of listfunc and just use golistPackages once go1.12 is released. var listfunc driver @@ -104,6 +109,13 @@ extractQueries: response = &driverResponse{} } + sizeswg.Wait() + if sizeserr != nil { + return nil, sizeserr + } + // types.SizesFor always returns nil or a *types.StdSizes + response.Sizes, _ = sizes.(*types.StdSizes) + if len(containFiles) == 0 && len(packagesNamed) == 0 { return response, nil } @@ -173,6 +185,10 @@ func runContainsQueries(cfg *Config, driver driver, addPkg func(*Package), queri var modCacheRegexp = regexp.MustCompile(`(.*)@([^/\\]*)(.*)`) func runNamedQueries(cfg *Config, driver driver, addPkg func(*Package), queries []string) ([]string, error) { + // calling `go env` isn't free; bail out if there's nothing to do. + if len(queries) == 0 { + return nil, nil + } // Determine which directories are relevant to scan. roots, modRoot, err := roots(cfg) if err != nil { @@ -209,7 +225,12 @@ func runNamedQueries(cfg *Config, driver driver, addPkg func(*Package), queries } } } - gopathwalk.Walk(roots, add, gopathwalk.Options{ModulesEnabled: modRoot != ""}) + + startWalk := time.Now() + gopathwalk.Walk(roots, add, gopathwalk.Options{ModulesEnabled: modRoot != "", Debug: debug}) + if debug { + log.Printf("%v for walk", time.Since(startWalk)) + } // Weird special case: the top-level package in a module will be in // whatever directory the user checked the repository out into. It's @@ -286,6 +307,12 @@ func runNamedQueries(cfg *Config, driver driver, addPkg func(*Package), queries gomod.WriteString(")\n") tmpCfg := *cfg + + // We're only trying to look at stuff in the module cache, so + // disable the network. This should speed things up, and has + // prevented errors in at least one case, #28518. + tmpCfg.Env = append(append([]string{"GOPROXY=off"}, cfg.Env...)) + var err error tmpCfg.Dir, err = ioutil.TempDir("", "gopackages-modquery") if err != nil { @@ -308,6 +335,18 @@ func runNamedQueries(cfg *Config, driver driver, addPkg func(*Package), queries return results, nil } +func getSizes(cfg *Config) (types.Sizes, error) { + stdout, err := invokeGo(cfg, "env", "GOARCH") + if err != nil { + return nil, err + } + + goarch := strings.TrimSpace(stdout.String()) + // Assume "gc" because SizesFor doesn't respond to other compilers. + // TODO(matloob): add support for gccgo as needed. + return types.SizesFor("gc", goarch), nil +} + // roots selects the appropriate paths to walk based on the passed-in configuration, // particularly the environment and the presence of a go.mod in cfg.Dir's parents. func roots(cfg *Config) ([]gopathwalk.Root, string, error) { @@ -469,6 +508,7 @@ func golistDriverCurrent(cfg *Config, words ...string) (*driverResponse, error) if err != nil { return nil, err } + seen := make(map[string]*jsonPackage) // Decode the JSON and convert it to Package form. var response driverResponse for dec := json.NewDecoder(buf); dec.More(); { @@ -491,6 +531,15 @@ func golistDriverCurrent(cfg *Config, words ...string) (*driverResponse, error) return nil, fmt.Errorf("package missing import path: %+v", p) } + if old, found := seen[p.ImportPath]; found { + if !reflect.DeepEqual(p, old) { + return nil, fmt.Errorf("go list repeated package %v with different values", p.ImportPath) + } + // skip the duplicate + continue + } + seen[p.ImportPath] = p + pkg := &Package{ Name: p.Name, ID: p.ImportPath, @@ -591,6 +640,9 @@ func golistargs(cfg *Config, words []string) []string { // invokeGo returns the stdout of a go command invocation. func invokeGo(cfg *Config, args ...string) (*bytes.Buffer, error) { + if debug { + defer func(start time.Time) { log.Printf("%s for %v", time.Since(start), cmdDebugStr(cfg, args...)) }(time.Now()) + } stdout := new(bytes.Buffer) stderr := new(bytes.Buffer) cmd := exec.CommandContext(cfg.Context, "go", args...) @@ -634,13 +686,24 @@ func invokeGo(cfg *Config, args ...string) (*bytes.Buffer, error) { // be useful for debugging. Print them if $GOPACKAGESPRINTGOLISTERRORS // is set. if len(stderr.Bytes()) != 0 && os.Getenv("GOPACKAGESPRINTGOLISTERRORS") != "" { - fmt.Fprintf(os.Stderr, "go %v stderr: <<%s>>\n", args, stderr) + fmt.Fprintf(os.Stderr, "%s stderr: <<%s>>\n", cmdDebugStr(cfg, args...), stderr) } // debugging if false { - fmt.Fprintf(os.Stderr, "go %v stdout: <<%s>>\n", args, stdout) + fmt.Fprintf(os.Stderr, "%s stdout: <<%s>>\n", cmdDebugStr(cfg, args...), stdout) } return stdout, nil } + +func cmdDebugStr(cfg *Config, args ...string) string { + env := make(map[string]string) + for _, kv := range cfg.Env { + split := strings.Split(kv, "=") + k, v := split[0], split[1] + env[k] = v + } + + return fmt.Sprintf("GOROOT=%v GOPATH=%v GO111MODULE=%v PWD=%v go %v", env["GOROOT"], env["GOPATH"], env["GO111MODULE"], env["PWD"], args) +} diff --git a/vendor/golang.org/x/tools/go/packages/packages.go b/vendor/golang.org/x/tools/go/packages/packages.go index ae70c22b8186..d359a33af1cd 100644 --- a/vendor/golang.org/x/tools/go/packages/packages.go +++ b/vendor/golang.org/x/tools/go/packages/packages.go @@ -19,9 +19,6 @@ import ( "log" "os" "path/filepath" - "runtime" - "sort" - "strings" "sync" "golang.org/x/tools/go/gcexportdata" @@ -140,6 +137,9 @@ type driver func(cfg *Config, patterns ...string) (*driverResponse, error) // driverResponse contains the results for a driver query. type driverResponse struct { + // Sizes, if not nil, is the types.Sizes to use when type checking. + Sizes *types.StdSizes + // Roots is the set of package IDs that make up the root packages. // We have to encode this separately because when we encode a single package // we cannot know if it is one of the roots as that requires knowledge of the @@ -174,7 +174,7 @@ func Load(cfg *Config, patterns ...string) ([]*Package, error) { if err != nil { return nil, err } - sort.Strings(response.Roots) // make all driver responses deterministic + l.sizes = response.Sizes return l.refine(response.Roots, response.Packages...) } @@ -369,6 +369,7 @@ type loaderPackage struct { type loader struct { pkgs map[string]*loaderPackage Config + sizes types.Sizes exportMu sync.Mutex // enforces mutual exclusion of exportdata operations } @@ -413,28 +414,37 @@ func newLoader(cfg *Config) *loader { // refine connects the supplied packages into a graph and then adds type and // and syntax information as requested by the LoadMode. func (ld *loader) refine(roots []string, list ...*Package) ([]*Package, error) { - isRoot := make(map[string]bool, len(roots)) - for _, root := range roots { - isRoot[root] = true + rootMap := make(map[string]int, len(roots)) + for i, root := range roots { + rootMap[root] = i } ld.pkgs = make(map[string]*loaderPackage) // first pass, fixup and build the map and roots - var initial []*loaderPackage + var initial = make([]*loaderPackage, len(roots)) for _, pkg := range list { + rootIndex := -1 + if i, found := rootMap[pkg.ID]; found { + rootIndex = i + } lpkg := &loaderPackage{ Package: pkg, needtypes: ld.Mode >= LoadAllSyntax || - ld.Mode >= LoadTypes && isRoot[pkg.ID], + ld.Mode >= LoadTypes && rootIndex >= 0, needsrc: ld.Mode >= LoadAllSyntax || - ld.Mode >= LoadSyntax && isRoot[pkg.ID] || + ld.Mode >= LoadSyntax && rootIndex >= 0 || pkg.ExportFile == "" && pkg.PkgPath != "unsafe", } ld.pkgs[lpkg.ID] = lpkg - if isRoot[lpkg.ID] { - initial = append(initial, lpkg) + if rootIndex >= 0 { + initial[rootIndex] = lpkg lpkg.initial = true } } + for i, root := range roots { + if initial[i] == nil { + return nil, fmt.Errorf("root package %v is missing", root) + } + } // Materialize the import graph. @@ -685,17 +695,6 @@ func (ld *loader) loadPackage(lpkg *loaderPackage) { panic("unreachable") }) - // This is only an approximation. - // TODO(adonovan): derive Sizes from the underlying build system. - goarch := runtime.GOARCH - const goarchPrefix = "GOARCH=" - for _, e := range ld.Config.Env { - if strings.HasPrefix(e, goarchPrefix) { - goarch = e[len(goarchPrefix):] - } - } - sizes := types.SizesFor("gc", goarch) - // type-check tc := &types.Config{ Importer: importer, @@ -706,7 +705,7 @@ func (ld *loader) loadPackage(lpkg *loaderPackage) { IgnoreFuncBodies: ld.Mode < LoadAllSyntax && !lpkg.initial, Error: appendError, - Sizes: sizes, + Sizes: ld.sizes, } types.NewChecker(tc, ld.Fset, lpkg.Types, lpkg.TypesInfo).Files(lpkg.Syntax) diff --git a/vendor/golang.org/x/tools/go/packages/visit.go b/vendor/golang.org/x/tools/go/packages/visit.go index c1a4b28ca03d..b13cb081fcbe 100644 --- a/vendor/golang.org/x/tools/go/packages/visit.go +++ b/vendor/golang.org/x/tools/go/packages/visit.go @@ -24,7 +24,7 @@ func Visit(pkgs []*Package, pre func(*Package) bool, post func(*Package)) { for path := range pkg.Imports { paths = append(paths, path) } - sort.Strings(paths) // for determinism + sort.Strings(paths) // Imports is a map, this makes visit stable for _, path := range paths { visit(pkg.Imports[path]) } diff --git a/vendor/golang.org/x/tools/internal/gopathwalk/walk.go b/vendor/golang.org/x/tools/internal/gopathwalk/walk.go index dc085fc16084..a561f9f4148e 100644 --- a/vendor/golang.org/x/tools/internal/gopathwalk/walk.go +++ b/vendor/golang.org/x/tools/internal/gopathwalk/walk.go @@ -11,12 +11,13 @@ import ( "bytes" "fmt" "go/build" - "golang.org/x/tools/internal/fastwalk" "io/ioutil" "log" "os" "path/filepath" "strings" + + "golang.org/x/tools/internal/fastwalk" ) // Options controls the behavior of a Walk call. @@ -176,7 +177,9 @@ func (w *walker) walk(path string, typ os.FileMode) error { if typ == os.ModeDir { base := filepath.Base(path) if base == "" || base[0] == '.' || base[0] == '_' || - base == "testdata" || (!w.opts.ModulesEnabled && base == "node_modules") { + base == "testdata" || + (w.root.Type == RootGOROOT && w.opts.ModulesEnabled && base == "vendor") || + (!w.opts.ModulesEnabled && base == "node_modules") { return filepath.SkipDir } fi, err := os.Lstat(path) diff --git a/vendor/modules.txt b/vendor/modules.txt index 97933249bcd4..26bd835157fd 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -193,7 +193,7 @@ golang.org/x/sys/windows golang.org/x/text/width golang.org/x/text/transform golang.org/x/text/unicode/norm -# golang.org/x/tools v0.0.0-20180831211245-6c7e314b6563 +# golang.org/x/tools v0.0.0-20181107225058-a28dfb48e06b golang.org/x/tools/go/loader golang.org/x/tools/go/packages golang.org/x/tools/imports