internal/lsp/source: fix race in workspace symbols with multiple views

Unfortunately only after merging CL 338729 did I use it in a multi-view
workspace. That CL added a goroutine per matcher to scan symbols, but
unfortunately did this for each view, resulting in a race if there are
multiple views.

The fix is straightforward.

Change-Id: I405b37921883f9617f17c1e1506ff67b4c661cbc
Reviewed-on: https://go-review.googlesource.com/c/tools/+/340970
Trust: Robert Findley <rfindley@google.com>
Run-TryBot: Robert Findley <rfindley@google.com>
gopls-CI: kokoro <noreply+kokoro@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
diff --git a/internal/lsp/source/workspace_symbol.go b/internal/lsp/source/workspace_symbol.go
index 9172edf..ecea2b4 100644
--- a/internal/lsp/source/workspace_symbol.go
+++ b/internal/lsp/source/workspace_symbol.go
@@ -306,6 +306,7 @@
 
 	results := make(chan *symbolStore)
 	matcherlen := len(sc.matchers)
+	files := make(map[span.URI]symbolFile)
 
 	for _, v := range views {
 		snapshot, release := v.Snapshot(ctx)
@@ -314,9 +315,12 @@
 		if err != nil {
 			return nil, err
 		}
-		var work []symbolFile
 
 		for uri, syms := range psyms {
+			// Only scan each file once.
+			if _, ok := files[uri]; ok {
+				continue
+			}
 			mds, err := snapshot.MetadataForFile(ctx, uri)
 			if err != nil {
 				return nil, err
@@ -325,26 +329,30 @@
 				// TODO: should use the bug reporting API
 				continue
 			}
-			work = append(work, symbolFile{uri, mds[0], syms})
+			files[uri] = symbolFile{uri, mds[0], syms}
 		}
+	}
 
-		// Compute matches concurrently. Each symbolWorker has its own symbolStore,
-		// which we merge at the end.
+	var work []symbolFile
+	for _, f := range files {
+		work = append(work, f)
+	}
 
-		for i, matcher := range sc.matchers {
-			go func(i int, matcher matcherFunc) {
-				w := &symbolWorker{
-					symbolizer: sc.symbolizer,
-					matcher:    matcher,
-					ss:         &symbolStore{},
-					roots:      roots,
-				}
-				for j := i; j < len(work); j += matcherlen {
-					w.matchFile(work[j])
-				}
-				results <- w.ss
-			}(i, matcher)
-		}
+	// Compute matches concurrently. Each symbolWorker has its own symbolStore,
+	// which we merge at the end.
+	for i, matcher := range sc.matchers {
+		go func(i int, matcher matcherFunc) {
+			w := &symbolWorker{
+				symbolizer: sc.symbolizer,
+				matcher:    matcher,
+				ss:         &symbolStore{},
+				roots:      roots,
+			}
+			for j := i; j < len(work); j += matcherlen {
+				w.matchFile(work[j])
+			}
+			results <- w.ss
+		}(i, matcher)
 	}
 
 	for i := 0; i < matcherlen; i++ {