internal/imports: save scanned module cache results

Save the packages found when scanning of the module cache.
The computed package may have a different import path due
to replace directives, so this needs to be updated
when the moduleResolver is initialized again.

Change-Id: Ib575fcc59b814ff263b431362df3698839a282f6
Reviewed-on: https://go-review.googlesource.com/c/tools/+/186301
Run-TryBot: Suzy Mueller <suzmue@golang.org>
Reviewed-by: Heschi Kreinick <heschi@google.com>
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
diff --git a/internal/imports/mod.go b/internal/imports/mod.go
index 3d68533..7cc6c9d 100644
--- a/internal/imports/mod.go
+++ b/internal/imports/mod.go
@@ -27,6 +27,8 @@
 	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 @@
 		return count(j) < count(i) // descending order
 	})
 
+	r.ModCachePkgs = make(map[string]*pkg)
+
 	r.Initialized = true
 	return nil
 }
@@ -232,6 +236,15 @@
 
 		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 @@
 			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 e1d9c8c..a836307 100644
--- a/internal/imports/mod_test.go
+++ b/internal/imports/mod_test.go
@@ -118,6 +118,25 @@
 	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 f7a5530..e9f12d2 100644
--- a/internal/lsp/source/format.go
+++ b/internal/lsp/source/format.go
@@ -75,6 +75,7 @@
 		resolver.Main = nil
 		resolver.ModsByModPath = nil
 		resolver.ModsByDir = nil
+		resolver.ModCachePkgs = nil
 	}
 	options := &imports.Options{
 		Env: view.ProcessEnv(ctx),