internal/gocommand: add a GoVersion function to avoid duplicating logic
It seems easier to have this as a shared internal function.
Updates golang/go#41598
Change-Id: If35bdbdf5499624dd55ae9daede1ff1ae9495026
Reviewed-on: https://go-review.googlesource.com/c/tools/+/263985
Trust: Rebecca Stambler <rstambler@golang.org>
Run-TryBot: Rebecca Stambler <rstambler@golang.org>
gopls-CI: kokoro <noreply+kokoro@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Peter Weinberger <pjw@google.com>
Reviewed-by: Heschi Kreinick <heschi@google.com>
diff --git a/go/packages/golist.go b/go/packages/golist.go
index 2696cfb..787783c 100644
--- a/go/packages/golist.go
+++ b/go/packages/golist.go
@@ -91,7 +91,7 @@
goVersionOnce sync.Once
goVersionError error
- goVersion string // third field of 'go version'
+ goVersion int // The X in Go 1.X.
// vendorDirs caches the (non)existence of vendor directories.
vendorDirs map[string]bool
@@ -731,7 +731,7 @@
// 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") {
+ if goV < 15 {
return len(p.Error.ImportStack) == 0
}
@@ -742,31 +742,9 @@
return len(p.Error.ImportStack) == 0 || p.Error.ImportStack[len(p.Error.ImportStack)-1] == p.ImportPath
}
-func (state *golistState) getGoVersion() (string, error) {
+func (state *golistState) getGoVersion() (int, 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]
+ state.goVersion, state.goVersionError = gocommand.GoVersion(state.ctx, state.cfgInvocation(), state.cfg.gocmdRunner)
})
return state.goVersion, state.goVersionError
}
diff --git a/internal/gocommand/version.go b/internal/gocommand/version.go
new file mode 100644
index 0000000..60d45ac
--- /dev/null
+++ b/internal/gocommand/version.go
@@ -0,0 +1,40 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package gocommand
+
+import (
+ "context"
+ "fmt"
+ "strings"
+)
+
+// GoVersion checks the go version by running "go list" with modules off.
+// It returns the X in Go 1.X.
+func GoVersion(ctx context.Context, inv Invocation, r *Runner) (int, error) {
+ inv.Verb = "list"
+ inv.Args = []string{"-e", "-f", `{{context.ReleaseTags}}`}
+ inv.Env = append(append([]string{}, inv.Env...), "GO111MODULE=off")
+ // Unset any unneeded flags.
+ inv.ModFile = ""
+ inv.ModFlag = ""
+ stdoutBytes, err := r.Run(ctx, inv)
+ if err != nil {
+ return 0, err
+ }
+ stdout := stdoutBytes.String()
+ if len(stdout) < 3 {
+ return 0, fmt.Errorf("bad ReleaseTags output: %q", stdout)
+ }
+ // Split up "[go1.1 go1.15]"
+ tags := strings.Fields(stdout[1 : len(stdout)-2])
+ for i := len(tags) - 1; i >= 0; i-- {
+ var version int
+ if _, err := fmt.Sscanf(tags[i], "go1.%d", &version); err != nil {
+ continue
+ }
+ return version, nil
+ }
+ return 0, fmt.Errorf("no parseable ReleaseTags in %v", tags)
+}
diff --git a/internal/lsp/cache/session.go b/internal/lsp/cache/session.go
index 9affdb5..733c937 100644
--- a/internal/lsp/cache/session.go
+++ b/internal/lsp/cache/session.go
@@ -650,34 +650,3 @@
}
return overlays
}
-
-// goVersion returns the Go version in use for the given session.
-func (s *Session) goVersion(ctx context.Context, folder string, env []string) (int, error) {
- // Check the go version by running "go list" with modules off.
- // Borrowed from internal/imports/mod.go:620.
- const format = `{{context.ReleaseTags}}`
- inv := gocommand.Invocation{
- Verb: "list",
- Args: []string{"-e", "-f", format},
- Env: append(env, "GO111MODULE=off"),
- WorkingDir: folder,
- }
- stdoutBytes, err := s.gocmdRunner.Run(ctx, inv)
- if err != nil {
- return 0, err
- }
- stdout := stdoutBytes.String()
- if len(stdout) < 3 {
- return 0, fmt.Errorf("bad ReleaseTags output: %q", stdout)
- }
- // Split up "[go1.1 go1.15]"
- tags := strings.Fields(stdout[1 : len(stdout)-2])
- for i := len(tags) - 1; i >= 0; i-- {
- var version int
- if _, err := fmt.Sscanf(tags[i], "go1.%d", &version); err != nil {
- continue
- }
- return version, nil
- }
- return 0, fmt.Errorf("no parseable ReleaseTags in %v", tags)
-}
diff --git a/internal/lsp/cache/view.go b/internal/lsp/cache/view.go
index 8b3d307..b20182a 100644
--- a/internal/lsp/cache/view.go
+++ b/internal/lsp/cache/view.go
@@ -631,7 +631,11 @@
return nil, errors.Errorf("invalid workspace configuration: %w", err)
}
var err error
- goversion, err := s.goVersion(ctx, folder.Filename(), options.EnvSlice())
+ inv := gocommand.Invocation{
+ WorkingDir: folder.Filename(),
+ Env: options.EnvSlice(),
+ }
+ goversion, err := gocommand.GoVersion(ctx, inv, s.gocmdRunner)
if err != nil {
return nil, err
}