aboutsummaryrefslogtreecommitdiffstats
path: root/vendor/golang.org/x/tools/go
diff options
context:
space:
mode:
authorLunny Xiao <xiaolunwen@gmail.com>2020-04-04 03:29:12 +0800
committerGitHub <noreply@github.com>2020-04-03 22:29:12 +0300
commit4f63f283c47dcf9e705ce5b8e8857f2b42cff8ad (patch)
treedd5dc2cae6ebae21826ffcce937533559be45a07 /vendor/golang.org/x/tools/go
parent4af7c47b38d382d105726f9553a1a68d46882cbf (diff)
downloadgitea-4f63f283c47dcf9e705ce5b8e8857f2b42cff8ad.tar.gz
gitea-4f63f283c47dcf9e705ce5b8e8857f2b42cff8ad.zip
Rename scripts to build and add revive command as a new build tool command (#10942)
Co-authored-by: techknowlogick <techknowlogick@gitea.io>
Diffstat (limited to 'vendor/golang.org/x/tools/go')
-rw-r--r--vendor/golang.org/x/tools/go/ast/astutil/imports.go5
-rw-r--r--vendor/golang.org/x/tools/go/internal/cgo/cgo.go6
-rw-r--r--vendor/golang.org/x/tools/go/internal/gcimporter/gcimporter.go8
-rw-r--r--vendor/golang.org/x/tools/go/internal/packagesdriver/sizes.go102
-rw-r--r--vendor/golang.org/x/tools/go/packages/golist.go692
-rw-r--r--vendor/golang.org/x/tools/go/packages/golist_overlay.go201
-rw-r--r--vendor/golang.org/x/tools/go/packages/packages.go44
7 files changed, 459 insertions, 599 deletions
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 3e4b195368..2087ceec9c 100644
--- a/vendor/golang.org/x/tools/go/ast/astutil/imports.go
+++ b/vendor/golang.org/x/tools/go/ast/astutil/imports.go
@@ -275,9 +275,10 @@ func DeleteNamedImport(fset *token.FileSet, f *ast.File, name, path string) (del
// We deleted an entry but now there may be
// a blank line-sized hole where the import was.
- if line-lastLine > 1 {
+ if line-lastLine > 1 || !gen.Rparen.IsValid() {
// There was a blank line immediately preceding the deleted import,
- // so there's no need to close the hole.
+ // so there's no need to close the hole. The right parenthesis is
+ // invalid after AddImport to an import statement without parenthesis.
// Do nothing.
} else if line != fset.File(gen.Rparen).LineCount() {
// There was no blank line. Close the hole.
diff --git a/vendor/golang.org/x/tools/go/internal/cgo/cgo.go b/vendor/golang.org/x/tools/go/internal/cgo/cgo.go
index 0f652ea6fb..5db8b30967 100644
--- a/vendor/golang.org/x/tools/go/internal/cgo/cgo.go
+++ b/vendor/golang.org/x/tools/go/internal/cgo/cgo.go
@@ -2,9 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-package cgo
-
-// This file handles cgo preprocessing of files containing `import "C"`.
+// Package cgo handles cgo preprocessing of files containing `import "C"`.
//
// DESIGN
//
@@ -51,6 +49,8 @@ package cgo
// its handling of function calls, analogous to the treatment of map
// lookups in which y=m[k] and y,ok=m[k] are both legal.
+package cgo
+
import (
"fmt"
"go/ast"
diff --git a/vendor/golang.org/x/tools/go/internal/gcimporter/gcimporter.go b/vendor/golang.org/x/tools/go/internal/gcimporter/gcimporter.go
index 9cf186605f..8dcd8bbb71 100644
--- a/vendor/golang.org/x/tools/go/internal/gcimporter/gcimporter.go
+++ b/vendor/golang.org/x/tools/go/internal/gcimporter/gcimporter.go
@@ -344,7 +344,7 @@ func (p *parser) expectKeyword(keyword string) {
// PackageId = string_lit .
//
-func (p *parser) parsePackageId() string {
+func (p *parser) parsePackageID() string {
id, err := strconv.Unquote(p.expect(scanner.String))
if err != nil {
p.error(err)
@@ -384,7 +384,7 @@ func (p *parser) parseDotIdent() string {
//
func (p *parser) parseQualifiedName() (id, name string) {
p.expect('@')
- id = p.parsePackageId()
+ id = p.parsePackageID()
p.expect('.')
// Per rev f280b8a485fd (10/2/2013), qualified names may be used for anonymous fields.
if p.tok == '?' {
@@ -696,7 +696,7 @@ func (p *parser) parseInterfaceType(parent *types.Package) types.Type {
// Complete requires the type's embedded interfaces to be fully defined,
// but we do not define any
- return types.NewInterface(methods, nil).Complete()
+ return newInterface(methods, nil).Complete()
}
// ChanType = ( "chan" [ "<-" ] | "<-" "chan" ) Type .
@@ -785,7 +785,7 @@ func (p *parser) parseType(parent *types.Package) types.Type {
func (p *parser) parseImportDecl() {
p.expectKeyword("import")
name := p.parsePackageName()
- p.getPkg(p.parsePackageId(), name)
+ p.getPkg(p.parsePackageID(), name)
}
// int_lit = [ "+" | "-" ] { "0" ... "9" } .
diff --git a/vendor/golang.org/x/tools/go/internal/packagesdriver/sizes.go b/vendor/golang.org/x/tools/go/internal/packagesdriver/sizes.go
index db0c9a7ea6..5ee692d383 100644
--- a/vendor/golang.org/x/tools/go/internal/packagesdriver/sizes.go
+++ b/vendor/golang.org/x/tools/go/internal/packagesdriver/sizes.go
@@ -11,11 +11,10 @@ import (
"encoding/json"
"fmt"
"go/types"
- "log"
- "os"
"os/exec"
"strings"
- "time"
+
+ "golang.org/x/tools/internal/gocommand"
)
var debug = false
@@ -78,97 +77,42 @@ func GetSizes(ctx context.Context, buildFlags, env []string, dir string, usesExp
}
func GetSizesGolist(ctx context.Context, buildFlags, env []string, dir string, usesExportData bool) (types.Sizes, error) {
- args := []string{"list", "-f", "{{context.GOARCH}} {{context.Compiler}}"}
- args = append(args, buildFlags...)
- args = append(args, "--", "unsafe")
- stdout, stderr, err := invokeGo(ctx, env, dir, usesExportData, args...)
+ inv := gocommand.Invocation{
+ Verb: "list",
+ Args: []string{"-f", "{{context.GOARCH}} {{context.Compiler}}", "--", "unsafe"},
+ Env: env,
+ BuildFlags: buildFlags,
+ WorkingDir: dir,
+ }
+ stdout, stderr, friendlyErr, rawErr := inv.RunRaw(ctx)
var goarch, compiler string
- if err != nil {
- if strings.Contains(err.Error(), "cannot find main module") {
+ if rawErr != nil {
+ if strings.Contains(rawErr.Error(), "cannot find main module") {
// User's running outside of a module. All bets are off. Get GOARCH and guess compiler is gc.
// TODO(matloob): Is this a problem in practice?
- envout, _, enverr := invokeGo(ctx, env, dir, usesExportData, "env", "GOARCH")
+ inv := gocommand.Invocation{
+ Verb: "env",
+ Args: []string{"GOARCH"},
+ Env: env,
+ WorkingDir: dir,
+ }
+ envout, enverr := inv.Run(ctx)
if enverr != nil {
- return nil, err
+ return nil, enverr
}
goarch = strings.TrimSpace(envout.String())
compiler = "gc"
} else {
- return nil, err
+ return nil, friendlyErr
}
} else {
fields := strings.Fields(stdout.String())
if len(fields) < 2 {
- return nil, fmt.Errorf("could not parse GOARCH and Go compiler in format \"<GOARCH> <compiler>\" from stdout of go command:\n%s\ndir: %s\nstdout: <<%s>>\nstderr: <<%s>>",
- cmdDebugStr(env, args...), dir, stdout.String(), stderr.String())
+ return nil, fmt.Errorf("could not parse GOARCH and Go compiler in format \"<GOARCH> <compiler>\":\nstdout: <<%s>>\nstderr: <<%s>>",
+ stdout.String(), stderr.String())
}
goarch = fields[0]
compiler = fields[1]
}
return types.SizesFor(compiler, goarch), nil
}
-
-// invokeGo returns the stdout and stderr of a go command invocation.
-func invokeGo(ctx context.Context, env []string, dir string, usesExportData bool, args ...string) (*bytes.Buffer, *bytes.Buffer, error) {
- if debug {
- defer func(start time.Time) { log.Printf("%s for %v", time.Since(start), cmdDebugStr(env, args...)) }(time.Now())
- }
- stdout := new(bytes.Buffer)
- stderr := new(bytes.Buffer)
- cmd := exec.CommandContext(ctx, "go", args...)
- // On darwin the cwd gets resolved to the real path, which breaks anything that
- // expects the working directory to keep the original path, including the
- // go command when dealing with modules.
- // The Go stdlib has a special feature where if the cwd and the PWD are the
- // same node then it trusts the PWD, so by setting it in the env for the child
- // process we fix up all the paths returned by the go command.
- cmd.Env = append(append([]string{}, env...), "PWD="+dir)
- cmd.Dir = dir
- cmd.Stdout = stdout
- cmd.Stderr = stderr
- if err := cmd.Run(); err != nil {
- exitErr, ok := err.(*exec.ExitError)
- if !ok {
- // Catastrophic error:
- // - executable not found
- // - context cancellation
- return nil, nil, fmt.Errorf("couldn't exec 'go %v': %s %T", args, err, err)
- }
-
- // Export mode entails a build.
- // If that build fails, errors appear on stderr
- // (despite the -e flag) and the Export field is blank.
- // Do not fail in that case.
- if !usesExportData {
- return nil, nil, fmt.Errorf("go %v: %s: %s", args, exitErr, stderr)
- }
- }
-
- // As of writing, go list -export prints some non-fatal compilation
- // errors to stderr, even with -e set. We would prefer that it put
- // them in the Package.Error JSON (see https://golang.org/issue/26319).
- // In the meantime, there's nowhere good to put them, but they can
- // be useful for debugging. Print them if $GOPACKAGESPRINTGOLISTERRORS
- // is set.
- if len(stderr.Bytes()) != 0 && os.Getenv("GOPACKAGESPRINTGOLISTERRORS") != "" {
- fmt.Fprintf(os.Stderr, "%s stderr: <<%s>>\n", cmdDebugStr(env, args...), stderr)
- }
-
- // debugging
- if false {
- fmt.Fprintf(os.Stderr, "%s stdout: <<%s>>\n", cmdDebugStr(env, args...), stdout)
- }
-
- return stdout, stderr, nil
-}
-
-func cmdDebugStr(envlist []string, args ...string) string {
- env := make(map[string]string)
- for _, kv := range envlist {
- 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/golist.go b/vendor/golang.org/x/tools/go/packages/golist.go
index 9c895b3895..b4a13ef454 100644
--- a/vendor/golang.org/x/tools/go/packages/golist.go
+++ b/vendor/golang.org/x/tools/go/packages/golist.go
@@ -6,26 +6,25 @@ package packages
import (
"bytes"
+ "context"
"encoding/json"
"fmt"
"go/types"
- "io/ioutil"
"log"
"os"
"os/exec"
"path"
"path/filepath"
"reflect"
- "regexp"
+ "sort"
"strconv"
"strings"
"sync"
- "time"
"unicode"
"golang.org/x/tools/go/internal/packagesdriver"
- "golang.org/x/tools/internal/gopathwalk"
- "golang.org/x/tools/internal/semver"
+ "golang.org/x/tools/internal/gocommand"
+ "golang.org/x/tools/internal/packagesinternal"
)
// debug controls verbose logging.
@@ -44,16 +43,21 @@ type responseDeduper struct {
dr *driverResponse
}
-// init fills in r with a driverResponse.
-func (r *responseDeduper) init(dr *driverResponse) {
- r.dr = dr
- r.seenRoots = map[string]bool{}
- r.seenPackages = map[string]*Package{}
+func newDeduper() *responseDeduper {
+ return &responseDeduper{
+ dr: &driverResponse{},
+ seenRoots: map[string]bool{},
+ seenPackages: map[string]*Package{},
+ }
+}
+
+// addAll fills in r with a driverResponse.
+func (r *responseDeduper) addAll(dr *driverResponse) {
for _, pkg := range dr.Packages {
- r.seenPackages[pkg.ID] = pkg
+ r.addPackage(pkg)
}
for _, root := range dr.Roots {
- r.seenRoots[root] = true
+ r.addRoot(root)
}
}
@@ -73,25 +77,47 @@ func (r *responseDeduper) addRoot(id string) {
r.dr.Roots = append(r.dr.Roots, id)
}
-// goInfo contains global information from the go tool.
-type goInfo struct {
- rootDirs map[string]string
- env goEnv
+type golistState struct {
+ cfg *Config
+ ctx context.Context
+
+ envOnce sync.Once
+ goEnvError error
+ goEnv map[string]string
+
+ rootsOnce sync.Once
+ rootDirsError error
+ rootDirs map[string]string
+
+ // vendorDirs caches the (non)existence of vendor directories.
+ vendorDirs map[string]bool
}
-type goEnv struct {
- modulesOn bool
+// getEnv returns Go environment variables. Only specific variables are
+// populated -- computing all of them is slow.
+func (state *golistState) getEnv() (map[string]string, error) {
+ state.envOnce.Do(func() {
+ var b *bytes.Buffer
+ b, state.goEnvError = state.invokeGo("env", "-json", "GOMOD", "GOPATH")
+ if state.goEnvError != nil {
+ return
+ }
+
+ state.goEnv = make(map[string]string)
+ decoder := json.NewDecoder(b)
+ if state.goEnvError = decoder.Decode(&state.goEnv); state.goEnvError != nil {
+ return
+ }
+ })
+ return state.goEnv, state.goEnvError
}
-func determineEnv(cfg *Config) goEnv {
- buf, err := invokeGo(cfg, "env", "GOMOD")
+// mustGetEnv is a convenience function that can be used if getEnv has already succeeded.
+func (state *golistState) mustGetEnv() map[string]string {
+ env, err := state.getEnv()
if err != nil {
- return goEnv{}
+ panic(fmt.Sprintf("mustGetEnv: %v", err))
}
- gomod := bytes.TrimSpace(buf.Bytes())
-
- env := goEnv{}
- env.modulesOn = len(gomod) > 0
return env
}
@@ -99,47 +125,38 @@ func determineEnv(cfg *Config) goEnv {
// the build system package structure.
// See driver for more details.
func goListDriver(cfg *Config, patterns ...string) (*driverResponse, error) {
- var sizes types.Sizes
+ // Make sure that any asynchronous go commands are killed when we return.
+ parentCtx := cfg.Context
+ if parentCtx == nil {
+ parentCtx = context.Background()
+ }
+ ctx, cancel := context.WithCancel(parentCtx)
+ defer cancel()
+
+ response := newDeduper()
+
+ // Fill in response.Sizes asynchronously if necessary.
var sizeserr error
var sizeswg sync.WaitGroup
if cfg.Mode&NeedTypesSizes != 0 || cfg.Mode&NeedTypes != 0 {
sizeswg.Add(1)
go func() {
- sizes, sizeserr = getSizes(cfg)
+ var sizes types.Sizes
+ sizes, sizeserr = packagesdriver.GetSizesGolist(ctx, cfg.BuildFlags, cfg.Env, cfg.Dir, usesExportData(cfg))
+ // types.SizesFor always returns nil or a *types.StdSizes.
+ response.dr.Sizes, _ = sizes.(*types.StdSizes)
sizeswg.Done()
}()
}
- defer sizeswg.Wait()
-
- // start fetching rootDirs
- var info goInfo
- var rootDirsReady, envReady = make(chan struct{}), make(chan struct{})
- go func() {
- info.rootDirs = determineRootDirs(cfg)
- close(rootDirsReady)
- }()
- go func() {
- info.env = determineEnv(cfg)
- close(envReady)
- }()
- getGoInfo := func() *goInfo {
- <-rootDirsReady
- <-envReady
- return &info
- }
-
- // Ensure that we don't leak goroutines: Load is synchronous, so callers will
- // not expect it to access the fields of cfg after the call returns.
- defer getGoInfo()
- // always pass getGoInfo to golistDriver
- golistDriver := func(cfg *Config, patterns ...string) (*driverResponse, error) {
- return golistDriver(cfg, getGoInfo, patterns...)
+ state := &golistState{
+ cfg: cfg,
+ ctx: ctx,
+ vendorDirs: map[string]bool{},
}
// Determine files requested in contains patterns
var containFiles []string
- var packagesNamed []string
restPatterns := make([]string, 0, len(patterns))
// Extract file= and other [querytype]= patterns. Report an error if querytype
// doesn't exist.
@@ -155,8 +172,6 @@ extractQueries:
containFiles = append(containFiles, value)
case "pattern":
restPatterns = append(restPatterns, value)
- case "iamashamedtousethedisabledqueryname":
- packagesNamed = append(packagesNamed, value)
case "": // not a reserved query
restPatterns = append(restPatterns, pattern)
default:
@@ -172,52 +187,34 @@ extractQueries:
}
}
- response := &responseDeduper{}
- var err error
-
// See if we have any patterns to pass through to go list. Zero initial
// patterns also requires a go list call, since it's the equivalent of
// ".".
if len(restPatterns) > 0 || len(patterns) == 0 {
- dr, err := golistDriver(cfg, restPatterns...)
+ dr, err := state.createDriverResponse(restPatterns...)
if err != nil {
return nil, err
}
- response.init(dr)
- } else {
- response.init(&driverResponse{})
- }
-
- sizeswg.Wait()
- if sizeserr != nil {
- return nil, sizeserr
+ response.addAll(dr)
}
- // types.SizesFor always returns nil or a *types.StdSizes
- response.dr.Sizes, _ = sizes.(*types.StdSizes)
-
- var containsCandidates []string
if len(containFiles) != 0 {
- if err := runContainsQueries(cfg, golistDriver, response, containFiles, getGoInfo); err != nil {
+ if err := state.runContainsQueries(response, containFiles); err != nil {
return nil, err
}
}
- if len(packagesNamed) != 0 {
- if err := runNamedQueries(cfg, golistDriver, response, packagesNamed); err != nil {
- return nil, err
- }
- }
-
- modifiedPkgs, needPkgs, err := processGolistOverlay(cfg, response, getGoInfo)
+ modifiedPkgs, needPkgs, err := state.processGolistOverlay(response)
if err != nil {
return nil, err
}
+
+ var containsCandidates []string
if len(containFiles) > 0 {
containsCandidates = append(containsCandidates, modifiedPkgs...)
containsCandidates = append(containsCandidates, needPkgs...)
}
- if err := addNeededOverlayPackages(cfg, golistDriver, response, needPkgs, getGoInfo); err != nil {
+ if err := state.addNeededOverlayPackages(response, needPkgs); err != nil {
return nil, err
}
// Check candidate packages for containFiles.
@@ -246,28 +243,32 @@ extractQueries:
}
}
+ sizeswg.Wait()
+ if sizeserr != nil {
+ return nil, sizeserr
+ }
return response.dr, nil
}
-func addNeededOverlayPackages(cfg *Config, driver driver, response *responseDeduper, pkgs []string, getGoInfo func() *goInfo) error {
+func (state *golistState) addNeededOverlayPackages(response *responseDeduper, pkgs []string) error {
if len(pkgs) == 0 {
return nil
}
- dr, err := driver(cfg, pkgs...)
+ dr, err := state.createDriverResponse(pkgs...)
if err != nil {
return err
}
for _, pkg := range dr.Packages {
response.addPackage(pkg)
}
- _, needPkgs, err := processGolistOverlay(cfg, response, getGoInfo)
+ _, needPkgs, err := state.processGolistOverlay(response)
if err != nil {
return err
}
- return addNeededOverlayPackages(cfg, driver, response, needPkgs, getGoInfo)
+ return state.addNeededOverlayPackages(response, needPkgs)
}
-func runContainsQueries(cfg *Config, driver driver, response *responseDeduper, queries []string, goInfo func() *goInfo) error {
+func (state *golistState) runContainsQueries(response *responseDeduper, queries []string) error {
for _, query := range queries {
// TODO(matloob): Do only one query per directory.
fdir := filepath.Dir(query)
@@ -277,44 +278,17 @@ func runContainsQueries(cfg *Config, driver driver, response *responseDeduper, q
if err != nil {
return fmt.Errorf("could not determine absolute path of file= query path %q: %v", query, err)
}
- dirResponse, err := driver(cfg, pattern)
- if err != nil || (len(dirResponse.Packages) == 1 && len(dirResponse.Packages[0].Errors) == 1) {
- // There was an error loading the package. Try to load the file as an ad-hoc package.
- // Usually the error will appear in a returned package, but may not if we're in modules mode
- // and the ad-hoc is located outside a module.
+ dirResponse, err := state.createDriverResponse(pattern)
+
+ // If there was an error loading the package, or the package is returned
+ // with errors, try to load the file as an ad-hoc package.
+ // Usually the error will appear in a returned package, but may not if we're
+ // in module mode and the ad-hoc is located outside a module.
+ if err != nil || len(dirResponse.Packages) == 1 && len(dirResponse.Packages[0].GoFiles) == 0 &&
+ len(dirResponse.Packages[0].Errors) == 1 {
var queryErr error
- dirResponse, queryErr = driver(cfg, query)
- if queryErr != nil {
- // Return the original error if the attempt to fall back failed.
- return err
- }
- // If we get nothing back from `go list`, try to make this file into its own ad-hoc package.
- if len(dirResponse.Packages) == 0 && queryErr == nil {
- dirResponse.Packages = append(dirResponse.Packages, &Package{
- ID: "command-line-arguments",
- PkgPath: query,
- GoFiles: []string{query},
- CompiledGoFiles: []string{query},
- Imports: make(map[string]*Package),
- })
- dirResponse.Roots = append(dirResponse.Roots, "command-line-arguments")
- }
- // Special case to handle issue #33482:
- // If this is a file= query for ad-hoc packages where the file only exists on an overlay,
- // and exists outside of a module, add the file in for the package.
- if len(dirResponse.Packages) == 1 && (dirResponse.Packages[0].ID == "command-line-arguments" ||
- filepath.ToSlash(dirResponse.Packages[0].PkgPath) == filepath.ToSlash(query)) {
- if len(dirResponse.Packages[0].GoFiles) == 0 {
- filename := filepath.Join(pattern, filepath.Base(query)) // avoid recomputing abspath
- // TODO(matloob): check if the file is outside of a root dir?
- for path := range cfg.Overlay {
- if path == filename {
- dirResponse.Packages[0].Errors = nil
- dirResponse.Packages[0].GoFiles = []string{path}
- dirResponse.Packages[0].CompiledGoFiles = []string{path}
- }
- }
- }
+ if dirResponse, queryErr = state.adhocPackage(pattern, query); queryErr != nil {
+ return err // return the original error
}
}
isRoot := make(map[string]bool, len(dirResponse.Roots))
@@ -342,276 +316,47 @@ func runContainsQueries(cfg *Config, driver driver, response *responseDeduper, q
return nil
}
-// modCacheRegexp splits a path in a module cache into module, module version, and package.
-var modCacheRegexp = regexp.MustCompile(`(.*)@([^/\\]*)(.*)`)
-
-func runNamedQueries(cfg *Config, driver driver, response *responseDeduper, queries []string) error {
- // calling `go env` isn't free; bail out if there's nothing to do.
- if len(queries) == 0 {
- return nil
- }
- // Determine which directories are relevant to scan.
- roots, modRoot, err := roots(cfg)
- if err != nil {
- return err
- }
-
- // Scan the selected directories. Simple matches, from GOPATH/GOROOT
- // or the local module, can simply be "go list"ed. Matches from the
- // module cache need special treatment.
- var matchesMu sync.Mutex
- var simpleMatches, modCacheMatches []string
- add := func(root gopathwalk.Root, dir string) {
- // Walk calls this concurrently; protect the result slices.
- matchesMu.Lock()
- defer matchesMu.Unlock()
-
- path := dir
- if dir != root.Path {
- path = dir[len(root.Path)+1:]
- }
- if pathMatchesQueries(path, queries) {
- switch root.Type {
- case gopathwalk.RootModuleCache:
- modCacheMatches = append(modCacheMatches, path)
- case gopathwalk.RootCurrentModule:
- // We'd need to read go.mod to find the full
- // import path. Relative's easier.
- rel, err := filepath.Rel(cfg.Dir, dir)
- if err != nil {
- // This ought to be impossible, since
- // we found dir in the current module.
- panic(err)
- }
- simpleMatches = append(simpleMatches, "./"+rel)
- case gopathwalk.RootGOPATH, gopathwalk.RootGOROOT:
- simpleMatches = append(simpleMatches, path)
- }
- }
- }
-
- startWalk := time.Now()
- gopathwalk.Walk(roots, add, gopathwalk.Options{ModulesEnabled: modRoot != "", Debug: debug})
- cfg.Logf("%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
- // more reasonable for that to not match the package name. So, if there
- // are any Go files in the mod root, query it just to be safe.
- if modRoot != "" {
- rel, err := filepath.Rel(cfg.Dir, modRoot)
- if err != nil {
- panic(err) // See above.
- }
-
- files, err := ioutil.ReadDir(modRoot)
- if err != nil {
- panic(err) // See above.
- }
-
- for _, f := range files {
- if strings.HasSuffix(f.Name(), ".go") {
- simpleMatches = append(simpleMatches, rel)
- break
- }
- }
- }
-
- addResponse := func(r *driverResponse) {
- for _, pkg := range r.Packages {
- response.addPackage(pkg)
- for _, name := range queries {
- if pkg.Name == name {
- response.addRoot(pkg.ID)
- break
- }
- }
- }
- }
-
- if len(simpleMatches) != 0 {
- resp, err := driver(cfg, simpleMatches...)
- if err != nil {
- return err
- }
- addResponse(resp)
- }
-
- // Module cache matches are tricky. We want to avoid downloading new
- // versions of things, so we need to use the ones present in the cache.
- // go list doesn't accept version specifiers, so we have to write out a
- // temporary module, and do the list in that module.
- if len(modCacheMatches) != 0 {
- // Collect all the matches, deduplicating by major version
- // and preferring the newest.
- type modInfo struct {
- mod string
- major string
- }
- mods := make(map[modInfo]string)
- var imports []string
- for _, modPath := range modCacheMatches {
- matches := modCacheRegexp.FindStringSubmatch(modPath)
- mod, ver := filepath.ToSlash(matches[1]), matches[2]
- importPath := filepath.ToSlash(filepath.Join(matches[1], matches[3]))
-
- major := semver.Major(ver)
- if prevVer, ok := mods[modInfo{mod, major}]; !ok || semver.Compare(ver, prevVer) > 0 {
- mods[modInfo{mod, major}] = ver
- }
-
- imports = append(imports, importPath)
- }
-
- // Build the temporary module.
- var gomod bytes.Buffer
- gomod.WriteString("module modquery\nrequire (\n")
- for mod, version := range mods {
- gomod.WriteString("\t" + mod.mod + " " + version + "\n")
- }
- 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([]string{"GOPROXY=off"}, cfg.Env...)
-
- var err error
- tmpCfg.Dir, err = ioutil.TempDir("", "gopackages-modquery")
- if err != nil {
- return err
- }
- defer os.RemoveAll(tmpCfg.Dir)
-
- if err := ioutil.WriteFile(filepath.Join(tmpCfg.Dir, "go.mod"), gomod.Bytes(), 0777); err != nil {
- return fmt.Errorf("writing go.mod for module cache query: %v", err)
- }
-
- // Run the query, using the import paths calculated from the matches above.
- resp, err := driver(&tmpCfg, imports...)
- if err != nil {
- return fmt.Errorf("querying module cache matches: %v", err)
- }
- addResponse(resp)
- }
-
- return nil
-}
-
-func getSizes(cfg *Config) (types.Sizes, error) {
- return packagesdriver.GetSizesGolist(cfg.Context, cfg.BuildFlags, cfg.Env, cfg.Dir, usesExportData(cfg))
-}
-
-// 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) {
- stdout, err := invokeGo(cfg, "env", "GOROOT", "GOPATH", "GOMOD")
+// adhocPackage attempts to load or construct an ad-hoc package for a given
+// query, if the original call to the driver produced inadequate results.
+func (state *golistState) adhocPackage(pattern, query string) (*driverResponse, error) {
+ response, err := state.createDriverResponse(query)
if err != nil {
- return nil, "", err
- }
-
- fields := strings.Split(stdout.String(), "\n")
- if len(fields) != 4 || len(fields[3]) != 0 {
- return nil, "", fmt.Errorf("go env returned unexpected output: %q", stdout.String())
- }
- goroot, gopath, gomod := fields[0], filepath.SplitList(fields[1]), fields[2]
- var modDir string
- if gomod != "" {
- modDir = filepath.Dir(gomod)
+ return nil, err
}
-
- var roots []gopathwalk.Root
- // Always add GOROOT.
- roots = append(roots, gopathwalk.Root{
- Path: filepath.Join(goroot, "/src"),
- Type: gopathwalk.RootGOROOT,
- })
- // If modules are enabled, scan the module dir.
- if modDir != "" {
- roots = append(roots, gopathwalk.Root{
- Path: modDir,
- Type: gopathwalk.RootCurrentModule,
+ // If we get nothing back from `go list`,
+ // try to make this file into its own ad-hoc package.
+ // TODO(rstambler): Should this check against the original response?
+ if len(response.Packages) == 0 {
+ response.Packages = append(response.Packages, &Package{
+ ID: "command-line-arguments",
+ PkgPath: query,
+ GoFiles: []string{query},
+ CompiledGoFiles: []string{query},
+ Imports: make(map[string]*Package),
})
- }
- // Add either GOPATH/src or GOPATH/pkg/mod, depending on module mode.
- for _, p := range gopath {
- if modDir != "" {
- roots = append(roots, gopathwalk.Root{
- Path: filepath.Join(p, "/pkg/mod"),
- Type: gopathwalk.RootModuleCache,
- })
- } else {
- roots = append(roots, gopathwalk.Root{
- Path: filepath.Join(p, "/src"),
- Type: gopathwalk.RootGOPATH,
- })
- }
- }
-
- return roots, modDir, nil
-}
-
-// These functions were copied from goimports. See further documentation there.
-
-// pathMatchesQueries is adapted from pkgIsCandidate.
-// TODO: is it reasonable to do Contains here, rather than an exact match on a path component?
-func pathMatchesQueries(path string, queries []string) bool {
- lastTwo := lastTwoComponents(path)
- for _, query := range queries {
- if strings.Contains(lastTwo, query) {
- return true
- }
- if hasHyphenOrUpperASCII(lastTwo) && !hasHyphenOrUpperASCII(query) {
- lastTwo = lowerASCIIAndRemoveHyphen(lastTwo)
- if strings.Contains(lastTwo, query) {
- return true
- }
- }
- }
- return false
-}
-
-// lastTwoComponents returns at most the last two path components
-// of v, using either / or \ as the path separator.
-func lastTwoComponents(v string) string {
- nslash := 0
- for i := len(v) - 1; i >= 0; i-- {
- if v[i] == '/' || v[i] == '\\' {
- nslash++
- if nslash == 2 {
- return v[i:]
+ response.Roots = append(response.Roots, "command-line-arguments")
+ }
+ // Handle special cases.
+ if len(response.Packages) == 1 {
+ // golang/go#33482: If this is a file= query for ad-hoc packages where
+ // the file only exists on an overlay, and exists outside of a module,
+ // add the file to the package and remove the errors.
+ if response.Packages[0].ID == "command-line-arguments" ||
+ filepath.ToSlash(response.Packages[0].PkgPath) == filepath.ToSlash(query) {
+ if len(response.Packages[0].GoFiles) == 0 {
+ filename := filepath.Join(pattern, filepath.Base(query)) // avoid recomputing abspath
+ // TODO(matloob): check if the file is outside of a root dir?
+ for path := range state.cfg.Overlay {
+ if path == filename {
+ response.Packages[0].Errors = nil
+ response.Packages[0].GoFiles = []string{path}
+ response.Packages[0].CompiledGoFiles = []string{path}
+ }
+ }
}
}
}
- return v
-}
-
-func hasHyphenOrUpperASCII(s string) bool {
- for i := 0; i < len(s); i++ {
- b := s[i]
- if b == '-' || ('A' <= b && b <= 'Z') {
- return true
- }
- }
- return false
-}
-
-func lowerASCIIAndRemoveHyphen(s string) (ret string) {
- buf := make([]byte, 0, len(s))
- for i := 0; i < len(s); i++ {
- b := s[i]
- switch {
- case b == '-':
- continue
- case 'A' <= b && b <= 'Z':
- buf = append(buf, b+('a'-'A'))
- default:
- buf = append(buf, b)
- }
- }
- return string(buf)
+ return response, nil
}
// Fields must match go list;
@@ -636,6 +381,7 @@ type jsonPackage struct {
Imports []string
ImportMap map[string]string
Deps []string
+ Module *packagesinternal.Module
TestGoFiles []string
TestImports []string
XTestGoFiles []string
@@ -656,10 +402,9 @@ func otherFiles(p *jsonPackage) [][]string {
return [][]string{p.CFiles, p.CXXFiles, p.MFiles, p.HFiles, p.FFiles, p.SFiles, p.SwigFiles, p.SwigCXXFiles, p.SysoFiles}
}
-// golistDriver uses the "go list" command to expand the pattern
-// words and return metadata for the specified packages. dir may be
-// "" and env may be nil, as per os/exec.Command.
-func golistDriver(cfg *Config, rootsDirs func() *goInfo, words ...string) (*driverResponse, error) {
+// createDriverResponse uses the "go list" command to expand the pattern
+// words and return a response for the specified packages.
+func (state *golistState) createDriverResponse(words ...string) (*driverResponse, error) {
// go list uses the following identifiers in ImportPath and Imports:
//
// "p" -- importable package or main (command)
@@ -673,11 +418,13 @@ func golistDriver(cfg *Config, rootsDirs func() *goInfo, words ...string) (*driv
// Run "go list" for complete
// information on the specified packages.
- buf, err := invokeGo(cfg, golistargs(cfg, words)...)
+ buf, err := state.invokeGo("list", golistargs(state.cfg, words)...)
if err != nil {
return nil, err
}
seen := make(map[string]*jsonPackage)
+ pkgs := make(map[string]*Package)
+ additionalErrors := make(map[string][]Error)
// Decode the JSON and convert it to Package form.
var response driverResponse
for dec := json.NewDecoder(buf); dec.More(); {
@@ -708,18 +455,72 @@ func golistDriver(cfg *Config, rootsDirs func() *goInfo, words ...string) (*driv
// contained in a known module or GOPATH entry. This will allow the package to be
// properly "reclaimed" when overlays are processed.
if filepath.IsAbs(p.ImportPath) && p.Error != nil {
- pkgPath, ok := getPkgPath(cfg, p.ImportPath, rootsDirs)
+ pkgPath, ok, err := state.getPkgPath(p.ImportPath)
+ if err != nil {
+ return nil, err
+ }
if ok {
p.ImportPath = pkgPath
}
}
if old, found := seen[p.ImportPath]; found {
- if !reflect.DeepEqual(p, old) {
- return nil, fmt.Errorf("internal error: go list gives conflicting information for package %v", p.ImportPath)
+ // If one version of the package has an error, and the other doesn't, assume
+ // that this is a case where go list is reporting a fake dependency variant
+ // of the imported package: When a package tries to invalidly import another
+ // package, go list emits a variant of the imported package (with the same
+ // import path, but with an error on it, and the package will have a
+ // DepError set on it). An example of when this can happen is for imports of
+ // main packages: main packages can not be imported, but they may be
+ // separately matched and listed by another pattern.
+ // See golang.org/issue/36188 for more details.
+
+ // The plan is that eventually, hopefully in Go 1.15, the error will be
+ // reported on the importing package rather than the duplicate "fake"
+ // version of the imported package. Once all supported versions of Go
+ // have the new behavior this logic can be deleted.
+ // TODO(matloob): delete the workaround logic once all supported versions of
+ // Go return the errors on the proper package.
+
+ // There should be exactly one version of a package that doesn't have an
+ // error.
+ if old.Error == nil && p.Error == nil {
+ if !reflect.DeepEqual(p, old) {
+ return nil, fmt.Errorf("internal error: go list gives conflicting information for package %v", p.ImportPath)
+ }
+ continue
}
- // skip the duplicate
- continue
+
+ // Determine if this package's error needs to be bubbled up.
+ // This is a hack, and we expect for go list to eventually set the error
+ // on the package.
+ if old.Error != nil {
+ var errkind string
+ if strings.Contains(old.Error.Err, "not an importable package") {
+ errkind = "not an importable package"
+ } else if strings.Contains(old.Error.Err, "use of internal package") && strings.Contains(old.Error.Err, "not allowed") {
+ errkind = "use of internal package not allowed"
+ }
+ if errkind != "" {
+ if len(old.Error.ImportStack) < 2 {
+ return nil, fmt.Errorf(`internal error: go list gave a %q error with an import stack with fewer than two elements`, errkind)
+ }
+ importingPkg := old.Error.ImportStack[len(old.Error.ImportStack)-2]
+ additionalErrors[importingPkg] = append(additionalErrors[importingPkg], Error{
+ Pos: old.Error.Pos,
+ Msg: old.Error.Err,
+ Kind: ListError,
+ })
+ }
+ }
+
+ // Make sure that if there's a version of the package without an error,
+ // that's the one reported to the user.
+ if old.Error == nil {
+ continue
+ }
+
+ // This package will replace the old one at the end of the loop.
}
seen[p.ImportPath] = p
@@ -729,6 +530,8 @@ func golistDriver(cfg *Config, rootsDirs func() *goInfo, words ...string) (*driv
GoFiles: absJoin(p.Dir, p.GoFiles, p.CgoFiles),
CompiledGoFiles: absJoin(p.Dir, p.CompiledGoFiles),
OtherFiles: absJoin(p.Dir, otherFiles(p)...),
+ forTest: p.ForTest,
+ module: p.Module,
}
// Work around https://golang.org/issue/28749:
@@ -811,34 +614,43 @@ func golistDriver(cfg *Config, rootsDirs func() *goInfo, words ...string) (*driv
msg += fmt.Sprintf(": import stack: %v", p.Error.ImportStack)
}
pkg.Errors = append(pkg.Errors, Error{
- Pos: p.Error.Pos,
- Msg: msg,
+ Pos: p.Error.Pos,
+ Msg: msg,
+ Kind: ListError,
})
}
+ pkgs[pkg.ID] = pkg
+ }
+
+ for id, errs := range additionalErrors {
+ if p, ok := pkgs[id]; ok {
+ p.Errors = append(p.Errors, errs...)
+ }
+ }
+ for _, pkg := range pkgs {
response.Packages = append(response.Packages, pkg)
}
+ sort.Slice(response.Packages, func(i, j int) bool { return response.Packages[i].ID < response.Packages[j].ID })
return &response, nil
}
// getPkgPath finds the package path of a directory if it's relative to a root directory.
-func getPkgPath(cfg *Config, dir string, goInfo func() *goInfo) (string, bool) {
+func (state *golistState) getPkgPath(dir string) (string, bool, error) {
absDir, err := filepath.Abs(dir)
if err != nil {
- cfg.Logf("error getting absolute path of %s: %v", dir, err)
- return "", false
+ return "", false, err
}
- for rdir, rpath := range goInfo().rootDirs {
- absRdir, err := filepath.Abs(rdir)
- if err != nil {
- cfg.Logf("error getting absolute path of %s: %v", rdir, err)
- continue
- }
+ roots, err := state.determineRootDirs()
+ if err != nil {
+ return "", false, err
+ }
+
+ for rdir, rpath := range roots {
// Make sure that the directory is in the module,
// to avoid creating a path relative to another module.
- if !strings.HasPrefix(absDir, absRdir) {
- cfg.Logf("%s does not have prefix %s", absDir, absRdir)
+ if !strings.HasPrefix(absDir, rdir) {
continue
}
// TODO(matloob): This doesn't properly handle symlinks.
@@ -853,11 +665,11 @@ func getPkgPath(cfg *Config, dir string, goInfo func() *goInfo) (string, bool) {
// Once the file is saved, gopls, or the next invocation of the tool will get the correct
// result straight from golist.
// TODO(matloob): Implement module tiebreaking?
- return path.Join(rpath, filepath.ToSlash(r)), true
+ return path.Join(rpath, filepath.ToSlash(r)), true, nil
}
- return filepath.ToSlash(r), true
+ return filepath.ToSlash(r), true, nil
}
- return "", false
+ return "", false, nil
}
// absJoin absolutizes and flattens the lists of files.
@@ -876,8 +688,8 @@ func absJoin(dir string, fileses ...[]string) (res []string) {
func golistargs(cfg *Config, words []string) []string {
const findFlags = NeedImports | NeedTypes | NeedSyntax | NeedTypesInfo
fullargs := []string{
- "list", "-e", "-json",
- fmt.Sprintf("-compiled=%t", cfg.Mode&(NeedCompiledGoFiles|NeedSyntax|NeedTypesInfo|NeedTypesSizes) != 0),
+ "-e", "-json",
+ fmt.Sprintf("-compiled=%t", cfg.Mode&(NeedCompiledGoFiles|NeedSyntax|NeedTypes|NeedTypesInfo|NeedTypesSizes) != 0),
fmt.Sprintf("-test=%t", cfg.Tests),
fmt.Sprintf("-export=%t", usesExportData(cfg)),
fmt.Sprintf("-deps=%t", cfg.Mode&NeedImports != 0),
@@ -892,25 +704,20 @@ 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) {
- stdout := new(bytes.Buffer)
- stderr := new(bytes.Buffer)
- cmd := exec.CommandContext(cfg.Context, "go", args...)
- // On darwin the cwd gets resolved to the real path, which breaks anything that
- // expects the working directory to keep the original path, including the
- // go command when dealing with modules.
- // The Go stdlib has a special feature where if the cwd and the PWD are the
- // same node then it trusts the PWD, so by setting it in the env for the child
- // process we fix up all the paths returned by the go command.
- cmd.Env = append(append([]string{}, cfg.Env...), "PWD="+cfg.Dir)
- cmd.Dir = cfg.Dir
- cmd.Stdout = stdout
- cmd.Stderr = stderr
- defer func(start time.Time) {
- cfg.Logf("%s for %v, stderr: <<%s>> stdout: <<%s>>\n", time.Since(start), cmdDebugStr(cmd, args...), stderr, stdout)
- }(time.Now())
-
- if err := cmd.Run(); err != nil {
+func (state *golistState) invokeGo(verb string, args ...string) (*bytes.Buffer, error) {
+ cfg := state.cfg
+
+ inv := &gocommand.Invocation{
+ Verb: verb,
+ Args: args,
+ BuildFlags: cfg.BuildFlags,
+ Env: cfg.Env,
+ Logf: cfg.Logf,
+ WorkingDir: cfg.Dir,
+ }
+
+ stdout, stderr, _, err := inv.RunRaw(cfg.Context)
+ if err != nil {
// Check for 'go' executable not being found.
if ee, ok := err.(*exec.Error); ok && ee.Err == exec.ErrNotFound {
return nil, fmt.Errorf("'go list' driver requires 'go', but %s", exec.ErrNotFound)
@@ -920,7 +727,7 @@ func invokeGo(cfg *Config, args ...string) (*bytes.Buffer, error) {
if !ok {
// Catastrophic error:
// - context cancellation
- return nil, fmt.Errorf("couldn't exec 'go %v': %s %T", args, err, err)
+ return nil, fmt.Errorf("couldn't run 'go': %v", err)
}
// Old go version?
@@ -947,7 +754,12 @@ func invokeGo(cfg *Config, args ...string) (*bytes.Buffer, error) {
!strings.ContainsRune("!\"#$%&'()*,:;<=>?[\\]^`{|}\uFFFD", r)
}
if len(stderr.String()) > 0 && strings.HasPrefix(stderr.String(), "# ") {
- if strings.HasPrefix(strings.TrimLeftFunc(stderr.String()[len("# "):], isPkgPathRune), "\n") {
+ msg := stderr.String()[len("# "):]
+ if strings.HasPrefix(strings.TrimLeftFunc(msg, isPkgPathRune), "\n") {
+ return stdout, nil
+ }
+ // Treat pkg-config errors as a special case (golang.org/issue/36770).
+ if strings.HasPrefix(msg, "pkg-config") {
return stdout, nil
}
}
@@ -1036,16 +848,6 @@ func invokeGo(cfg *Config, args ...string) (*bytes.Buffer, error) {
return nil, fmt.Errorf("go %v: %s: %s", args, exitErr, stderr)
}
}
-
- // As of writing, go list -export prints some non-fatal compilation
- // errors to stderr, even with -e set. We would prefer that it put
- // them in the Package.Error JSON (see https://golang.org/issue/26319).
- // In the meantime, there's nowhere good to put them, but they can
- // be useful for debugging. Print them if $GOPACKAGESPRINTGOLISTERRORS
- // is set.
- if len(stderr.Bytes()) != 0 && os.Getenv("GOPACKAGESPRINTGOLISTERRORS") != "" {
- fmt.Fprintf(os.Stderr, "%s stderr: <<%s>>\n", cmdDebugStr(cmd, args...), stderr)
- }
return stdout, nil
}
diff --git a/vendor/golang.org/x/tools/go/packages/golist_overlay.go b/vendor/golang.org/x/tools/go/packages/golist_overlay.go
index a7de62299d..7974a6c9bb 100644
--- a/vendor/golang.org/x/tools/go/packages/golist_overlay.go
+++ b/vendor/golang.org/x/tools/go/packages/golist_overlay.go
@@ -1,12 +1,13 @@
package packages
import (
- "bytes"
"encoding/json"
"fmt"
"go/parser"
"go/token"
+ "os"
"path/filepath"
+ "sort"
"strconv"
"strings"
)
@@ -16,7 +17,7 @@ import (
// sometimes incorrect.
// TODO(matloob): Handle unsupported cases, including the following:
// - determining the correct package to add given a new import path
-func processGolistOverlay(cfg *Config, response *responseDeduper, rootDirs func() *goInfo) (modifiedPkgs, needPkgs []string, err error) {
+func (state *golistState) processGolistOverlay(response *responseDeduper) (modifiedPkgs, needPkgs []string, err error) {
havePkgs := make(map[string]string) // importPath -> non-test package ID
needPkgsSet := make(map[string]bool)
modifiedPkgsSet := make(map[string]bool)
@@ -34,7 +35,23 @@ func processGolistOverlay(cfg *Config, response *responseDeduper, rootDirs func(
// potentially modifying the transitive set of dependencies).
var overlayAddsImports bool
- for opath, contents := range cfg.Overlay {
+ // If both a package and its test package are created by the overlay, we
+ // need the real package first. Process all non-test files before test
+ // files, and make the whole process deterministic while we're at it.
+ var overlayFiles []string
+ for opath := range state.cfg.Overlay {
+ overlayFiles = append(overlayFiles, opath)
+ }
+ sort.Slice(overlayFiles, func(i, j int) bool {
+ iTest := strings.HasSuffix(overlayFiles[i], "_test.go")
+ jTest := strings.HasSuffix(overlayFiles[j], "_test.go")
+ if iTest != jTest {
+ return !iTest // non-tests are before tests.
+ }
+ return overlayFiles[i] < overlayFiles[j]
+ })
+ for _, opath := range overlayFiles {
+ contents := state.cfg.Overlay[opath]
base := filepath.Base(opath)
dir := filepath.Dir(opath)
var pkg *Package // if opath belongs to both a package and its test variant, this will be the test variant
@@ -64,14 +81,8 @@ func processGolistOverlay(cfg *Config, response *responseDeduper, rootDirs func(
testVariantOf = p
continue nextPackage
}
+ // We must have already seen the package of which this is a test variant.
if pkg != nil && p != pkg && pkg.PkgPath == p.PkgPath {
- // If we've already seen the test variant,
- // make sure to label which package it is a test variant of.
- if hasTestFiles(pkg) {
- testVariantOf = p
- continue nextPackage
- }
- // If we have already seen the package of which this is a test variant.
if hasTestFiles(p) {
testVariantOf = pkg
}
@@ -86,7 +97,10 @@ func processGolistOverlay(cfg *Config, response *responseDeduper, rootDirs func(
if pkg == nil {
// Try to find the module or gopath dir the file is contained in.
// Then for modules, add the module opath to the beginning.
- pkgPath, ok := getPkgPath(cfg, dir, rootDirs)
+ pkgPath, ok, err := state.getPkgPath(dir)
+ if err != nil {
+ return nil, nil, err
+ }
if !ok {
break
}
@@ -114,6 +128,11 @@ func processGolistOverlay(cfg *Config, response *responseDeduper, rootDirs func(
if isTestFile && !isXTest && testVariantOf != nil {
pkg.GoFiles = append(pkg.GoFiles, testVariantOf.GoFiles...)
pkg.CompiledGoFiles = append(pkg.CompiledGoFiles, testVariantOf.CompiledGoFiles...)
+ // Add the package under test and its imports to the test variant.
+ pkg.forTest = testVariantOf.PkgPath
+ for k, v := range testVariantOf.Imports {
+ pkg.Imports[k] = &Package{ID: v.ID}
+ }
}
}
}
@@ -130,42 +149,45 @@ func processGolistOverlay(cfg *Config, response *responseDeduper, rootDirs func(
continue
}
for _, imp := range imports {
- _, found := pkg.Imports[imp]
- if !found {
- overlayAddsImports = true
- // TODO(matloob): Handle cases when the following block isn't correct.
- // These include imports of vendored packages, etc.
- id, ok := havePkgs[imp]
- if !ok {
- id = imp
- }
- pkg.Imports[imp] = &Package{ID: id}
- // Add dependencies to the non-test variant version of this package as wel.
- if testVariantOf != nil {
- testVariantOf.Imports[imp] = &Package{ID: id}
+ if _, found := pkg.Imports[imp]; found {
+ continue
+ }
+ overlayAddsImports = true
+ id, ok := havePkgs[imp]
+ if !ok {
+ var err error
+ id, err = state.resolveImport(dir, imp)
+ if err != nil {
+ return nil, nil, err
}
}
+ pkg.Imports[imp] = &Package{ID: id}
+ // Add dependencies to the non-test variant version of this package as well.
+ if testVariantOf != nil {
+ testVariantOf.Imports[imp] = &Package{ID: id}
+ }
}
- continue
}
- // toPkgPath tries to guess the package path given the id.
- // This isn't always correct -- it's certainly wrong for
- // vendored packages' paths.
- toPkgPath := func(id string) string {
- // TODO(matloob): Handle vendor paths.
- i := strings.IndexByte(id, ' ')
- if i >= 0 {
- return id[:i]
+ // toPkgPath guesses the package path given the id.
+ toPkgPath := func(sourceDir, id string) (string, error) {
+ if i := strings.IndexByte(id, ' '); i >= 0 {
+ return state.resolveImport(sourceDir, id[:i])
}
- return id
+ return state.resolveImport(sourceDir, id)
}
- // Do another pass now that new packages have been created to determine the
- // set of missing packages.
+ // Now that new packages have been created, do another pass to determine
+ // the new set of missing packages.
for _, pkg := range response.dr.Packages {
for _, imp := range pkg.Imports {
- pkgPath := toPkgPath(imp.ID)
+ if len(pkg.GoFiles) == 0 {
+ return nil, nil, fmt.Errorf("cannot resolve imports for package %q with no Go files", pkg.PkgPath)
+ }
+ pkgPath, err := toPkgPath(filepath.Dir(pkg.GoFiles[0]), imp.ID)
+ if err != nil {
+ return nil, nil, err
+ }
if _, ok := havePkgs[pkgPath]; !ok {
needPkgsSet[pkgPath] = true
}
@@ -185,6 +207,52 @@ func processGolistOverlay(cfg *Config, response *responseDeduper, rootDirs func(
return modifiedPkgs, needPkgs, err
}
+// resolveImport finds the the ID of a package given its import path.
+// In particular, it will find the right vendored copy when in GOPATH mode.
+func (state *golistState) resolveImport(sourceDir, importPath string) (string, error) {
+ env, err := state.getEnv()
+ if err != nil {
+ return "", err
+ }
+ if env["GOMOD"] != "" {
+ return importPath, nil
+ }
+
+ searchDir := sourceDir
+ for {
+ vendorDir := filepath.Join(searchDir, "vendor")
+ exists, ok := state.vendorDirs[vendorDir]
+ if !ok {
+ info, err := os.Stat(vendorDir)
+ exists = err == nil && info.IsDir()
+ state.vendorDirs[vendorDir] = exists
+ }
+
+ if exists {
+ vendoredPath := filepath.Join(vendorDir, importPath)
+ if info, err := os.Stat(vendoredPath); err == nil && info.IsDir() {
+ // We should probably check for .go files here, but shame on anyone who fools us.
+ path, ok, err := state.getPkgPath(vendoredPath)
+ if err != nil {
+ return "", err
+ }
+ if ok {
+ return path, nil
+ }
+ }
+ }
+
+ // We know we've hit the top of the filesystem when we Dir / and get /,
+ // or C:\ and get C:\, etc.
+ next := filepath.Dir(searchDir)
+ if next == searchDir {
+ break
+ }
+ searchDir = next
+ }
+ return importPath, nil
+}
+
func hasTestFiles(p *Package) bool {
for _, f := range p.GoFiles {
if strings.HasSuffix(f, "_test.go") {
@@ -194,44 +262,59 @@ func hasTestFiles(p *Package) bool {
return false
}
-// determineRootDirs returns a mapping from directories code can be contained in to the
-// corresponding import path prefixes of those directories.
-// Its result is used to try to determine the import path for a package containing
-// an overlay file.
-func determineRootDirs(cfg *Config) map[string]string {
- // Assume modules first:
- out, err := invokeGo(cfg, "list", "-m", "-json", "all")
+// determineRootDirs returns a mapping from absolute directories that could
+// contain code to their corresponding import path prefixes.
+func (state *golistState) determineRootDirs() (map[string]string, error) {
+ env, err := state.getEnv()
if err != nil {
- return determineRootDirsGOPATH(cfg)
+ return nil, err
+ }
+ if env["GOMOD"] != "" {
+ state.rootsOnce.Do(func() {
+ state.rootDirs, state.rootDirsError = state.determineRootDirsModules()
+ })
+ } else {
+ state.rootsOnce.Do(func() {
+ state.rootDirs, state.rootDirsError = state.determineRootDirsGOPATH()
+ })
+ }
+ return state.rootDirs, state.rootDirsError
+}
+
+func (state *golistState) determineRootDirsModules() (map[string]string, error) {
+ out, err := state.invokeGo("list", "-m", "-json", "all")
+ if err != nil {
+ return nil, err
}
m := map[string]string{}
type jsonMod struct{ Path, Dir string }
for dec := json.NewDecoder(out); dec.More(); {
mod := new(jsonMod)
if err := dec.Decode(mod); err != nil {
- return m // Give up and return an empty map. Package won't be found for overlay.
+ return nil, err
}
if mod.Dir != "" && mod.Path != "" {
// This is a valid module; add it to the map.
- m[mod.Dir] = mod.Path
+ absDir, err := filepath.Abs(mod.Dir)
+ if err != nil {
+ return nil, err
+ }
+ m[absDir] = mod.Path
}
}
- return m
+ return m, nil
}
-func determineRootDirsGOPATH(cfg *Config) map[string]string {
+func (state *golistState) determineRootDirsGOPATH() (map[string]string, error) {
m := map[string]string{}
- out, err := invokeGo(cfg, "env", "GOPATH")
- if err != nil {
- // Could not determine root dir mapping. Everything is best-effort, so just return an empty map.
- // When we try to find the import path for a directory, there will be no root-dir match and
- // we'll give up.
- return m
- }
- for _, p := range filepath.SplitList(string(bytes.TrimSpace(out.Bytes()))) {
- m[filepath.Join(p, "src")] = ""
+ for _, dir := range filepath.SplitList(state.mustGetEnv()["GOPATH"]) {
+ absDir, err := filepath.Abs(dir)
+ if err != nil {
+ return nil, err
+ }
+ m[filepath.Join(absDir, "src")] = ""
}
- return m
+ return m, nil
}
func extractImports(filename string, contents []byte) ([]string, error) {
diff --git a/vendor/golang.org/x/tools/go/packages/packages.go b/vendor/golang.org/x/tools/go/packages/packages.go
index 050cca43a2..1ac6558c1d 100644
--- a/vendor/golang.org/x/tools/go/packages/packages.go
+++ b/vendor/golang.org/x/tools/go/packages/packages.go
@@ -23,6 +23,7 @@ import (
"sync"
"golang.org/x/tools/go/gcexportdata"
+ "golang.org/x/tools/internal/packagesinternal"
)
// A LoadMode controls the amount of detail to return when loading.
@@ -34,6 +35,9 @@ import (
// Load may return more information than requested.
type LoadMode int
+// TODO(matloob): When a V2 of go/packages is released, rename NeedExportsFile to
+// NeedExportFile to make it consistent with the Package field it's adding.
+
const (
// NeedName adds Name and PkgPath.
NeedName LoadMode = 1 << iota
@@ -51,7 +55,7 @@ const (
// NeedDeps adds the fields requested by the LoadMode in the packages in Imports.
NeedDeps
- // NeedExportsFile adds ExportsFile.
+ // NeedExportsFile adds ExportFile.
NeedExportsFile
// NeedTypes adds Types, Fset, and IllTyped.
@@ -160,7 +164,7 @@ type Config struct {
Tests bool
// Overlay provides a mapping of absolute file paths to file contents.
- // If the file with the given path already exists, the parser will use the
+ // If the file with the given path already exists, the parser will use the
// alternative file contents provided by the map.
//
// Overlays provide incomplete support for when a given file doesn't
@@ -292,6 +296,21 @@ type Package struct {
// TypesSizes provides the effective size function for types in TypesInfo.
TypesSizes types.Sizes
+
+ // forTest is the package under test, if any.
+ forTest string
+
+ // module is the module information for the package if it exists.
+ module *packagesinternal.Module
+}
+
+func init() {
+ packagesinternal.GetForTest = func(p interface{}) string {
+ return p.(*Package).forTest
+ }
+ packagesinternal.GetModule = func(p interface{}) *packagesinternal.Module {
+ return p.(*Package).module
+ }
}
// An Error describes a problem with a package's metadata, syntax, or types.
@@ -500,12 +519,23 @@ func (ld *loader) refine(roots []string, list ...*Package) ([]*Package, error) {
if i, found := rootMap[pkg.ID]; found {
rootIndex = i
}
+
+ // Overlays can invalidate export data.
+ // TODO(matloob): make this check fine-grained based on dependencies on overlaid files
+ exportDataInvalid := len(ld.Overlay) > 0 || pkg.ExportFile == "" && pkg.PkgPath != "unsafe"
+ // This package needs type information if the caller requested types and the package is
+ // either a root, or it's a non-root and the user requested dependencies ...
+ needtypes := (ld.Mode&NeedTypes|NeedTypesInfo != 0 && (rootIndex >= 0 || ld.Mode&NeedDeps != 0))
+ // This package needs source if the call requested source (or types info, which implies source)
+ // and the package is either a root, or itas a non- root and the user requested dependencies...
+ needsrc := ((ld.Mode&(NeedSyntax|NeedTypesInfo) != 0 && (rootIndex >= 0 || ld.Mode&NeedDeps != 0)) ||
+ // ... or if we need types and the exportData is invalid. We fall back to (incompletely)
+ // typechecking packages from source if they fail to compile.
+ (ld.Mode&NeedTypes|NeedTypesInfo != 0 && exportDataInvalid)) && pkg.PkgPath != "unsafe"
lpkg := &loaderPackage{
Package: pkg,
- needtypes: (ld.Mode&(NeedTypes|NeedTypesInfo) != 0 && ld.Mode&NeedDeps != 0 && rootIndex < 0) || rootIndex >= 0,
- needsrc: (ld.Mode&(NeedSyntax|NeedTypesInfo) != 0 && ld.Mode&NeedDeps != 0 && rootIndex < 0) || rootIndex >= 0 ||
- len(ld.Overlay) > 0 || // Overlays can invalidate export data. TODO(matloob): make this check fine-grained based on dependencies on overlaid files
- pkg.ExportFile == "" && pkg.PkgPath != "unsafe",
+ needtypes: needtypes,
+ needsrc: needsrc,
}
ld.pkgs[lpkg.ID] = lpkg
if rootIndex >= 0 {
@@ -713,7 +743,7 @@ func (ld *loader) loadPackage(lpkg *loaderPackage) {
// which would then require that such created packages be explicitly
// inserted back into the Import graph as a final step after export data loading.
// The Diamond test exercises this case.
- if !lpkg.needtypes {
+ if !lpkg.needtypes && !lpkg.needsrc {
return
}
if !lpkg.needsrc {