|
|
|
@ -24,7 +24,7 @@ import (
@@ -24,7 +24,7 @@ import (
|
|
|
|
|
|
|
|
|
|
"golang.org/x/tools/go/internal/packagesdriver" |
|
|
|
|
"golang.org/x/tools/internal/gocommand" |
|
|
|
|
"golang.org/x/tools/internal/packagesinternal" |
|
|
|
|
"golang.org/x/xerrors" |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
// debug controls verbose logging.
|
|
|
|
@ -89,6 +89,10 @@ type golistState struct {
@@ -89,6 +89,10 @@ type golistState struct {
|
|
|
|
|
rootDirsError error |
|
|
|
|
rootDirs map[string]string |
|
|
|
|
|
|
|
|
|
goVersionOnce sync.Once |
|
|
|
|
goVersionError error |
|
|
|
|
goVersion string // third field of 'go version'
|
|
|
|
|
|
|
|
|
|
// vendorDirs caches the (non)existence of vendor directories.
|
|
|
|
|
vendorDirs map[string]bool |
|
|
|
|
} |
|
|
|
@ -142,7 +146,7 @@ func goListDriver(cfg *Config, patterns ...string) (*driverResponse, error) {
@@ -142,7 +146,7 @@ func goListDriver(cfg *Config, patterns ...string) (*driverResponse, error) {
|
|
|
|
|
sizeswg.Add(1) |
|
|
|
|
go func() { |
|
|
|
|
var sizes types.Sizes |
|
|
|
|
sizes, sizeserr = packagesdriver.GetSizesGolist(ctx, cfg.BuildFlags, cfg.Env, cfg.Dir, usesExportData(cfg)) |
|
|
|
|
sizes, sizeserr = packagesdriver.GetSizesGolist(ctx, cfg.BuildFlags, cfg.Env, cfg.gocmdRunner, cfg.Dir) |
|
|
|
|
// types.SizesFor always returns nil or a *types.StdSizes.
|
|
|
|
|
response.dr.Sizes, _ = sizes.(*types.StdSizes) |
|
|
|
|
sizeswg.Done() |
|
|
|
@ -381,7 +385,7 @@ type jsonPackage struct {
@@ -381,7 +385,7 @@ type jsonPackage struct {
|
|
|
|
|
Imports []string |
|
|
|
|
ImportMap map[string]string |
|
|
|
|
Deps []string |
|
|
|
|
Module *packagesinternal.Module |
|
|
|
|
Module *Module |
|
|
|
|
TestGoFiles []string |
|
|
|
|
TestImports []string |
|
|
|
|
XTestGoFiles []string |
|
|
|
@ -502,10 +506,19 @@ func (state *golistState) createDriverResponse(words ...string) (*driverResponse
@@ -502,10 +506,19 @@ func (state *golistState) createDriverResponse(words ...string) (*driverResponse
|
|
|
|
|
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) |
|
|
|
|
if len(old.Error.ImportStack) < 1 { |
|
|
|
|
return nil, fmt.Errorf(`internal error: go list gave a %q error with empty import stack`, errkind) |
|
|
|
|
} |
|
|
|
|
importingPkg := old.Error.ImportStack[len(old.Error.ImportStack)-1] |
|
|
|
|
if importingPkg == old.ImportPath { |
|
|
|
|
// Using an older version of Go which put this package itself on top of import
|
|
|
|
|
// stack, instead of the importer. Look for importer in second from top
|
|
|
|
|
// position.
|
|
|
|
|
if len(old.Error.ImportStack) < 2 { |
|
|
|
|
return nil, fmt.Errorf(`internal error: go list gave a %q error with an import stack without importing package`, errkind) |
|
|
|
|
} |
|
|
|
|
importingPkg = old.Error.ImportStack[len(old.Error.ImportStack)-2] |
|
|
|
|
} |
|
|
|
|
importingPkg := old.Error.ImportStack[len(old.Error.ImportStack)-2] |
|
|
|
|
additionalErrors[importingPkg] = append(additionalErrors[importingPkg], Error{ |
|
|
|
|
Pos: old.Error.Pos, |
|
|
|
|
Msg: old.Error.Err, |
|
|
|
@ -531,7 +544,26 @@ func (state *golistState) createDriverResponse(words ...string) (*driverResponse
@@ -531,7 +544,26 @@ func (state *golistState) createDriverResponse(words ...string) (*driverResponse
|
|
|
|
|
CompiledGoFiles: absJoin(p.Dir, p.CompiledGoFiles), |
|
|
|
|
OtherFiles: absJoin(p.Dir, otherFiles(p)...), |
|
|
|
|
forTest: p.ForTest, |
|
|
|
|
module: p.Module, |
|
|
|
|
Module: p.Module, |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (state.cfg.Mode&typecheckCgo) != 0 && len(p.CgoFiles) != 0 { |
|
|
|
|
if len(p.CompiledGoFiles) > len(p.GoFiles) { |
|
|
|
|
// We need the cgo definitions, which are in the first
|
|
|
|
|
// CompiledGoFile after the non-cgo ones. This is a hack but there
|
|
|
|
|
// isn't currently a better way to find it. We also need the pure
|
|
|
|
|
// Go files and unprocessed cgo files, all of which are already
|
|
|
|
|
// in pkg.GoFiles.
|
|
|
|
|
cgoTypes := p.CompiledGoFiles[len(p.GoFiles)] |
|
|
|
|
pkg.CompiledGoFiles = append([]string{cgoTypes}, pkg.GoFiles...) |
|
|
|
|
} else { |
|
|
|
|
// golang/go#38990: go list silently fails to do cgo processing
|
|
|
|
|
pkg.CompiledGoFiles = nil |
|
|
|
|
pkg.Errors = append(pkg.Errors, Error{ |
|
|
|
|
Msg: "go list failed to return CompiledGoFiles; https://golang.org/issue/38990?", |
|
|
|
|
Kind: ListError, |
|
|
|
|
}) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Work around https://golang.org/issue/28749:
|
|
|
|
@ -607,6 +639,39 @@ func (state *golistState) createDriverResponse(words ...string) (*driverResponse
@@ -607,6 +639,39 @@ func (state *golistState) createDriverResponse(words ...string) (*driverResponse
|
|
|
|
|
pkg.CompiledGoFiles = pkg.GoFiles |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Temporary work-around for golang/go#39986. Parse filenames out of
|
|
|
|
|
// error messages. This happens if there are unrecoverable syntax
|
|
|
|
|
// errors in the source, so we can't match on a specific error message.
|
|
|
|
|
if err := p.Error; err != nil && state.shouldAddFilenameFromError(p) { |
|
|
|
|
addFilenameFromPos := func(pos string) bool { |
|
|
|
|
split := strings.Split(pos, ":") |
|
|
|
|
if len(split) < 1 { |
|
|
|
|
return false |
|
|
|
|
} |
|
|
|
|
filename := strings.TrimSpace(split[0]) |
|
|
|
|
if filename == "" { |
|
|
|
|
return false |
|
|
|
|
} |
|
|
|
|
if !filepath.IsAbs(filename) { |
|
|
|
|
filename = filepath.Join(state.cfg.Dir, filename) |
|
|
|
|
} |
|
|
|
|
info, _ := os.Stat(filename) |
|
|
|
|
if info == nil { |
|
|
|
|
return false |
|
|
|
|
} |
|
|
|
|
pkg.CompiledGoFiles = append(pkg.CompiledGoFiles, filename) |
|
|
|
|
pkg.GoFiles = append(pkg.GoFiles, filename) |
|
|
|
|
return true |
|
|
|
|
} |
|
|
|
|
found := addFilenameFromPos(err.Pos) |
|
|
|
|
// In some cases, go list only reports the error position in the
|
|
|
|
|
// error text, not the error position. One such case is when the
|
|
|
|
|
// file's package name is a keyword (see golang.org/issue/39763).
|
|
|
|
|
if !found { |
|
|
|
|
addFilenameFromPos(err.Err) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if p.Error != nil { |
|
|
|
|
msg := strings.TrimSpace(p.Error.Err) // Trim to work around golang.org/issue/32363.
|
|
|
|
|
// Address golang.org/issue/35964 by appending import stack to error message.
|
|
|
|
@ -636,6 +701,58 @@ func (state *golistState) createDriverResponse(words ...string) (*driverResponse
@@ -636,6 +701,58 @@ func (state *golistState) createDriverResponse(words ...string) (*driverResponse
|
|
|
|
|
return &response, nil |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (state *golistState) shouldAddFilenameFromError(p *jsonPackage) bool { |
|
|
|
|
if len(p.GoFiles) > 0 || len(p.CompiledGoFiles) > 0 { |
|
|
|
|
return false |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
goV, err := state.getGoVersion() |
|
|
|
|
if err != nil { |
|
|
|
|
return false |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// On Go 1.14 and earlier, only add filenames from errors if the import stack is empty.
|
|
|
|
|
// The import stack behaves differently for these versions than newer Go versions.
|
|
|
|
|
if strings.HasPrefix(goV, "go1.13") || strings.HasPrefix(goV, "go1.14") { |
|
|
|
|
return len(p.Error.ImportStack) == 0 |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// On Go 1.15 and later, only parse filenames out of error if there's no import stack,
|
|
|
|
|
// or the current package is at the top of the import stack. This is not guaranteed
|
|
|
|
|
// to work perfectly, but should avoid some cases where files in errors don't belong to this
|
|
|
|
|
// package.
|
|
|
|
|
return len(p.Error.ImportStack) == 0 || p.Error.ImportStack[len(p.Error.ImportStack)-1] == p.ImportPath |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (state *golistState) getGoVersion() (string, error) { |
|
|
|
|
state.goVersionOnce.Do(func() { |
|
|
|
|
var b *bytes.Buffer |
|
|
|
|
// Invoke go version. Don't use invokeGo because it will supply build flags, and
|
|
|
|
|
// go version doesn't expect build flags.
|
|
|
|
|
inv := gocommand.Invocation{ |
|
|
|
|
Verb: "version", |
|
|
|
|
Env: state.cfg.Env, |
|
|
|
|
Logf: state.cfg.Logf, |
|
|
|
|
} |
|
|
|
|
gocmdRunner := state.cfg.gocmdRunner |
|
|
|
|
if gocmdRunner == nil { |
|
|
|
|
gocmdRunner = &gocommand.Runner{} |
|
|
|
|
} |
|
|
|
|
b, _, _, state.goVersionError = gocmdRunner.RunRaw(state.cfg.Context, inv) |
|
|
|
|
if state.goVersionError != nil { |
|
|
|
|
return |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
sp := strings.Split(b.String(), " ") |
|
|
|
|
if len(sp) < 3 { |
|
|
|
|
state.goVersionError = fmt.Errorf("go version output: expected 'go version <version>', got '%s'", b.String()) |
|
|
|
|
return |
|
|
|
|
} |
|
|
|
|
state.goVersion = sp[2] |
|
|
|
|
}) |
|
|
|
|
return state.goVersion, state.goVersionError |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// getPkgPath finds the package path of a directory if it's relative to a root directory.
|
|
|
|
|
func (state *golistState) getPkgPath(dir string) (string, bool, error) { |
|
|
|
|
absDir, err := filepath.Abs(dir) |
|
|
|
@ -707,7 +824,7 @@ func golistargs(cfg *Config, words []string) []string {
@@ -707,7 +824,7 @@ func golistargs(cfg *Config, words []string) []string {
|
|
|
|
|
func (state *golistState) invokeGo(verb string, args ...string) (*bytes.Buffer, error) { |
|
|
|
|
cfg := state.cfg |
|
|
|
|
|
|
|
|
|
inv := &gocommand.Invocation{ |
|
|
|
|
inv := gocommand.Invocation{ |
|
|
|
|
Verb: verb, |
|
|
|
|
Args: args, |
|
|
|
|
BuildFlags: cfg.BuildFlags, |
|
|
|
@ -715,8 +832,11 @@ func (state *golistState) invokeGo(verb string, args ...string) (*bytes.Buffer,
@@ -715,8 +832,11 @@ func (state *golistState) invokeGo(verb string, args ...string) (*bytes.Buffer,
|
|
|
|
|
Logf: cfg.Logf, |
|
|
|
|
WorkingDir: cfg.Dir, |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
stdout, stderr, _, err := inv.RunRaw(cfg.Context) |
|
|
|
|
gocmdRunner := cfg.gocmdRunner |
|
|
|
|
if gocmdRunner == nil { |
|
|
|
|
gocmdRunner = &gocommand.Runner{} |
|
|
|
|
} |
|
|
|
|
stdout, stderr, _, err := gocmdRunner.RunRaw(cfg.Context, inv) |
|
|
|
|
if err != nil { |
|
|
|
|
// Check for 'go' executable not being found.
|
|
|
|
|
if ee, ok := err.(*exec.Error); ok && ee.Err == exec.ErrNotFound { |
|
|
|
@ -727,7 +847,7 @@ func (state *golistState) invokeGo(verb string, args ...string) (*bytes.Buffer,
@@ -727,7 +847,7 @@ func (state *golistState) invokeGo(verb string, args ...string) (*bytes.Buffer,
|
|
|
|
|
if !ok { |
|
|
|
|
// Catastrophic error:
|
|
|
|
|
// - context cancellation
|
|
|
|
|
return nil, fmt.Errorf("couldn't run 'go': %v", err) |
|
|
|
|
return nil, xerrors.Errorf("couldn't run 'go': %w", err) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Old go version?
|
|
|
|
|