x/playground: remove -mod=mod and execute go mod tidy before command

Previously, `-mod=mod` was included to pull down dependencies.
This change executes go mod tidy before building packages, that will
allow for go work usage, and address golang/go#40728

Fixes golang/go#54741

Change-Id: I66affc8d2a2e72d958415ea1c052dd3dcf38841e
Reviewed-on: https://go-review.googlesource.com/c/playground/+/458895
Reviewed-by: Robert Findley <rfindley@google.com>
Auto-Submit: Bryan Mills <bcmills@google.com>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
Commit-Queue: Bryan Mills <bcmills@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Run-TryBot: Bryan Mills <bcmills@google.com>
Reviewed-by: Bryan Mills <bcmills@google.com>
diff --git a/mod.go b/mod.go
new file mode 100644
index 0000000..6f523a2
--- /dev/null
+++ b/mod.go
@@ -0,0 +1,36 @@
+// Copyright 2024 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+	"context"
+	"fmt"
+	"os"
+	"os/exec"
+	"strings"
+)
+
+func modTidy(ctx context.Context, dir, goPath string) (output string, execErr error) {
+	cmd := exec.Command("go", "mod", "tidy")
+	cmd.Dir = dir
+	cmd.Env = append(os.Environ(), "CGO_ENABLED=0", "GOPATH="+goPath)
+	cmd.Env = append(cmd.Env,
+		"GO111MODULE=on",
+		"GOPROXY="+playgroundGoproxy(),
+	)
+	out, err := cmd.CombinedOutput()
+	if err == nil {
+		return "", nil
+	}
+	if _, ok := err.(*exec.ExitError); !ok {
+		return "", fmt.Errorf("error vetting go source: %v", err)
+	}
+
+	// Rewrite compiler errors to refer to progName
+	// instead of '/tmp/sandbox1234/main.go'.
+	errs := strings.Replace(string(out), dir, "", -1)
+
+	return errs, nil
+}
diff --git a/sandbox.go b/sandbox.go
index c081e06..b009c74 100644
--- a/sandbox.go
+++ b/sandbox.go
@@ -460,7 +460,6 @@
 	// Create a GOPATH just for modules to be downloaded
 	// into GOPATH/pkg/mod.
 	cmd.Args = append(cmd.Args, "-modcacherw")
-	cmd.Args = append(cmd.Args, "-mod=mod")
 	br.goPath, err = os.MkdirTemp("", "gopath")
 	if err != nil {
 		log.Printf("error creating temp directory: %v", err)
@@ -472,6 +471,12 @@
 	out := &bytes.Buffer{}
 	cmd.Stderr, cmd.Stdout = out, out
 
+	// Run "go mod tidy" before executing a command.
+	_, err = modTidy(ctx, tmpDir, br.goPath)
+	if err != nil {
+		return nil, fmt.Errorf("error running go mod tidy: %v", err)
+	}
+
 	if err := cmd.Start(); err != nil {
 		return nil, fmt.Errorf("error starting go build: %v", err)
 	}
diff --git a/tests.go b/tests.go
index 46731a6..aa59aa0 100644
--- a/tests.go
+++ b/tests.go
@@ -633,4 +633,40 @@
 `, errors: `./foo.go:6:2: syntax error: unexpected =, expecting }
 `,
 	},
+	{
+		name: "workspace",
+		prog: `
+package main
+
+import "internal/bar"
+
+func main() {
+	bar.Print()
+}
+-- go.work --
+go 1.18
+
+use (
+	.
+	./projects/bar
+)
+-- go.mod --
+module internal/foo
+
+go 1.18
+
+require internal/bar v0.0.0
+-- projects/bar/go.mod --
+module internal/bar
+
+go 1.18
+-- projects/bar/upper.go --
+package bar
+
+import "fmt"
+
+func Print() {
+	fmt.Println("bar")
+}
+`, want: "bar\n"},
 }
diff --git a/vet.go b/vet.go
index d58c0f7..ae9043a 100644
--- a/vet.go
+++ b/vet.go
@@ -62,7 +62,7 @@
 			mGoVetLatency.M(float64(time.Since(start))/float64(time.Millisecond)))
 	}()
 
-	cmd := exec.Command("go", "vet", "--tags=faketime", "--mod=mod")
+	cmd := exec.Command("go", "vet", "--tags=faketime")
 	cmd.Dir = dir
 	// Linux go binary is not built with CGO_ENABLED=0.
 	// Prevent vet to compile packages in cgo mode.