gopls: fix a couple places where temporary files are not removed

Fix two places where gopls fails to clean up temporary files:
- In telemetry tests, the deferred cleanup was not run after os.Exit.
- In the GC details codelens, a persistent GC details directory is
  assumed; just use a temp directory instead.

Change-Id: Icef5a4b612ac1727fee7d2c65e99a90f73123081
Reviewed-on: https://go-review.googlesource.com/c/tools/+/579755
Reviewed-by: Alan Donovan <adonovan@google.com>
Auto-Submit: Robert Findley <rfindley@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
diff --git a/gopls/internal/golang/gc_annotations.go b/gopls/internal/golang/gc_annotations.go
index 1ff8661..6a4648f 100644
--- a/gopls/internal/golang/gc_annotations.go
+++ b/gopls/internal/golang/gc_annotations.go
@@ -17,6 +17,7 @@
 	"golang.org/x/tools/gopls/internal/cache/metadata"
 	"golang.org/x/tools/gopls/internal/protocol"
 	"golang.org/x/tools/gopls/internal/settings"
+	"golang.org/x/tools/internal/event"
 	"golang.org/x/tools/internal/gocommand"
 )
 
@@ -25,11 +26,16 @@
 		return nil, nil
 	}
 	pkgDir := filepath.Dir(mp.CompiledGoFiles[0].Path())
