gopls/internal/lsp/source: avoid unnecessary transitive rdeps

Most callers of ReverseDependencies need only the direct importers.
The new 'transitive bool' flag lets them express that and do less
work.

Change-Id: Ib0a6b1efc892424dde0e4d2c30ad67c91212fc56
Reviewed-on: https://go-review.googlesource.com/c/tools/+/458855
gopls-CI: kokoro <noreply+kokoro@google.com>
Run-TryBot: Alan Donovan <adonovan@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Robert Findley <rfindley@google.com>
Auto-Submit: Alan Donovan <adonovan@google.com>
diff --git a/gopls/internal/lsp/cache/graph.go b/gopls/internal/lsp/cache/graph.go
index 5df0234..aaabbdf 100644
--- a/gopls/internal/lsp/cache/graph.go
+++ b/gopls/internal/lsp/cache/graph.go
@@ -103,10 +103,10 @@
 	}
 }
 
-// reverseTransitiveClosure returns a new mapping containing the
+// reverseReflexiveTransitiveClosure returns a new mapping containing the
 // metadata for the specified packages along with any package that
-// transitively imports one of them, keyed by ID.
-func (g *metadataGraph) reverseTransitiveClosure(ids ...PackageID) map[PackageID]*source.Metadata {
+// transitively imports one of them, keyed by ID, including all the initial packages.
+func (g *metadataGraph) reverseReflexiveTransitiveClosure(ids ...PackageID) map[PackageID]*source.Metadata {
 	seen := make(map[PackageID]*source.Metadata)
 	var visitAll func([]PackageID)
 	visitAll = func(ids []PackageID) {
diff --git a/gopls/internal/lsp/cache/snapshot.go b/gopls/internal/lsp/cache/snapshot.go
index ea57d57..bda3690 100644
--- a/gopls/internal/lsp/cache/snapshot.go
+++ b/gopls/internal/lsp/cache/snapshot.go
@@ -819,19 +819,32 @@
 	return metas, nil
 }
 
-func (s *snapshot) ReverseDependencies(ctx context.Context, id PackageID) (map[PackageID]*source.Metadata, error) {
+func (s *snapshot) ReverseDependencies(ctx context.Context, id PackageID, transitive bool) (map[PackageID]*source.Metadata, error) {
 	if err := s.awaitLoaded(ctx); err != nil {
 		return nil, err
 	}
 	s.mu.Lock()
 	meta := s.meta
 	s.mu.Unlock()
-	rdeps := meta.reverseTransitiveClosure(id)
 
-	// Remove the original package ID from the map.
-	// TODO(adonovan): we should make ReverseDependencies and
-	// reverseTransitiveClosure consistent wrt reflexiveness.
-	delete(rdeps, id)
+	var rdeps map[PackageID]*source.Metadata
+	if transitive {
+		rdeps = meta.reverseReflexiveTransitiveClosure(id)
+
+		// Remove the original package ID from the map.
+		// (Callers all want irreflexivity but it's easier
+		// to compute reflexively then subtract.)
+		delete(rdeps, id)
+
+	} else {
+		// direct reverse dependencies
+		rdeps = make(map[PackageID]*source.Metadata)
+		for _, rdepID := range meta.importedBy[id] {
+			if rdep := meta.metadata[rdepID]; rdep != nil {
+				rdeps[rdepID] = rdep
+			}
+		}
+	}
 
 	return rdeps, nil
 }