diff options
author | Lunny Xiao <xiaolunwen@gmail.com> | 2020-04-04 03:29:12 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-04-03 22:29:12 +0300 |
commit | 4f63f283c47dcf9e705ce5b8e8857f2b42cff8ad (patch) | |
tree | dd5dc2cae6ebae21826ffcce937533559be45a07 /vendor/golang.org/x/tools/go | |
parent | 4af7c47b38d382d105726f9553a1a68d46882cbf (diff) | |
download | gitea-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')
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 { |