internal/imports: use a module resolver if GOWORK is set

Previously, gopls would fall back on a gopath resolver when running
goimports from a directory containing go.work (but not go.mod). Fix this
by update the code to recognize that GOWORK also puts goimports into
module mode.

All the work to _support_ go.work had already been done, but the tests
were only passing because they were setting GO111MODULE=on explicitly
(and therefore GOMOD=/dev/null was satisfying the pre-existing check).

Also add a test for the regression in gopls.

Fixes golang/go#52784

Change-Id: I31df6f71a949a5668e8dc001b3ee25ad26f2f927
Reviewed-on: https://go-review.googlesource.com/c/tools/+/413689
TryBot-Result: Gopher Robot <gobot@golang.org>
Run-TryBot: Robert Findley <rfindley@google.com>
gopls-CI: kokoro <noreply+kokoro@google.com>
Reviewed-by: Michael Matloob <matloob@golang.org>
This commit is contained in:
Robert Findley 2022-06-24 16:31:42 -04:00
Родитель 60ca6366e6
Коммит e1ec1f3230
5 изменённых файлов: 93 добавлений и 34 удалений

Просмотреть файл

@ -214,3 +214,49 @@ func TestA(t *testing.T) {
)
})
}
// Test for golang/go#52784
func TestGoWorkImports(t *testing.T) {
testenv.NeedsGo1Point(t, 18)
const pkg = `
-- go.work --
go 1.19
use (
./caller
./mod
)
-- caller/go.mod --
module caller.com
go 1.18
require mod.com v0.0.0
replace mod.com => ../mod
-- caller/caller.go --
package main
func main() {
a.Test()
}
-- mod/go.mod --
module mod.com
go 1.18
-- mod/a/a.go --
package a
func Test() {
}
`
Run(t, pkg, func(t *testing.T, env *Env) {
env.OpenFile("caller/caller.go")
env.Await(env.DiagnosticAtRegexp("caller/caller.go", "a.Test"))
// Saving caller.go should trigger goimports, which should find a.Test in
// the mod.com module, thanks to the go.work file.
env.SaveBuffer("caller/caller.go")
env.Await(EmptyDiagnostics("caller/caller.go"))
})
}

Просмотреть файл

