diff --git a/internal/imports/mod.go b/internal/imports/mod.go index 3d685332d1..7cc6c9db99 100644 --- a/internal/imports/mod.go +++ b/internal/imports/mod.go @@ -27,6 +27,8 @@ type ModuleResolver struct { Main *ModuleJSON ModsByModPath []*ModuleJSON // All modules, ordered by # of path components in module Path... ModsByDir []*ModuleJSON // ...or Dir. + + ModCachePkgs map[string]*pkg // Packages in the mod cache, keyed by absolute directory. } type ModuleJSON struct { @@ -87,6 +89,8 @@ func (r *ModuleResolver) init() error { return count(j) < count(i) // descending order }) + r.ModCachePkgs = make(map[string]*pkg) + r.Initialized = true return nil } @@ -232,6 +236,15 @@ func (r *ModuleResolver) scan(_ references) ([]*pkg, error) { dupCheck[dir] = true + absDir := dir + // Packages in the module cache are immutable. If we have + // already seen this package on a previous scan of the module + // cache, return that result. + if p, ok := r.ModCachePkgs[absDir]; ok { + result = append(result, p) + return + } + subdir := "" if dir != root.Path { subdir = dir[len(root.Path)+len("/"):] @@ -298,10 +311,18 @@ func (r *ModuleResolver) scan(_ references) ([]*pkg, error) { dir = canonicalDir } - result = append(result, &pkg{ + res := &pkg{ importPathShort: VendorlessPath(importPath), dir: dir, - }) + } + + switch root.Type { + case gopathwalk.RootModuleCache: + // Save the results of processing this directory. + r.ModCachePkgs[absDir] = res + } + + result = append(result, res) }, gopathwalk.Options{Debug: r.env.Debug, ModulesEnabled: true}) return result, nil } diff --git a/internal/imports/mod_test.go b/internal/imports/mod_test.go index e1d9c8ceec..a836307680 100644 --- a/internal/imports/mod_test.go +++ b/internal/imports/mod_test.go @@ -118,6 +118,25 @@ import _ "example.com" mt.assertFound("example.com", "x") } +// Tests that scanning the module cache > 1 time is able to find the same module. +func TestModMultipleScans(t *testing.T) { + mt := setup(t, ` +-- go.mod -- +module x + +require example.com v1.0.0 + +-- x.go -- +package x +import _ "example.com" +`, "") + defer mt.cleanup() + + mt.assertScanFinds("example.com", "x") + mt.assertScanFinds("example.com", "x") + +} + // Tests that -mod=vendor sort of works. Adapted from mod_getmode_vendor.txt. func TestModeGetmodeVendor(t *testing.T) { mt := setup(t, ` diff --git a/internal/lsp/source/format.go b/internal/lsp/source/format.go index f7a5530115..e9f12d2c61 100644 --- a/internal/lsp/source/format.go +++ b/internal/lsp/source/format.go @@ -75,6 +75,7 @@ func Imports(ctx context.Context, view View, f GoFile, rng span.Range) ([]TextEd resolver.Main = nil resolver.ModsByModPath = nil resolver.ModsByDir = nil + resolver.ModCachePkgs = nil } options := &imports.Options{ Env: view.ProcessEnv(ctx),