-	outDir := filepath.Join(os.TempDir(), fmt.Sprintf("gopls-%d.details", os.Getpid()))
-
-	if err := os.MkdirAll(outDir, 0700); err != nil {
+	outDir, err := os.MkdirTemp("", fmt.Sprintf("gopls-%d.details", os.Getpid()))
+	if err != nil {
 		return nil, err
 	}
+	defer func() {
+		if err := os.RemoveAll(outDir); err != nil {
+			event.Error(ctx, "cleaning gcdetails dir", err)
+		}
+	}()
+
 	tmpFile, err := os.CreateTemp(os.TempDir(), "gopls-x")
 	if err != nil {
 		return nil, err
diff --git a/gopls/internal/telemetry/telemetry_test.go b/gopls/internal/telemetry/telemetry_test.go
index cada51c..d2eecdf 100644
--- a/gopls/internal/telemetry/telemetry_test.go
+++ b/gopls/internal/telemetry/telemetry_test.go
@@ -31,8 +31,9 @@
 		panic(err)
 	}
 	countertest.Open(tmp)
-	defer os.RemoveAll(tmp)
-	Main(m)
+	code := Main(m)
+	os.RemoveAll(tmp)
+	os.Exit(code)
 }
 
 func TestTelemetry(t *testing.T) {
diff --git a/gopls/internal/test/integration/codelens/codelens_test.go b/gopls/internal/test/integration/codelens/codelens_test.go
index 971154c..95b2a6e 100644
--- a/gopls/internal/test/integration/codelens/codelens_test.go
+++ b/gopls/internal/test/integration/codelens/codelens_test.go
@@ -6,6 +6,7 @@
 
 import (
 	"fmt"
+	"os"
 	"testing"
 
 	"golang.org/x/tools/gopls/internal/server"
@@ -20,7 +21,7 @@
 
 func TestMain(m *testing.M) {
 	bug.PanicOnBugs = true
-	Main(m)
+	os.Exit(Main(m))
 }
 
 func TestDisablingCodeLens(t *testing.T) {
diff --git a/gopls/internal/test/integration/completion/completion_test.go b/gopls/internal/test/integration/completion/completion_test.go
index 78de61a..d58024e 100644
--- a/gopls/internal/test/integration/completion/completion_test.go
+++ b/gopls/internal/test/integration/completion/completion_test.go
@@ -6,6 +6,7 @@
 
 import (
 	"fmt"
+	"os"
 	"sort"
 	"strings"
 	"testing"
@@ -24,7 +25,7 @@
 
 func TestMain(m *testing.M) {
 	bug.PanicOnBugs = true
-	Main(m)
+	os.Exit(Main(m))
 }
 
 const proxy = `
diff --git a/gopls/internal/test/integration/debug/debug_test.go b/gopls/internal/test/integration/debug/debug_test.go
index 4e4428b..3b43de9 100644
--- a/gopls/internal/test/integration/debug/debug_test.go
+++ b/gopls/internal/test/integration/debug/debug_test.go
@@ -9,6 +9,7 @@
 	"encoding/json"
 	"io"
 	"net/http"
+	"os"
 	"strings"
 	"testing"
 
@@ -19,7 +20,7 @@
 )
 
 func TestMain(m *testing.M) {
-	Main(m)
+	os.Exit(Main(m))
 }
 
 func TestBugNotification(t *testing.T) {
diff --git a/gopls/internal/test/integration/diagnostics/diagnostics_test.go b/gopls/internal/test/integration/diagnostics/diagnostics_test.go
index 269734c..2862a86 100644
--- a/gopls/internal/test/integration/diagnostics/diagnostics_test.go
+++ b/gopls/internal/test/integration/diagnostics/diagnostics_test.go
@@ -7,6 +7,7 @@
 import (
 	"context"
 	"fmt"
+	"os"
 	"os/exec"
 	"testing"
 
@@ -20,7 +21,7 @@
 
 func TestMain(m *testing.M) {
 	bug.PanicOnBugs = true
-	Main(m)
+	os.Exit(Main(m))
 }
 
 // Use mod.com for all go.mod files due to golang/go#35230.
diff --git a/gopls/internal/test/integration/doc.go b/gopls/internal/test/integration/doc.go
index 5599564..a1c5856 100644
--- a/gopls/internal/test/integration/doc.go
+++ b/gopls/internal/test/integration/doc.go
@@ -33,7 +33,7 @@
 //	)
 //
 //	func TestMain(m *testing.M) {
-//		Main(m, hooks.Options)
+//		os.Exit(Main(m, hooks.Options))
 //	}
 //
 // # Writing a simple integration test
diff --git a/gopls/internal/test/integration/inlayhints/inlayhints_test.go b/gopls/internal/test/integration/inlayhints/inlayhints_test.go
index feb837b..bb6d68e 100644
--- a/gopls/internal/test/integration/inlayhints/inlayhints_test.go
+++ b/gopls/internal/test/integration/inlayhints/inlayhints_test.go
@@ -4,6 +4,7 @@
 package inlayhint
 
 import (
+	"os"
 	"testing"
 
 	"golang.org/x/tools/gopls/internal/golang"
@@ -13,7 +14,7 @@
 
 func TestMain(m *testing.M) {
 	bug.PanicOnBugs = true
-	Main(m)
+	os.Exit(Main(m))
 }
 
 func TestEnablingInlayHints(t *testing.T) {
diff --git a/gopls/internal/test/integration/misc/misc_test.go b/gopls/internal/test/integration/misc/misc_test.go
index a2c0292..666887f 100644
--- a/gopls/internal/test/integration/misc/misc_test.go
+++ b/gopls/internal/test/integration/misc/misc_test.go
@@ -5,6 +5,7 @@
 package misc
 
 import (
+	"os"
 	"strings"
 	"testing"
 
@@ -16,7 +17,7 @@
 
 func TestMain(m *testing.M) {
 	bug.PanicOnBugs = true
-	integration.Main(m)
+	os.Exit(integration.Main(m))
 }
 
 // TestDocumentURIFix ensures that a DocumentURI supplied by the
diff --git a/gopls/internal/test/integration/modfile/modfile_test.go b/gopls/internal/test/integration/modfile/modfile_test.go
index 8c41c44..243bb04 100644
--- a/gopls/internal/test/integration/modfile/modfile_test.go
+++ b/gopls/internal/test/integration/modfile/modfile_test.go
@@ -5,6 +5,7 @@
 package modfile
 
 import (
+	"os"
 	"path/filepath"
 	"runtime"
 	"strings"
@@ -19,7 +20,7 @@
 
 func TestMain(m *testing.M) {
 	bug.PanicOnBugs = true
-	Main(m)
+	os.Exit(Main(m))
 }
 
 const workspaceProxy = `
diff --git a/gopls/internal/test/integration/regtest.go b/gopls/internal/test/integration/regtest.go
index dffc432..96c1044 100644
--- a/gopls/internal/test/integration/regtest.go
+++ b/gopls/internal/test/integration/regtest.go
@@ -111,7 +111,24 @@
 var runFromMain = false // true if Main has been called
 
 // Main sets up and tears down the shared integration test state.
-func Main(m *testing.M) {
+func Main(m *testing.M) (code int) {
+	defer func() {
+		if runner != nil {
+			if err := runner.Close(); err != nil {
+				fmt.Fprintf(os.Stderr, "closing test runner: %v\n", err)
+				// Cleanup is broken in go1.12 and earlier, and sometimes flakes on
+				// Windows due to file locking, but this is OK for our CI.
+				//
+				// Fail on go1.13+, except for windows and android which have shutdown problems.
+				if testenv.Go1Point() >= 13 && runtime.GOOS != "windows" && runtime.GOOS != "android" {
+					if code == 0 {
+						code = 1
+					}
+				}
+			}
+		}
+	}()
+
 	runFromMain = true
 
 	// golang/go#54461: enable additional debugging around hanging Go commands.
@@ -121,12 +138,12 @@
 	// suite. See the documentation for runTestAsGoplsEnvvar for more details.
 	if os.Getenv(runTestAsGoplsEnvvar) == "true" {
 		tool.Main(context.Background(), cmd.New(), os.Args[1:])
-		os.Exit(0)
+		return 0
 	}
 
 	if !testenv.HasExec() {
 		fmt.Printf("skipping all tests: exec not supported on %s/%s\n", runtime.GOOS, runtime.GOARCH)
-		os.Exit(0)
+		return 0
 	}
 	testenv.ExitIfSmallMachine()
 
@@ -137,12 +154,12 @@
 
 	if skipReason := checkBuilder(); skipReason != "" {
 		fmt.Printf("Skipping all tests: %s\n", skipReason)
-		os.Exit(0)
+		return 0
 	}
 
 	if err := testenv.HasTool("go"); err != nil {
 		fmt.Println("Missing go command")
-		os.Exit(1)
+		return 1
 	}
 
 	runner = &Runner{
@@ -168,19 +185,5 @@
 	}
 	runner.tempDir = dir
 
-	var code int
-	defer func() {
-		if err := runner.Close(); err != nil {
-			fmt.Fprintf(os.Stderr, "closing test runner: %v\n", err)
-			// Cleanup is broken in go1.12 and earlier, and sometimes flakes on
-			// Windows due to file locking, but this is OK for our CI.
-			//
-			// Fail on go1.13+, except for windows and android which have shutdown problems.
-			if testenv.Go1Point() >= 13 && runtime.GOOS != "windows" && runtime.GOOS != "android" {
-				os.Exit(1)
-			}
-		}
-		os.Exit(code)
-	}()
-	code = m.Run()
+	return m.Run()
 }
diff --git a/gopls/internal/test/integration/template/template_test.go b/gopls/internal/test/integration/template/template_test.go
index c1c232b..ef8d099 100644
--- a/gopls/internal/test/integration/template/template_test.go
+++ b/gopls/internal/test/integration/template/template_test.go
@@ -5,6 +5,7 @@
 package template
 
 import (
+	"os"
 	"strings"
 	"testing"
 
@@ -15,7 +16,7 @@
 
 func TestMain(m *testing.M) {
 	bug.PanicOnBugs = true
-	Main(m)
+	os.Exit(Main(m))
 }
 
 func TestMultilineTokens(t *testing.T) {
diff --git a/gopls/internal/test/integration/watch/watch_test.go b/gopls/internal/test/integration/watch/watch_test.go
index 91beb3a..7f41511 100644
--- a/gopls/internal/test/integration/watch/watch_test.go
+++ b/gopls/internal/test/integration/watch/watch_test.go
@@ -5,6 +5,7 @@
 package watch
 
 import (
+	"os"
 	"testing"
 
 	. "golang.org/x/tools/gopls/internal/test/integration"
@@ -16,7 +17,7 @@
 
 func TestMain(m *testing.M) {
 	bug.PanicOnBugs = true
-	Main(m)
+	os.Exit(Main(m))
 }
 
 func TestEditFile(t *testing.T) {
diff --git a/gopls/internal/test/integration/workspace/workspace_test.go b/gopls/internal/test/integration/workspace/workspace_test.go
index e9b709f..929f332 100644
--- a/gopls/internal/test/integration/workspace/workspace_test.go
+++ b/gopls/internal/test/integration/workspace/workspace_test.go
@@ -7,6 +7,7 @@
 import (
 	"context"
 	"fmt"
+	"os"
 	"sort"
 	"strings"
 	"testing"
@@ -24,7 +25,7 @@
 
 func TestMain(m *testing.M) {
 	bug.PanicOnBugs = true
-	Main(m)
+	os.Exit(Main(m))
 }
 
 const workspaceProxy = `