@ -796,7 +796,7 @@ func GetPackageExports(ctx context.Context, wrapped func(PackageExport), searchP
return getCandidatePkgs(ctx, callback, filename, filePkg, env)
}
var RequiredGoEnvVars = []string{"GO111MODULE", "GOFLAGS", "GOINSECURE", "GOMOD", "GOMODCACHE", "GONOPROXY", "GONOSUMDB", "GOPATH", "GOPROXY", "GOROOT", "GOSUMDB"}
var RequiredGoEnvVars = []string{"GO111MODULE", "GOFLAGS", "GOINSECURE", "GOMOD", "GOMODCACHE", "GONOPROXY", "GONOSUMDB", "GOPATH", "GOPROXY", "GOROOT", "GOSUMDB", "GOWORK"}
// ProcessEnv contains environment variables and settings that affect the use of
// the go command, the go/build package, etc.
@ -906,7 +906,7 @@ func (e *ProcessEnv) GetResolver() (Resolver, error) {
if err := e.init(); err != nil {
return nil, err
}
if len(e.Env["GOMOD"]) == 0 {
if len(e.Env["GOMOD"]) == 0 && len(e.Env["GOWORK"]) == 0 {
e.resolver = newGopathResolver(e)
return e.resolver, nil
}

Просмотреть файл

@ -70,9 +70,17 @@ func (r *ModuleResolver) init() error {
Logf: r.env.Logf,
WorkingDir: r.env.WorkingDir,
}
vendorEnabled, mainModVendor, err := gocommand.VendorEnabled(context.TODO(), inv, r.env.GocmdRunner)
if err != nil {
return err
vendorEnabled := false
var mainModVendor *gocommand.ModuleJSON
// Module vendor directories are ignored in workspace mode:
// https://go.googlesource.com/proposal/+/master/design/45713-workspace.md
if len(r.env.Env["GOWORK"]) == 0 {
vendorEnabled, mainModVendor, err = gocommand.VendorEnabled(context.TODO(), inv, r.env.GocmdRunner)
if err != nil {
return err
}
}
if mainModVendor != nil && vendorEnabled {

Просмотреть файл

@ -29,7 +29,7 @@ import (
// Tests that we can find packages in the stdlib.
func TestScanStdlib(t *testing.T) {
mt := setup(t, `
mt := setup(t, nil, `
-- go.mod --
module x
`, "")
@ -42,7 +42,7 @@ module x
// where the module is in scope -- here we have to figure out the import path
// without any help from go list.
func TestScanOutOfScopeNestedModule(t *testing.T) {
mt := setup(t, `
mt := setup(t, nil, `
-- go.mod --
module x
@ -68,7 +68,7 @@ package x`, "")
// Tests that we don't find a nested module contained in a local replace target.
// The code for this case is too annoying to write, so it's just ignored.
func TestScanNestedModuleInLocalReplace(t *testing.T) {
mt := setup(t, `
mt := setup(t, nil, `
-- go.mod --
module x
@ -107,7 +107,7 @@ package z
// Tests that path encoding is handled correctly. Adapted from mod_case.txt.
func TestModCase(t *testing.T) {
mt := setup(t, `
mt := setup(t, nil, `
-- go.mod --
module x
@ -124,7 +124,7 @@ import _ "rsc.io/QUOTE/QUOTE"
// Not obviously relevant to goimports. Adapted from mod_domain_root.txt anyway.
func TestModDomainRoot(t *testing.T) {
mt := setup(t, `
mt := setup(t, nil, `
-- go.mod --
module x
@ -140,7 +140,7 @@ import _ "example.com"
// Tests that scanning the module cache > 1 time is able to find the same module.
func TestModMultipleScans(t *testing.T) {
mt := setup(t, `
mt := setup(t, nil, `
-- go.mod --
module x
@ -159,7 +159,7 @@ import _ "example.com"
// Tests that scanning the module cache > 1 time is able to find the same module
// in the module cache.
func TestModMultipleScansWithSubdirs(t *testing.T) {
mt := setup(t, `
mt := setup(t, nil, `
-- go.mod --
module x
@ -178,7 +178,7 @@ import _ "rsc.io/quote"
// Tests that scanning the module cache > 1 after changing a package in module cache to make it unimportable
// is able to find the same module.
func TestModCacheEditModFile(t *testing.T) {
mt := setup(t, `
mt := setup(t, nil, `
-- go.mod --
module x
@ -219,7 +219,7 @@ import _ "rsc.io/quote"
// Tests that -mod=vendor works. Adapted from mod_vendor_build.txt.
func TestModVendorBuild(t *testing.T) {
mt := setup(t, `
mt := setup(t, nil, `
-- go.mod --
module m
go 1.12
@ -250,7 +250,7 @@ import _ "rsc.io/sampler"
// Tests that -mod=vendor is auto-enabled only for go1.14 and higher.
// Vaguely inspired by mod_vendor_auto.txt.
func TestModVendorAuto(t *testing.T) {
mt := setup(t, `
mt := setup(t, nil, `
-- go.mod --
module m
go 1.14
@ -276,7 +276,7 @@ import _ "rsc.io/sampler"
// Tests that a module replace works. Adapted from mod_list.txt. We start with
// go.mod2; the first part of the test is irrelevant.
func TestModList(t *testing.T) {
mt := setup(t, `
mt := setup(t, nil, `
-- go.mod --
module x
require rsc.io/quote v1.5.1
@ -293,7 +293,7 @@ import _ "rsc.io/quote"
// Tests that a local replace works. Adapted from mod_local_replace.txt.
func TestModLocalReplace(t *testing.T) {
mt := setup(t, `
mt := setup(t, nil, `
-- x/y/go.mod --
module x/y
require zz v1.0.0
@ -317,7 +317,7 @@ package z
// Tests that the package at the root of the main module can be found.
// Adapted from the first part of mod_multirepo.txt.
func TestModMultirepo1(t *testing.T) {
mt := setup(t, `
mt := setup(t, nil, `
-- go.mod --
module rsc.io/quote
@ -333,7 +333,7 @@ package quote
// of mod_multirepo.txt (We skip the case where it doesn't have a go.mod
// entry -- we just don't work in that case.)
func TestModMultirepo3(t *testing.T) {
mt := setup(t, `
mt := setup(t, nil, `
-- go.mod --
module rsc.io/quote
@ -352,7 +352,7 @@ import _ "rsc.io/quote/v2"
// Tests that a nested module is found in the module cache, even though
// it's checked out. Adapted from the fourth part of mod_multirepo.txt.
func TestModMultirepo4(t *testing.T) {
mt := setup(t, `
mt := setup(t, nil, `
-- go.mod --
module rsc.io/quote
require rsc.io/quote/v2 v2.0.1
@ -376,7 +376,7 @@ import _ "rsc.io/quote/v2"
// Tests a simple module dependency. Adapted from the first part of mod_replace.txt.
func TestModReplace1(t *testing.T) {
mt := setup(t, `
mt := setup(t, nil, `
-- go.mod --
module quoter
@ -392,7 +392,7 @@ package main
// Tests a local replace. Adapted from the second part of mod_replace.txt.
func TestModReplace2(t *testing.T) {
mt := setup(t, `
mt := setup(t, nil, `
-- go.mod --
module quoter
@ -418,7 +418,7 @@ import "rsc.io/sampler"
// Tests that a module can be replaced by a different module path. Adapted
// from the third part of mod_replace.txt.
func TestModReplace3(t *testing.T) {
mt := setup(t, `
mt := setup(t, nil, `
-- go.mod --
module quoter
@ -451,7 +451,7 @@ package quote
// mod_replace_import.txt, with example.com/v changed to /vv because Go 1.11
// thinks /v is an invalid major version.
func TestModReplaceImport(t *testing.T) {
mt := setup(t, `
mt := setup(t, nil, `
-- go.mod --
module example.com/m
@ -556,7 +556,7 @@ package v
func TestModWorkspace(t *testing.T) {
testenv.NeedsGo1Point(t, 18)
mt := setup(t, `
mt := setup(t, nil, `
-- go.work --
go 1.18
@ -592,7 +592,7 @@ package b
func TestModWorkspaceReplace(t *testing.T) {
testenv.NeedsGo1Point(t, 18)
mt := setup(t, `
mt := setup(t, nil, `
-- go.work --
use m
@ -651,7 +651,7 @@ func G() {
func TestModWorkspaceReplaceOverride(t *testing.T) {
testenv.NeedsGo1Point(t, 18)
mt := setup(t, `-- go.work --
mt := setup(t, nil, `-- go.work --
use m
use n
replace example.com/dep => ./dep3
@ -716,7 +716,7 @@ func G() {
func TestModWorkspacePrune(t *testing.T) {
testenv.NeedsGo1Point(t, 18)
mt := setup(t, `
mt := setup(t, nil, `
-- go.work --
go 1.18
@ -885,7 +885,7 @@ package z
// Tests that we handle GO111MODULE=on with no go.mod file. See #30855.
func TestNoMainModule(t *testing.T) {
testenv.NeedsGo1Point(t, 12)
mt := setup(t, `
mt := setup(t, map[string]string{"GO111MODULE": "on"}, `
-- x.go --
package x
`, "")
@ -993,7 +993,9 @@ type modTest struct {
// setup builds a test environment from a txtar and supporting modules
// in testdata/mod, along the lines of TestScript in cmd/go.
func setup(t *testing.T, main, wd string) *modTest {
//
// extraEnv is applied on top of the default test env.
func setup(t *testing.T, extraEnv map[string]string, main, wd string) *modTest {
t.Helper()
testenv.NeedsGo1Point(t, 11)
testenv.NeedsTool(t, "go")
@ -1023,13 +1025,16 @@ func setup(t *testing.T, main, wd string) *modTest {
Env: map[string]string{
"GOPATH": filepath.Join(dir, "gopath"),
"GOMODCACHE": "",
"GO111MODULE": "on",
"GO111MODULE": "auto",
"GOSUMDB": "off",
"GOPROXY": proxydir.ToURL(proxyDir),
},
WorkingDir: filepath.Join(mainDir, wd),
GocmdRunner: &gocommand.Runner{},
}
for k, v := range extraEnv {
env.Env[k] = v
}
if *testDebug {
env.Logf = log.Printf
}
@ -1168,7 +1173,7 @@ func removeDir(dir string) {
// Tests that findModFile can find the mod files from a path in the module cache.
func TestFindModFileModCache(t *testing.T) {
mt := setup(t, `
mt := setup(t, nil, `
-- go.mod --
module x
@ -1220,7 +1225,7 @@ func TestInvalidModCache(t *testing.T) {
}
func TestGetCandidatesRanking(t *testing.T) {
mt := setup(t, `
mt := setup(t, nil, `
-- go.mod --
module example.com

2
internal/lsp/cache/load.go поставляемый
Просмотреть файл

@ -253,7 +253,7 @@ func (m *moduleErrorMap) Error() string {
var buf bytes.Buffer
fmt.Fprintf(&buf, "%d modules have errors:\n", len(paths))
for _, path := range paths {
fmt.Fprintf(&buf, "\t%s", m.errs[path][0].Msg)
fmt.Fprintf(&buf, "\t%s:%s\n", path, m.errs[path][0].Msg)
}
return buf.String()