gopls/internal/lsp/cache: simplify canonical URI cache

The View's URI canonicalization cache entry, fileBase,
used to hold a list of URIs even though only the first
is ever needed. This change simplifies it to a pair
of (URI, filename). This should reduce allocation slightly.

Also:
- Eliminate the unnecessary pointer type to further reduce allocation.
- Use a dedicated mutex for the two maps.
- Update the names to better reflect the purpose.
- Document various failed approaches to optimization
  and two problems in the existing logic, one
  of correctness, the other of performance.

Change-Id: Ic74ae4bae8864de292fe97d26c5f9aacf2367961
Reviewed-on: https://go-review.googlesource.com/c/tools/+/454435
Run-TryBot: Alan Donovan <adonovan@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: 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 80de11a..1ef4622 100644
--- a/gopls/internal/lsp/cache/snapshot.go
+++ b/gopls/internal/lsp/cache/snapshot.go
@@ -1287,12 +1287,12 @@
 }
 
 func (s *snapshot) FindFile(uri span.URI) source.VersionedFileHandle {
-	f := s.view.getFile(uri)
+	uri, _ = s.view.canonicalURI(uri, true)
 
 	s.mu.Lock()
 	defer s.mu.Unlock()
 
-	result, _ := s.files.Get(f.URI())
+	result, _ := s.files.Get(uri)
 	return result
 }
 
@@ -1302,11 +1302,22 @@
 // GetVersionedFile succeeds even if the file does not exist. A non-nil error return
 // indicates some type of internal error, for example if ctx is cancelled.
 func (s *snapshot) GetVersionedFile(ctx context.Context, uri span.URI) (source.VersionedFileHandle, error) {
-	f := s.view.getFile(uri)
+	uri, _ = s.view.canonicalURI(uri, true)
 
 	s.mu.Lock()
 	defer s.mu.Unlock()
-	return s.getFileLocked(ctx, f)
+
+	if fh, ok := s.files.Get(uri); ok {
+		return fh, nil
+	}
+
+	fh, err := s.view.cache.getFile(ctx, uri) // read the file
+	if err != nil {
+		return nil, err
+	}
+	closed := &closedFile{fh}
+	s.files.Set(uri, closed)
+	return closed, nil
 }
 
 // GetFile implements the fileSource interface by wrapping GetVersionedFile.
@@ -1314,20 +1325,6 @@
 	return s.GetVersionedFile(ctx, uri)
 }
 
-func (s *snapshot) getFileLocked(ctx context.Context, f *fileBase) (source.VersionedFileHandle, error) {
-	if fh, ok := s.files.Get(f.URI()); ok {
-		return fh, nil
-	}
-
-	fh, err := s.view.cache.getFile(ctx, f.URI()) // read the file
-	if err != nil {
-		return nil, err
-	}
-	closed := &closedFile{fh}
-	s.files.Set(f.URI(), closed)
-	return closed, nil
-}
-
 func (s *snapshot) IsOpen(uri span.URI) bool {
 	s.mu.Lock()
 	defer s.mu.Unlock()