internal/lsp/cache: let gopls track go.mod files

We used to read the go.mod file information out of the imports.Resolver.
Now that gopls tracks go.mod itself, we can use that instead. This is a
slight regression, in that go.mods in replace targets will no longer be
watched, but I don't think that's too important.

This allows us to stop reading the ModuleResolver's internals, which
were not sufficiently locked.

Updates golang/go#36605.

Change-Id: I42939e0248cba1f6b3850a003de67fcc11ab10b1
Reviewed-on: https://go-review.googlesource.com/c/tools/+/215319
Run-TryBot: Heschi Kreinick <heschi@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
diff --git a/internal/imports/mod.go b/internal/imports/mod.go
index 553f216..3ae859e 100644
--- a/internal/imports/mod.go
+++ b/internal/imports/mod.go
@@ -29,10 +29,10 @@
 	scanSema       chan struct{} // scanSema prevents concurrent scans and guards scannedRoots.
 	scannedRoots   map[gopathwalk.Root]bool
 
-	Initialized   bool
-	Main          *ModuleJSON
-	ModsByModPath []*ModuleJSON // All modules, ordered by # of path components in module Path...
-	ModsByDir     []*ModuleJSON // ...or Dir.
+	initialized   bool
+	main          *ModuleJSON
+	modsByModPath []*ModuleJSON // All modules, ordered by # of path components in module Path...
+	modsByDir     []*ModuleJSON // ...or Dir.
 
 	// moduleCacheCache stores information about the module cache.
 	moduleCacheCache *dirInfoCache
