internal/lsp: ignore files in the builtin package

This change stops diagnostics from running in files making up the "fake"
builtin package.

Fixes golang/go#31962

Change-Id: Ic54e1587e3ad54f0c1f5e82f1a6f3522b4c6bee9
Reviewed-on: https://go-review.googlesource.com/c/tools/+/177218
Run-TryBot: Rebecca Stambler <rstambler@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ian Cottrell <iancottrell@google.com>
diff --git a/internal/lsp/cache/view.go b/internal/lsp/cache/view.go
index 3b59ed5..6ef8ca8 100644
--- a/internal/lsp/cache/view.go
+++ b/internal/lsp/cache/view.go
@@ -6,6 +6,7 @@
 
 import (
 	"context"
+	"fmt"
 	"go/ast"
 	"go/parser"
 	"go/token"
@@ -272,15 +273,18 @@
 
 // getFile is the unlocked internal implementation of GetFile.
 func (v *View) getFile(uri span.URI) (*File, error) {
+	filename, err := uri.Filename()
+	if err != nil {
+		return nil, err
+	}
+	if v.isIgnored(filename) {
+		return nil, fmt.Errorf("%s is ignored", filename)
+	}
 	if f, err := v.findFile(uri); err != nil {
 		return nil, err
 	} else if f != nil {
 		return f, nil
 	}
-	filename, err := uri.Filename()
-	if err != nil {
-		return nil, err
-	}
 	f := &File{
 		view:     v,
 		filename: filename,
@@ -289,6 +293,20 @@
 	return f, nil
 }
 
+// isIgnored checks if the given filename is a file we ignore.
+// As of right now, we only ignore files in the "builtin" package.
+func (v *View) isIgnored(filename string) bool {
+	bpkg := v.BuiltinPackage()
+	if bpkg != nil {
+		for builtinFilename := range bpkg.Files {
+			if filename == builtinFilename {
+				return true
+			}
+		}
+	}
+	return false
+}
+
 // findFile checks the cache for any file matching the given uri.
 //
 // An error is only returned for an irreparable failure, for example, if the
diff --git a/internal/lsp/diagnostics.go b/internal/lsp/diagnostics.go
index 040c383..2af02da 100644
--- a/internal/lsp/diagnostics.go
+++ b/internal/lsp/diagnostics.go
@@ -13,18 +13,6 @@
 	"golang.org/x/tools/internal/span"
 )
 
-func (s *Server) cacheAndDiagnose(ctx context.Context, uri span.URI, content string) error {
-	view := s.findView(ctx, uri)
-	if err := view.SetContent(ctx, uri, []byte(content)); err != nil {
-		return err
-	}
-	go func() {
-		ctx := view.BackgroundContext()
-		s.Diagnostics(ctx, view, uri)
-	}()
-	return nil
-}
-
 func (s *Server) Diagnostics(ctx context.Context, view *cache.View, uri span.URI) {
 	if ctx.Err() != nil {
 		s.log.Errorf(ctx, "canceling diagnostics for %s: %v", uri, ctx.Err())
diff --git a/internal/lsp/server.go b/internal/lsp/server.go
index 0a23f69..047be4e 100644
--- a/internal/lsp/server.go
+++ b/internal/lsp/server.go
@@ -135,7 +135,7 @@
 // Text Synchronization
 
 func (s *Server) DidOpen(ctx context.Context, params *protocol.DidOpenTextDocumentParams) error {
-	return s.cacheAndDiagnose(ctx, span.NewURI(params.TextDocument.URI), params.TextDocument.Text)
+	return s.didOpen(ctx, params)
 }
 
 func (s *Server) DidChange(ctx context.Context, params *protocol.DidChangeTextDocumentParams) error {
diff --git a/internal/lsp/source/source_test.go b/internal/lsp/source/source_test.go
index f840e41..7c3db45 100644
--- a/internal/lsp/source/source_test.go
+++ b/internal/lsp/source/source_test.go
@@ -156,15 +156,6 @@
 			t.Errorf("%s: %s", src, diff)
 		}
 	}
-	// Make sure we don't crash completing the first position in file set.
-	uri := span.FileURI(r.view.FileSet().File(1).Name())
-	f, err := r.view.GetFile(ctx, uri)
-	if err != nil {
-		t.Fatalf("failed for %v: %v", uri, err)
-	}
-	source.Completion(ctx, f, 1)
-
-	r.checkCompletionSnippets(ctx, t, snippets, items)
 }
 
 func (r *runner) checkCompletionSnippets(ctx context.Context, t *testing.T, data tests.CompletionSnippets, items tests.CompletionItems) {
diff --git a/internal/lsp/text_synchronization.go b/internal/lsp/text_synchronization.go
index a29edd5..1667773 100644
--- a/internal/lsp/text_synchronization.go
+++ b/internal/lsp/text_synchronization.go
@@ -13,6 +13,10 @@
 	"golang.org/x/tools/internal/span"
 )
 
+func (s *Server) didOpen(ctx context.Context, params *protocol.DidOpenTextDocumentParams) error {
+	return s.cacheAndDiagnose(ctx, span.NewURI(params.TextDocument.URI), []byte(params.TextDocument.Text))
+}
+
 func (s *Server) didChange(ctx context.Context, params *protocol.DidChangeTextDocumentParams) error {
 	if len(params.ContentChanges) < 1 {
 		return jsonrpc2.NewErrorf(jsonrpc2.CodeInternalError, "no content changes provided")
@@ -34,7 +38,19 @@
 		}
 		text = change.Text
 	}
-	return s.cacheAndDiagnose(ctx, span.NewURI(params.TextDocument.URI), text)
+	return s.cacheAndDiagnose(ctx, span.NewURI(params.TextDocument.URI), []byte(text))
+}
+
+func (s *Server) cacheAndDiagnose(ctx context.Context, uri span.URI, content []byte) error {
+	view := s.findView(ctx, uri)
+	if err := view.SetContent(ctx, uri, content); err != nil {
+		return err
+	}
+	go func() {
+		ctx := view.BackgroundContext()
+		s.Diagnostics(ctx, view, uri)
+	}()
+	return nil
 }
 
 func (s *Server) applyChanges(ctx context.Context, params *protocol.DidChangeTextDocumentParams) (string, error) {