internal/lsp: don't use -modfile for `go mod` commands

These are commands whose changes should be reflected in the existing
go.mod file, as they do not provide edits. Add a third way of running
the go command, explicitly without -modfile. Update the regression test
accordingly.

Change-Id: I866b5db83b504fae190e58c306c01a7a4296672d
Reviewed-on: https://go-review.googlesource.com/c/tools/+/239200
Run-TryBot: Rebecca Stambler <rstambler@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Heschi Kreinick <heschi@google.com>
diff --git a/internal/lsp/cache/snapshot.go b/internal/lsp/cache/snapshot.go
index 32f0358..27d9612 100644
--- a/internal/lsp/cache/snapshot.go
+++ b/internal/lsp/cache/snapshot.go
@@ -133,6 +133,12 @@
 	return cfg
 }
 
+func (s *snapshot) RunGoCommandDirect(ctx context.Context, verb string, args []string) error {
+	cfg := s.config(ctx)
+	_, _, err := runGoCommand(ctx, cfg, nil, nil, verb, args)
+	return err
+}
+
 func (s *snapshot) RunGoCommand(ctx context.Context, verb string, args []string) (*bytes.Buffer, error) {
 	cfg := s.config(ctx)
 	var modFH, sumFH source.FileHandle
diff --git a/internal/lsp/command.go b/internal/lsp/command.go
index 6693e30..9826258 100644
--- a/internal/lsp/command.go
+++ b/internal/lsp/command.go
@@ -66,7 +66,7 @@
 		if params.Command == source.CommandVendor {
 			arg = "vendor"
 		}
-		err := s.goModCommand(ctx, uri, "mod", []string{arg}...)
+		err := s.directGoModCommand(ctx, uri, "mod", []string{arg}...)
 		return nil, err
 	case source.CommandUpgradeDependency:
 		if len(params.Arguments) < 2 {
@@ -74,19 +74,18 @@
 		}
 		uri := protocol.DocumentURI(params.Arguments[0].(string))
 		deps := params.Arguments[1].(string)
-		err := s.goModCommand(ctx, uri, "get", strings.Split(deps, " ")...)
+		err := s.directGoModCommand(ctx, uri, "get", strings.Split(deps, " ")...)
 		return nil, err
 	}
 	return nil, nil
 }
 
-func (s *Server) goModCommand(ctx context.Context, uri protocol.DocumentURI, verb string, args ...string) error {
+func (s *Server) directGoModCommand(ctx context.Context, uri protocol.DocumentURI, verb string, args ...string) error {
 	view, err := s.session.ViewOf(uri.SpanURI())
 	if err != nil {
 		return err
 	}
-	_, err = view.Snapshot().RunGoCommand(ctx, verb, args)
-	return err
+	return view.Snapshot().RunGoCommandDirect(ctx, verb, args)
 }
 
 func (s *Server) runTest(ctx context.Context, snapshot source.Snapshot, funcName string) error {
diff --git a/internal/lsp/diagnostics.go b/internal/lsp/diagnostics.go
index 4c9fe25..a2e653f 100644
--- a/internal/lsp/diagnostics.go
+++ b/internal/lsp/diagnostics.go
@@ -101,7 +101,7 @@
 		if item == nil || err != nil {
 			return nil, nil
 		}
-		if err := s.goModCommand(ctx, protocol.URIFromSpanURI(modURI), "mod", []string{"vendor"}...); err != nil {
+		if err := s.directGoModCommand(ctx, protocol.URIFromSpanURI(modURI), "mod", []string{"vendor"}...); err != nil {
 			return nil, &protocol.ShowMessageParams{
 				Type:    protocol.Error,
 				Message: fmt.Sprintf(`"go mod vendor" failed with %v`, err),
diff --git a/internal/lsp/regtest/codelens_test.go b/internal/lsp/regtest/codelens_test.go
index 42c5f74..14f5bef 100644
--- a/internal/lsp/regtest/codelens_test.go
+++ b/internal/lsp/regtest/codelens_test.go
@@ -100,6 +100,7 @@
 `
 	runner.Run(t, shouldUpdateDep, func(t *testing.T, env *Env) {
 		env.OpenFile("go.mod")
+		before := env.ReadWorkspaceFile("go.mod")
 		lenses := env.CodeLens("go.mod")
 		want := "Upgrade dependency to v1.3.3"
 		var found *protocol.CodeLens
@@ -117,6 +118,10 @@
 		}); err != nil {
 			t.Fatal(err)
 		}
+		after := env.ReadWorkspaceFile("go.mod")
+		if before == after {
+			t.Fatalf("go.mod file was unchanged by upgrade command")
+		}
 	}, WithProxy(proxyWithLatest))
 }
 
diff --git a/internal/lsp/source/view.go b/internal/lsp/source/view.go
index f66f82d..64d64a2 100644
--- a/internal/lsp/source/view.go
+++ b/internal/lsp/source/view.go
@@ -47,12 +47,17 @@
 	Analyze(ctx context.Context, pkgID string, analyzers ...*analysis.Analyzer) ([]*Error, error)
 
 	// RunGoCommandPiped runs the given `go` command in the view, using the
-	// provided stdout and stderr.
+	// provided stdout and stderr. It will use the -modfile flag, if possible.
 	RunGoCommandPiped(ctx context.Context, verb string, args []string, stdout, stderr io.Writer) error
 
-	// RunGoCommand runs the given `go` command in the view.
+	// RunGoCommand runs the given `go` command in the view. It will use the
+	// -modfile flag, if possible.
 	RunGoCommand(ctx context.Context, verb string, args []string) (*bytes.Buffer, error)
 
+	// RunGoCommandDirect runs the given `go` command, never using the
+	// -modfile flag.
+	RunGoCommandDirect(ctx context.Context, verb string, args []string) error
+
 	// ModTidyHandle returns a ModTidyHandle for the given go.mod file handle.
 	// This function can have no data or error if there is no modfile detected.
 	ModTidyHandle(ctx context.Context, fh FileHandle) (ModTidyHandle, error)