Skip to content

Commit 7cc3be7

Browse files
committed
internal/imports: use a clean GOMODCACHE for the scan root directory
The directories processed by gopathwalk are clean, yet in the scan callback are assumed to have the root as a prefix. For the module cache, this root was previous not guaranteed to be clean, if it came from a GOMODCACHE environment variable. As a result, the computed relative path may be inaccurate, and may even panic if the unclean root is much longer than its clean form. Reproduce the crash of golang/go#67156 in a test, update scanDirForPackage to be more robust, and fix the uncleanliness of the module cache root. Also fix some handling of GOMODCACHE cleanup in imports tests. Fixes golang/go#67156 Change-Id: Ia899256fed9629b7e753a52feb02b4235bfc8388 Reviewed-on: https://go-review.googlesource.com/c/tools/+/603635 LUCI-TryBot-Result: Go LUCI <[email protected]> Reviewed-by: Alan Donovan <[email protected]>
1 parent d47b4fb commit 7cc3be7

File tree

2 files changed

+67
-14
lines changed

2 files changed

+67
-14
lines changed

gopls/internal/test/integration/misc/imports_test.go

Lines changed: 61 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ package misc
66

77
import (
88
"os"
9+
"os/exec"
910
"path/filepath"
1011
"strings"
1112
"testing"
@@ -195,8 +196,7 @@ func main() {
195196
})
196197
}
197198

198-
func TestGOMODCACHE(t *testing.T) {
199-
const proxy = `
199+
const exampleProxy = `
200200
-- [email protected]/go.mod --
201201
module example.com
202202
@@ -210,31 +210,29 @@ package y
210210
211211
const Y = 2
212212
`
213+
214+
func TestGOMODCACHE(t *testing.T) {
213215
const files = `
214216
-- go.mod --
215217
module mod.com
216218
217219
go 1.12
218220
219221
require example.com v1.2.3
220-
-- go.sum --
221-
example.com v1.2.3 h1:6vTQqzX+pnwngZF1+5gcO3ZEWmix1jJ/h+pWS8wUxK0=
222-
example.com v1.2.3/go.mod h1:Y2Rc5rVWjWur0h3pd9aEvK5Pof8YKDANh9gHA2Maujo=
223222
-- main.go --
224223
package main
225224
226225
import "example.com/x"
227226
228227
var _, _ = x.X, y.Y
229228
`
230-
modcache, err := os.MkdirTemp("", "TestGOMODCACHE-modcache")
231-
if err != nil {
232-
t.Fatal(err)
233-
}
234-
defer os.RemoveAll(modcache)
229+
modcache := t.TempDir()
230+
defer cleanModCache(t, modcache) // see doc comment of cleanModCache
231+
235232
WithOptions(
236233
EnvVars{"GOMODCACHE": modcache},
237-
ProxyFiles(proxy),
234+
ProxyFiles(exampleProxy),
235+
WriteGoSum("."),
238236
).Run(t, files, func(t *testing.T, env *Env) {
239237
env.OpenFile("main.go")
240238
env.AfterChange(Diagnostics(env.AtRegexp("main.go", `y.Y`)))
@@ -248,6 +246,58 @@ var _, _ = x.X, y.Y
248246
})
249247
}
250248

249+
func TestRelativeReplace(t *testing.T) {
250+
const files = `
251+
-- go.mod --
252+
module mod.com/a
253+
254+
go 1.20
255+
256+
require (
257+
example.com v1.2.3
258+
)
259+
260+
replace example.com/b => ../b
261+
-- main.go --
262+
package main
263+
264+
import "example.com/x"
265+
266+
var _, _ = x.X, y.Y
267+
`
268+
modcache := t.TempDir()
269+
base := filepath.Base(modcache)
270+
defer cleanModCache(t, modcache) // see doc comment of cleanModCache
271+
272+
// Construct a very unclean module cache whose length exceeds the length of
273+
// the clean directory path, to reproduce the crash in golang/go#67156
274+
const sep = string(filepath.Separator)
275+
modcache += strings.Repeat(sep+".."+sep+base, 10)
276+
277+
WithOptions(
278+
EnvVars{"GOMODCACHE": modcache},
279+
ProxyFiles(exampleProxy),
280+
WriteGoSum("."),
281+
).Run(t, files, func(t *testing.T, env *Env) {
282+
env.OpenFile("main.go")
283+
env.AfterChange(Diagnostics(env.AtRegexp("main.go", `y.Y`)))
284+
env.SaveBuffer("main.go")
285+
env.AfterChange(NoDiagnostics(ForFile("main.go")))
286+
})
287+
}
288+
289+
// TODO(rfindley): this is only necessary as the module cache cleaning of the
290+
// sandbox does not respect GOMODCACHE set via EnvVars. We should fix this, but
291+
// that is probably part of a larger refactoring of the sandbox that I'm not
292+
// inclined to undertake.
293+
func cleanModCache(t *testing.T, modcache string) {
294+
cmd := exec.Command("go", "clean", "-modcache")
295+
cmd.Env = append(os.Environ(), "GOMODCACHE="+modcache)
296+
if err := cmd.Run(); err != nil {
297+
t.Errorf("cleaning modcache: %v", err)
298+
}
299+
}
300+
251301
// Tests golang/go#40685.
252302
func TestAcceptImportsQuickFixTestVariant(t *testing.T) {
253303
const pkg = `

internal/imports/mod.go

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -245,7 +245,10 @@ func newModuleResolver(e *ProcessEnv, moduleCacheCache *DirInfoCache) (*ModuleRe
245245
// 2. Use this to separate module cache scanning from other scanning.
246246
func gomodcacheForEnv(goenv map[string]string) string {
247247
if gmc := goenv["GOMODCACHE"]; gmc != "" {
248-
return gmc
248+
// golang/go#67156: ensure that the module cache is clean, since it is
249+
// assumed as a prefix to directories scanned by gopathwalk, which are
250+
// themselves clean.
251+
return filepath.Clean(gmc)
249252
}
250253
gopaths := filepath.SplitList(goenv["GOPATH"])
251254
if len(gopaths) == 0 {
@@ -740,8 +743,8 @@ func (r *ModuleResolver) loadExports(ctx context.Context, pkg *pkg, includeTest
740743

741744
func (r *ModuleResolver) scanDirForPackage(root gopathwalk.Root, dir string) directoryPackageInfo {
742745
subdir := ""
743-
if dir != root.Path {
744-
subdir = dir[len(root.Path)+len("/"):]
746+
if prefix := root.Path + string(filepath.Separator); strings.HasPrefix(dir, prefix) {
747+
subdir = dir[len(prefix):]
745748
}
746749
importPath := filepath.ToSlash(subdir)
747750
if strings.HasPrefix(importPath, "vendor/") {

0 commit comments

Comments
 (0)