gopls/internal/server: set -mod=readonly when checking for upgrades

When a vendor directory is present, we must explicitly use -mod=readonly
to query upgrades with `go list`. This was broken by the fixes for
workspace vendoring, which removed the `-mod` flag in most usage of the
gocommand package.

Fixes golang/go#66055

Change-Id: I29efb617a8fe56e9752dc088dc5ea884f1cefb86
Reviewed-on: https://go-review.googlesource.com/c/tools/+/569877
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Alan Donovan <adonovan@google.com>
diff --git a/gopls/internal/server/command.go b/gopls/internal/server/command.go
index 3f5d53c..878d923 100644
--- a/gopls/internal/server/command.go
+++ b/gopls/internal/server/command.go
@@ -751,6 +751,7 @@
 	stdout, err := snapshot.RunGoCommandDirect(ctx, cache.Normal|cache.AllowNetwork, &gocommand.Invocation{
 		Verb:       "list",
 		Args:       append([]string{"-m", "-u", "-json"}, modules...),
+		ModFlag:    "readonly", // necessary when vendor is present (golang/go#66055)
 		WorkingDir: filepath.Dir(uri.Path()),
 	})
 	if err != nil {
diff --git a/gopls/internal/test/integration/codelens/codelens_test.go b/gopls/internal/test/integration/codelens/codelens_test.go
index 07ad3b9..c1c28da 100644
--- a/gopls/internal/test/integration/codelens/codelens_test.go
+++ b/gopls/internal/test/integration/codelens/codelens_test.go
@@ -73,13 +73,7 @@
 	}
 }
 
-// This test confirms the full functionality of the code lenses for updating
-// dependencies in a go.mod file. It checks for the code lens that suggests
-// an update and then executes the command associated with that code lens. A
-// regression test for golang/go#39446. It also checks that these code lenses
-// only affect the diagnostics and contents of the containing go.mod file.
-func TestUpgradeCodelens(t *testing.T) {
-	const proxyWithLatest = `
+const proxyWithLatest = `
 -- golang.org/x/hello@v1.3.3/go.mod --
 module golang.org/x/hello
 
@@ -98,6 +92,13 @@
 var Goodbye error
 `
 
+// This test confirms the full functionality of the code lenses for updating
+// dependencies in a go.mod file, when using a go.work file. It checks for the
+// code lens that suggests an update and then executes the command associated
+// with that code lens. A regression test for golang/go#39446. It also checks
+// that these code lenses only affect the diagnostics and contents of the
+// containing go.mod file.
+func TestUpgradeCodelens_Workspace(t *testing.T) {
 	const shouldUpdateDep = `
 -- go.work --
 go 1.18
@@ -246,6 +247,62 @@
 	}
 }
 
+func TestUpgradeCodelens_ModVendor(t *testing.T) {
+	// This test checks the regression of golang/go#66055. The upgrade codelens
+	// should work in a mod vendor context (the test above using a go.work file
+	// was not broken).
+	testenv.NeedsGo1Point(t, 22)
+	const shouldUpdateDep = `
+-- go.mod --
+module mod.com/a
+
+go 1.22
+
+require golang.org/x/hello v1.2.3
+-- go.sum --
+golang.org/x/hello v1.2.3 h1:7Wesfkx/uBd+eFgPrq0irYj/1XfmbvLV8jZ/W7C2Dwg=
+golang.org/x/hello v1.2.3/go.mod h1:OgtlzsxVMUUdsdQCIDYgaauCTH47B8T8vofouNJfzgY=
+-- main.go --
+package main
+
+import "golang.org/x/hello/hi"
+
+func main() {
+	_ = hi.Goodbye
+}
+`
+
+	const wantGoModA = `module mod.com/a
+
+go 1.22
+
+require golang.org/x/hello v1.3.3
+`
+
+	WithOptions(
+		ProxyFiles(proxyWithLatest),
+	).Run(t, shouldUpdateDep, func(t *testing.T, env *Env) {
+		env.RunGoCommand("mod", "vendor")
+		env.AfterChange()
+		env.OpenFile("go.mod")
+
+		env.ExecuteCodeLensCommand("go.mod", command.CheckUpgrades, nil)
+		d := &protocol.PublishDiagnosticsParams{}
+		env.OnceMet(
+			CompletedWork(server.DiagnosticWorkTitle(server.FromCheckUpgrades), 1, true),
+			Diagnostics(env.AtRegexp("go.mod", `require`), WithMessage("can be upgraded")),
+			ReadDiagnostics("go.mod", d),
+		)
+
+		// Apply the diagnostics to a/go.mod.
+		env.ApplyQuickFixes("go.mod", d.Diagnostics)
+		env.AfterChange()
+		if got := env.BufferText("go.mod"); got != wantGoModA {
+			t.Fatalf("go.mod upgrade failed:\n%s", compare.Text(wantGoModA, got))
+		}
+	})
+}
+
 func TestUnusedDependenciesCodelens(t *testing.T) {
 	const proxy = `
 -- golang.org/x/hello@v1.0.0/go.mod --
diff --git a/internal/gocommand/invoke.go b/internal/gocommand/invoke.go
index 5531252..f7de3c8 100644
--- a/internal/gocommand/invoke.go
+++ b/internal/gocommand/invoke.go
@@ -158,12 +158,15 @@
 	BuildFlags []string
 
 	// If ModFlag is set, the go command is invoked with -mod=ModFlag.
+	// TODO(rfindley): remove, in favor of Args.
 	ModFlag string
 
 	// If ModFile is set, the go command is invoked with -modfile=ModFile.
+	// TODO(rfindley): remove, in favor of Args.
 	ModFile string
 
 	// If Overlay is set, the go command is invoked with -overlay=Overlay.
+	// TODO(rfindley): remove, in favor of Args.
 	Overlay string
 
 	// If CleanEnv is set, the invocation will run only with the environment