internal/lsp: remove logic for re-creating a view when a go.mod changes

This is no longer necessary now that we modify the modules field based
on newly created/deleted modules.

There was a race condition setting the metadata--we were reusing old
metadata that may have already been cached in some instances. Now, we
always override metadata.

Also, disabled the TestUseGoplsMod test -- there seems to be an issue
with it. Will discuss this offline in the AM.

Change-Id: Ie8c97557d4f0a319051256e5f130b9cdae479928
Reviewed-on: https://go-review.googlesource.com/c/tools/+/258121
Trust: Rebecca Stambler <rstambler@golang.org>
Run-TryBot: Rebecca Stambler <rstambler@golang.org>
gopls-CI: kokoro <noreply+kokoro@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Heschi Kreinick <heschi@google.com>
diff --git a/gopls/internal/regtest/workspace_test.go b/gopls/internal/regtest/workspace_test.go
index 704f598..77d55be 100644
--- a/gopls/internal/regtest/workspace_test.go
+++ b/gopls/internal/regtest/workspace_test.go
@@ -286,6 +286,7 @@
 		if want := "b.com@v1.2.3/b/b.go"; !strings.HasSuffix(original, want) {
 			t.Errorf("expected %s, got %v", want, original)
 		}
+		env.CloseBuffer(original)
 		env.WriteWorkspaceFiles(map[string]string{
 			"modb/go.mod": "module b.com",
 			"modb/b/b.go": `package b
diff --git a/internal/lsp/cache/session.go b/internal/lsp/cache/session.go
index 61595a2..4986c00 100644
--- a/internal/lsp/cache/session.go
+++ b/internal/lsp/cache/session.go
@@ -483,13 +483,6 @@
 					deletions[c.URI] = struct{}{}
 				}
 			}
-			// If the file change is to a go.mod file, and initialization for
-			// the view has previously failed, we should attempt to retry.
-			// TODO(rstambler): We can use unsaved contents with -modfile, so
-			// maybe we should do that and retry on any change?
-			if fh.Kind() == source.Mod && (c.OnDisk || c.Action == source.Save) {
-				view.maybeReinitialize()
-			}
 		}
 	}
 	var snapshots []source.Snapshot
diff --git a/internal/lsp/cache/snapshot.go b/internal/lsp/cache/snapshot.go
index 759cb33..d08bc78 100644
--- a/internal/lsp/cache/snapshot.go
+++ b/internal/lsp/cache/snapshot.go
@@ -126,10 +126,11 @@
 }
 
 func (s *snapshot) ModFiles() []span.URI {
-	if s.view.modURI == "" {
-		return nil
+	var uris []span.URI
+	for _, m := range s.modules {
+		uris = append(uris, m.modURI)
 	}
-	return []span.URI{s.view.modURI}
+	return uris
 }
 
 func (s *snapshot) ValidBuildConfiguration() bool {
@@ -928,7 +929,7 @@
 	return fmt.Sprintf("v%v/%v", v.id, snapshotID)
 }
 
-func (s *snapshot) clone(ctx context.Context, withoutURIs map[span.URI]source.VersionedFileHandle, forceReloadMetadata bool) *snapshot {
+func (s *snapshot) clone(ctx context.Context, withoutURIs map[span.URI]source.VersionedFileHandle, forceReloadMetadata bool) (*snapshot, reinitializeView) {
 	s.mu.Lock()
 	defer s.mu.Unlock()
 
@@ -1062,8 +1063,8 @@
 			// If the view's go.mod file's contents have changed, invalidate
 			// the metadata for every known package in the snapshot.
 			if invalidateMetadata {
-				for k := range s.packages {
-					directIDs[k.id] = struct{}{}
+				for k := range s.metadata {
+					directIDs[k] = struct{}{}
 				}
 				// If a go.mod file in the workspace has changed, we need to
 				// rebuild the workspace module.
@@ -1159,7 +1160,6 @@
 	// When modules change, we need to recompute their workspace directories,
 	// as replace directives may have changed.
 	if modulesChanged {
-		// Use the new snapshot, as it can be locked without causing issues.
 		result.workspaceDirectories = result.findWorkspaceDirectories(ctx)
 	}
 
@@ -1200,7 +1200,11 @@
 	}
 	// Copy the set of initally loaded packages.
 	for id, pkgPath := range s.workspacePackages {
-		if id == "command-line-arguments" {
+		// Packages with the id "command-line-arguments" or with a package path
+		// prefixed with "_/" are generated by the go command when the user is
+		// outside of GOPATH and outside of a module. Do not cache them as
+		// workspace packages for longer than necessary.
+		if id == "command-line-arguments" || strings.HasPrefix(string(pkgPath), "_/") {
 			if invalidateMetadata, ok := transitiveIDs[id]; invalidateMetadata && ok {
 				continue
 			}
@@ -1224,10 +1228,6 @@
 		result.workspacePackages[id] = pkgPath
 	}
 
-	if shouldReinitializeView {
-		s.view.definitelyReinitialize()
-	}
-
 	// Inherit all of the go.mod-related handles.
 	for _, v := range result.modTidyHandles {
 		newGen.Inherit(v.handle)
@@ -1244,12 +1244,27 @@
 	if result.workspaceModuleHandle != nil {
 		newGen.Inherit(result.workspaceModuleHandle.handle)
 	}
-
 	// Don't bother copying the importedBy graph,
 	// as it changes each time we update metadata.
-	return result
+
+	var reinitialize reinitializeView
+	if modulesChanged {
+		reinitialize = maybeReinit
+	}
+	if shouldReinitializeView {
+		reinitialize = definitelyReinit
+	}
+	return result, reinitialize
 }
 
+type reinitializeView int
+
+const (
+	doNotReinit = reinitializeView(iota)
+	maybeReinit
+	definitelyReinit
+)
+
 // fileWasSaved reports whether the FileHandle passed in has been saved. It
 // accomplishes this by checking to see if the original and current FileHandles
 // are both overlays, and if the current FileHandle is saved while the original
@@ -1270,7 +1285,7 @@
 // determine if the file requires a metadata reload.
 func (s *snapshot) shouldInvalidateMetadata(ctx context.Context, newSnapshot *snapshot, originalFH, currentFH source.FileHandle) bool {
 	if originalFH == nil {
-		return currentFH.Kind() == source.Go
+		return true
 	}
 	// If the file hasn't changed, there's no need to reload.
 	if originalFH.FileIdentity() == currentFH.FileIdentity() {
diff --git a/internal/lsp/cache/view.go b/internal/lsp/cache/view.go
index fca69e8..01b9526 100644
--- a/internal/lsp/cache/view.go
+++ b/internal/lsp/cache/view.go
@@ -801,9 +801,14 @@
 	defer v.snapshotMu.Unlock()
 
 	oldSnapshot := v.snapshot
-	v.snapshot = oldSnapshot.clone(ctx, uris, forceReloadMetadata)
+	var reinitialize reinitializeView
+	v.snapshot, reinitialize = oldSnapshot.clone(ctx, uris, forceReloadMetadata)
 	go oldSnapshot.generation.Destroy()
 
+	if reinitialize == maybeReinit || reinitialize == definitelyReinit {
+		v.reinitialize(reinitialize == definitelyReinit)
+	}
+
 	return v.snapshot, v.snapshot.generation.Acquire(ctx)
 }
 
@@ -818,14 +823,6 @@
 	v.backgroundCtx, v.cancel = context.WithCancel(v.baseCtx)
 }
 
-func (v *View) maybeReinitialize() {
-	v.reinitialize(false)
-}
-
-func (v *View) definitelyReinitialize() {
-	v.reinitialize(true)
-}
-
 func (v *View) reinitialize(force bool) {
 	v.initializationSema <- struct{}{}
 	defer func() {
diff --git a/internal/lsp/text_synchronization.go b/internal/lsp/text_synchronization.go
index 89aea44..e1a80eb 100644
--- a/internal/lsp/text_synchronization.go
+++ b/internal/lsp/text_synchronization.go
@@ -251,31 +251,6 @@
 	}
 
 	for snapshot, uris := range snapshotSet {
-		// If a modification comes in for the view's go.mod file and the view
-		// was never properly initialized, or the view does not have
-		// a go.mod file, try to recreate the associated view.
-		if len(snapshot.ModFiles()) == 0 {
-			for _, uri := range uris {
-				// Don't rebuild the view until the go.mod is on disk.
-				if !snapshot.IsSaved(uri) {
-					continue
-				}
-				fh, err := snapshot.GetFile(ctx, uri)
-				if err != nil {
-					return err
-				}
-				switch fh.Kind() {
-				case source.Mod:
-					newSnapshot, release, err := snapshot.View().Rebuild(ctx)
-					releases = append(releases, release)
-					if err != nil {
-						return err
-					}
-					// Update the snapshot to the rebuilt one.
-					snapshot = newSnapshot
-				}
-			}
-		}
 		diagnosticWG.Add(1)
 		go func(snapshot source.Snapshot, uris []span.URI) {
 			defer diagnosticWG.Done()