gopls/internal/lsp/source: use metadata files for workspace symbols

The workspace symbol handler was walking the files map ("seen files")
rather than the package graph.

In golang/go#57558, we endeavor to avoid any dependency on seen files,
and pragmatically we will soon no longer read the entire workspace
during loading.

For golang/go#57558

Change-Id: Ie95a333842af24ab801bb3e314f5c00e1e5e1b0b
Reviewed-on: https://go-review.googlesource.com/c/tools/+/468775
Reviewed-by: Alan Donovan <adonovan@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Run-TryBot: Robert Findley <rfindley@google.com>
gopls-CI: kokoro <noreply+kokoro@google.com>
diff --git a/gopls/internal/lsp/cache/snapshot.go b/gopls/internal/lsp/cache/snapshot.go
index dd18018..ab42556 100644
--- a/gopls/internal/lsp/cache/snapshot.go
+++ b/gopls/internal/lsp/cache/snapshot.go
@@ -1022,18 +1022,30 @@
 	return active, nil
 }
 
-// Symbols extracts and returns the symbols for each file in all the snapshot's views.
-func (s *snapshot) Symbols(ctx context.Context) map[span.URI][]source.Symbol {
-	// Read the set of Go files out of the snapshot.
-	var goFiles []source.FileHandle
+// Symbols extracts and returns symbol information for every file contained in
+// a loaded package. It awaits snapshot loading.
+//
+// TODO(rfindley): move this to the top of cache/symbols.go
+func (s *snapshot) Symbols(ctx context.Context) (map[span.URI][]source.Symbol, error) {
+	if err := s.awaitLoaded(ctx); err != nil {
+		return nil, err
+	}
+
+	// Build symbols for all loaded Go files.
 	s.mu.Lock()
-	s.files.Range(func(uri span.URI, f source.FileHandle) {
-		if s.View().FileKind(f) == source.Go {
-			goFiles = append(goFiles, f)
-		}
-	})
+	meta := s.meta
 	s.mu.Unlock()
 
+	goFiles := make(map[span.URI]struct{})
+	for _, m := range meta.metadata {
+		for _, uri := range m.GoFiles {
+			goFiles[uri] = struct{}{}
+		}
+		for _, uri := range m.CompiledGoFiles {
+			goFiles[uri] = struct{}{}
+		}
+	}
+
 	// Symbolize them in parallel.
 	var (
 		group    errgroup.Group
@@ -1042,15 +1054,15 @@
 		result   = make(map[span.URI][]source.Symbol)
 	)
 	group.SetLimit(nprocs)
-	for _, f := range goFiles {
-		f := f
+	for uri := range goFiles {
+		uri := uri
 		group.Go(func() error {
-			symbols, err := s.symbolize(ctx, f)
+			symbols, err := s.symbolize(ctx, uri)
 			if err != nil {
 				return err
 			}
 			resultMu.Lock()
-			result[f.URI()] = symbols
+			result[uri] = symbols
 			resultMu.Unlock()
 			return nil
 		})
@@ -1060,7 +1072,7 @@
 	if err := group.Wait(); err != nil {
 		event.Error(ctx, "getting snapshot symbols", err)
 	}
-	return result
+	return result, nil
 }
 
 func (s *snapshot) AllMetadata(ctx context.Context) ([]*source.Metadata, error) {
diff --git a/gopls/internal/lsp/cache/symbols.go b/gopls/internal/lsp/cache/symbols.go
index 8cdb147..7fa3584 100644
--- a/gopls/internal/lsp/cache/symbols.go
+++ b/gopls/internal/lsp/cache/symbols.go
@@ -14,12 +14,12 @@
 
 	"golang.org/x/tools/gopls/internal/lsp/protocol"
 	"golang.org/x/tools/gopls/internal/lsp/source"
+	"golang.org/x/tools/gopls/internal/span"
 	"golang.org/x/tools/internal/memoize"
 )
 
-// symbolize returns the result of symbolizing the file identified by fh, using a cache.
-func (s *snapshot) symbolize(ctx context.Context, fh source.FileHandle) ([]source.Symbol, error) {
-	uri := fh.URI()
+// symbolize returns the result of symbolizing the file identified by uri, using a cache.
+func (s *snapshot) symbolize(ctx context.Context, uri span.URI) ([]source.Symbol, error) {
 
 	s.mu.Lock()
 	entry, hit := s.symbolizeHandles.Get(uri)
@@ -32,6 +32,10 @@
 
 	// Cache miss?
 	if !hit {
+		fh, err := s.GetFile(ctx, uri)
+		if err != nil {
+			return nil, err
+		}
 		type symbolHandleKey source.Hash
 		key := symbolHandleKey(fh.FileIdentity().Hash)
 		promise, release := s.store.Promise(key, func(_ context.Context, arg interface{}) interface{} {
diff --git a/gopls/internal/lsp/source/view.go b/gopls/internal/lsp/source/view.go
index 172a4fb..1bfca02 100644
--- a/gopls/internal/lsp/source/view.go
+++ b/gopls/internal/lsp/source/view.go
@@ -175,7 +175,7 @@
 	AllMetadata(ctx context.Context) ([]*Metadata, error)
 
 	// Symbols returns all symbols in the snapshot.
-	Symbols(ctx context.Context) map[span.URI][]Symbol
+	Symbols(ctx context.Context) (map[span.URI][]Symbol, error)
 
 	// Metadata returns the metadata for the specified package,
 	// or nil if it was not found.
diff --git a/gopls/internal/lsp/source/workspace_symbol.go b/gopls/internal/lsp/source/workspace_symbol.go
index 1b157c6..17c3a24 100644
--- a/gopls/internal/lsp/source/workspace_symbol.go
+++ b/gopls/internal/lsp/source/workspace_symbol.go
@@ -298,7 +298,6 @@
 //   - A symbolizer determines how we extract the symbol for an object. This
 //     enables the 'symbolStyle' configuration option.
 func collectSymbols(ctx context.Context, views []View, matcherType SymbolMatcher, symbolizer symbolizer, query string) ([]protocol.SymbolInformation, error) {
-
 	// Extract symbols from all files.
 	var work []symbolFile
 	var roots []string
@@ -318,7 +317,11 @@
 		filters := v.Options().DirectoryFilters
 		filterer := NewFilterer(filters)
 		folder := filepath.ToSlash(v.Folder().Filename())
-		for uri, syms := range snapshot.Symbols(ctx) {
+		symbols, err := snapshot.Symbols(ctx)
+		if err != nil {
+			return nil, err
+		}
+		for uri, syms := range symbols {
 			norm := filepath.ToSlash(uri.Filename())
 			nm := strings.TrimPrefix(norm, folder)
 			if filterer.Disallow(nm) {