internal/lsp: key GC details off package ID

Rather than using the directory of the package, store the package ID and
calculate the directory in GCOptimizationDetails. I think this is
slightly more readable/cleaner.

Change-Id: I13cac8a7552b73b2bd5d25ff582b5d4936a74827
Reviewed-on: https://go-review.googlesource.com/c/tools/+/297877
Trust: Heschi Kreinick <heschi@google.com>
Run-TryBot: Heschi Kreinick <heschi@google.com>
gopls-CI: kokoro <noreply+kokoro@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Robert Findley <rfindley@google.com>
diff --git a/internal/lsp/command.go b/internal/lsp/command.go
index edcff12..1960270 100644
--- a/internal/lsp/command.go
+++ b/internal/lsp/command.go
@@ -613,13 +613,16 @@
 		progress:    "Toggling GC Details",
 		forURI:      args.URI,
 	}, func(ctx context.Context, deps commandDeps) error {
-		pkgDir := span.URIFromPath(filepath.Dir(args.URI.SpanURI().Filename()))
+		pkg, err := deps.snapshot.PackageForFile(ctx, deps.fh.URI(), source.TypecheckWorkspace, source.NarrowestPackage)
+		if err != nil {
+			return err
+		}
 		c.s.gcOptimizationDetailsMu.Lock()
-		if _, ok := c.s.gcOptimizationDetails[pkgDir]; ok {
-			delete(c.s.gcOptimizationDetails, pkgDir)
+		if _, ok := c.s.gcOptimizationDetails[pkg.ID()]; ok {
+			delete(c.s.gcOptimizationDetails, pkg.ID())
 			c.s.clearDiagnosticSource(gcDetailsSource)
 		} else {
-			c.s.gcOptimizationDetails[pkgDir] = struct{}{}
+			c.s.gcOptimizationDetails[pkg.ID()] = struct{}{}
 		}
 		c.s.gcOptimizationDetailsMu.Unlock()
 		c.s.diagnoseSnapshot(deps.snapshot, nil, false)
diff --git a/internal/lsp/diagnostics.go b/internal/lsp/diagnostics.go
index 479d948..8b93058 100644
--- a/internal/lsp/diagnostics.go
+++ b/internal/lsp/diagnostics.go
@@ -246,20 +246,8 @@
 	ctx, done := event.Start(ctx, "Server.diagnosePkg", tag.Snapshot.Of(snapshot.ID()), tag.Package.Of(pkg.ID()))
 	defer done()
 	includeAnalysis := alwaysAnalyze // only run analyses for packages with open files
-	var gcDetailsDir span.URI        // find the package's optimization details, if available
 	for _, pgf := range pkg.CompiledGoFiles() {
-		if snapshot.IsOpen(pgf.URI) {
-			includeAnalysis = true
-		}
-		if gcDetailsDir == "" {
-			dirURI := span.URIFromPath(filepath.Dir(pgf.URI.Filename()))
-			s.gcOptimizationDetailsMu.Lock()
-			_, ok := s.gcOptimizationDetails[dirURI]
-			s.gcOptimizationDetailsMu.Unlock()
-			if ok {
-				gcDetailsDir = dirURI
-			}
-		}
+		includeAnalysis = includeAnalysis || snapshot.IsOpen(pgf.URI)
 	}
 
 	typeCheckDiagnostics := source.GetTypeCheckDiagnostics(ctx, snapshot, pkg)
@@ -276,10 +264,14 @@
 			s.storeDiagnostics(snapshot, uri, analysisSource, diags)
 		}
 	}
-	// If gc optimization details are available, add them to the
+
+	// If gc optimization details are requested, add them to the
 	// diagnostic reports.
-	if gcDetailsDir != "" {
-		gcReports, err := source.GCOptimizationDetails(ctx, snapshot, gcDetailsDir)
+	s.gcOptimizationDetailsMu.Lock()
+	_, enableGCDetails := s.gcOptimizationDetails[pkg.ID()]
+	s.gcOptimizationDetailsMu.Unlock()
+	if enableGCDetails {
+		gcReports, err := source.GCOptimizationDetails(ctx, snapshot, pkg)
 		if err != nil {
 			event.Error(ctx, "warning: gc details", err, tag.Snapshot.Of(snapshot.ID()), tag.Package.Of(pkg.ID()))
 		}
diff --git a/internal/lsp/server.go b/internal/lsp/server.go
index df1da8c..793ee5d 100644
--- a/internal/lsp/server.go
+++ b/internal/lsp/server.go
@@ -24,7 +24,7 @@
 func NewServer(session source.Session, client protocol.Client) *Server {
 	return &Server{
 		diagnostics:           map[span.URI]*fileReports{},
-		gcOptimizationDetails: make(map[span.URI]struct{}),
+		gcOptimizationDetails: make(map[string]struct{}),
 		watchedGlobPatterns:   make(map[string]struct{}),
 		changedFiles:          make(map[span.URI]struct{}),
 		session:               session,
@@ -91,9 +91,9 @@
 
 	// gcOptimizationDetails describes the packages for which we want
 	// optimization details to be included in the diagnostics. The key is the
-	// directory of the package.
+	// ID of the package.
 	gcOptimizationDetailsMu sync.Mutex
-	gcOptimizationDetails   map[span.URI]struct{}
+	gcOptimizationDetails   map[string]struct{}
 
 	// diagnosticsSema limits the concurrency of diagnostics runs, which can be
 	// expensive.
diff --git a/internal/lsp/source/gc_annotations.go b/internal/lsp/source/gc_annotations.go
index f2a0e67..3616bbf 100644
--- a/internal/lsp/source/gc_annotations.go
+++ b/internal/lsp/source/gc_annotations.go
@@ -35,7 +35,11 @@
 	Bounds Annotation = "bounds"
 )
 
-func GCOptimizationDetails(ctx context.Context, snapshot Snapshot, pkgDir span.URI) (map[VersionedFileIdentity][]*Diagnostic, error) {
+func GCOptimizationDetails(ctx context.Context, snapshot Snapshot, pkg Package) (map[VersionedFileIdentity][]*Diagnostic, error) {
+	if len(pkg.CompiledGoFiles()) == 0 {
+		return nil, nil
+	}
+	pkgDir := filepath.Dir(pkg.CompiledGoFiles()[0].URI.Filename())
 	outDir := filepath.Join(os.TempDir(), fmt.Sprintf("gopls-%d.details", os.Getpid()))
 
 	if err := os.MkdirAll(outDir, 0700); err != nil {
@@ -60,7 +64,7 @@
 			fmt.Sprintf("-o=%s", tmpFile.Name()),
 			".",
 		},
-		WorkingDir: pkgDir.Filename(),
+		WorkingDir: pkgDir,
 	}
 	_, err = snapshot.RunGoCommandDirect(ctx, Normal, inv)
 	if err != nil {
@@ -83,7 +87,7 @@
 		if fh == nil {
 			continue
 		}
-		if pkgDir.Filename() != filepath.Dir(fh.URI().Filename()) {
+		if pkgDir != filepath.Dir(fh.URI().Filename()) {
 			// https://github.com/golang/go/issues/42198
 			// sometimes the detail diagnostics generated for files
 			// outside the package can never be taken back.