@@ -59,7 +59,7 @@
 }
 
 func (r *ModuleResolver) init() error {
-	if r.Initialized {
+	if r.initialized {
 		return nil
 	}
 	mainMod, vendorEnabled, err := vendorEnabled(r.env)
@@ -70,13 +70,13 @@
 	if mainMod != nil && vendorEnabled {
 		// Vendor mode is on, so all the non-Main modules are irrelevant,
 		// and we need to search /vendor for everything.
-		r.Main = mainMod
+		r.main = mainMod
 		r.dummyVendorMod = &ModuleJSON{
 			Path: "",
 			Dir:  filepath.Join(mainMod.Dir, "vendor"),
 		}
-		r.ModsByModPath = []*ModuleJSON{mainMod, r.dummyVendorMod}
-		r.ModsByDir = []*ModuleJSON{mainMod, r.dummyVendorMod}
+		r.modsByModPath = []*ModuleJSON{mainMod, r.dummyVendorMod}
+		r.modsByDir = []*ModuleJSON{mainMod, r.dummyVendorMod}
 	} else {
 		// Vendor mode is off, so run go list -m ... to find everything.
 		r.initAllMods()
@@ -84,15 +84,15 @@
 
 	r.moduleCacheDir = filepath.Join(filepath.SplitList(r.env.GOPATH)[0], "/pkg/mod")
 
-	sort.Slice(r.ModsByModPath, func(i, j int) bool {
+	sort.Slice(r.modsByModPath, func(i, j int) bool {
 		count := func(x int) int {
-			return strings.Count(r.ModsByModPath[x].Path, "/")
+			return strings.Count(r.modsByModPath[x].Path, "/")
 		}
 		return count(j) < count(i) // descending order
 	})
-	sort.Slice(r.ModsByDir, func(i, j int) bool {
+	sort.Slice(r.modsByDir, func(i, j int) bool {
 		count := func(x int) int {
-			return strings.Count(r.ModsByDir[x].Dir, "/")
+			return strings.Count(r.modsByDir[x].Dir, "/")
 		}
 		return count(j) < count(i) // descending order
 	})
@@ -100,8 +100,8 @@
 	r.roots = []gopathwalk.Root{
 		{filepath.Join(r.env.GOROOT, "/src"), gopathwalk.RootGOROOT},
 	}
-	if r.Main != nil {
-		r.roots = append(r.roots, gopathwalk.Root{r.Main.Dir, gopathwalk.RootCurrentModule})
+	if r.main != nil {
+		r.roots = append(r.roots, gopathwalk.Root{r.main.Dir, gopathwalk.RootCurrentModule})
 	}
 	if vendorEnabled {
 		r.roots = append(r.roots, gopathwalk.Root{r.dummyVendorMod.Dir, gopathwalk.RootOther})
@@ -115,12 +115,12 @@
 			}
 		}
 		// Walk dependent modules before scanning the full mod cache, direct deps first.
-		for _, mod := range r.ModsByModPath {
+		for _, mod := range r.modsByModPath {
 			if !mod.Indirect && !mod.Main {
 				addDep(mod)
 			}
 		}
-		for _, mod := range r.ModsByModPath {
+		for _, mod := range r.modsByModPath {
 			if mod.Indirect && !mod.Main {
 				addDep(mod)
 			}
@@ -141,7 +141,7 @@
 			listeners: map[*int]cacheListener{},
 		}
 	}
-	r.Initialized = true
+	r.initialized = true
 	return nil
 }
 
@@ -162,10 +162,10 @@
 			// Can't do anything with a module that's not downloaded.
 			continue
 		}
-		r.ModsByModPath = append(r.ModsByModPath, mod)
-		r.ModsByDir = append(r.ModsByDir, mod)
+		r.modsByModPath = append(r.modsByModPath, mod)
+		r.modsByDir = append(r.modsByDir, mod)
 		if mod.Main {
-			r.Main = mod
+			r.main = mod
 		}
 	}
 	return nil
@@ -198,7 +198,7 @@
 func (r *ModuleResolver) findPackage(importPath string) (*ModuleJSON, string) {
 	// This can't find packages in the stdlib, but that's harmless for all
 	// the existing code paths.
-	for _, m := range r.ModsByModPath {
+	for _, m := range r.modsByModPath {
 		if !strings.HasPrefix(importPath, m.Path) {
 			continue
 		}
@@ -292,7 +292,7 @@
 	// - in /vendor/ in -mod=vendor mode.
 	//    - nested module? Dunno.
 	// Rumor has it that replace targets cannot contain other replace targets.
-	for _, m := range r.ModsByDir {
+	for _, m := range r.modsByDir {
 		if !strings.HasPrefix(dir, m.Dir) {
 			continue
 		}
@@ -570,7 +570,7 @@
 	}
 	switch root.Type {
 	case gopathwalk.RootCurrentModule:
-		importPath = path.Join(r.Main.Path, filepath.ToSlash(subdir))
+		importPath = path.Join(r.main.Path, filepath.ToSlash(subdir))
 	case gopathwalk.RootModuleCache:
 		matches := modCacheRegexp.FindStringSubmatch(subdir)
 		if len(matches) == 0 {
diff --git a/internal/lsp/cache/view.go b/internal/lsp/cache/view.go
index 6f26106..88a6934 100644
--- a/internal/lsp/cache/view.go
+++ b/internal/lsp/cache/view.go
@@ -69,11 +69,7 @@
 	processEnv           *imports.ProcessEnv
 	cacheRefreshDuration time.Duration
 	cacheRefreshTimer    *time.Timer
-
-	// modFileVersions stores the last seen versions of the module files that are used
-	// by processEnvs resolver.
-	// TODO(suzmue): These versions may not actually be on disk.
-	modFileVersions map[string]string
+	cachedModFileVersion source.FileIdentity
 
 	// keep track of files by uri and by basename, a single file may be mapped
 	// to multiple uris, and the same basename may map to multiple files
@@ -287,9 +283,12 @@
 		}
 	}
 
-	// Before running the user provided function, clear caches in the resolver.
-	if v.modFilesChanged() {
-		v.processEnv.GetResolver().(*imports.ModuleResolver).ClearForNewMod()
+	// In module mode, check if the mod file has changed.
+	if mod, _, err := v.Snapshot().ModFiles(ctx); err == nil && mod != nil {
+		if mod.Identity() != v.cachedModFileVersion {
+			v.processEnv.GetResolver().(*imports.ModuleResolver).ClearForNewMod()
+		}
+		v.cachedModFileVersion = mod.Identity()
 	}
 
 	// Run the user function.
@@ -308,10 +307,6 @@
 		return err
 	}
 
-	// If applicable, store the file versions of the 'go.mod' files that are
-	// looked at by the resolver.
-	v.storeModFileVersions()
-
 	if v.cacheRefreshTimer == nil {
 		// Don't refresh more than twice per minute.
 		delay := 30 * time.Second
@@ -377,35 +372,6 @@
 	return env, nil
 }
 
-func (v *view) modFilesChanged() bool {
-	// Check the versions of the 'go.mod' files of the main module
-	// and modules included by a replace directive. Return true if
-	// any of these file versions do not match.
-	for filename, version := range v.modFileVersions {
-		if version != v.fileVersion(filename) {
-			return true
-		}
-	}
-	return false
-}
-
-func (v *view) storeModFileVersions() {
-	// Store the mod files versions, if we are using a ModuleResolver.
-	r, moduleMode := v.processEnv.GetResolver().(*imports.ModuleResolver)
-	if !moduleMode || !r.Initialized {
-		return
-	}
-	v.modFileVersions = make(map[string]string)
-
-	// Get the file versions of the 'go.mod' files of the main module
-	// and modules included by a replace directive in the resolver.
-	for _, mod := range r.ModsByModPath {
-		if (mod.Main || mod.Replace != nil) && mod.GoMod != "" {
-			v.modFileVersions[mod.GoMod] = v.fileVersion(mod.GoMod)
-		}
-	}
-}
-
 func (v *view) fileVersion(filename string) string {
 	uri := span.FileURI(filename)
 	fh := v.session.GetFile(uri)