internal/lsp: fix panic in bestView
Rather than panicking when we have not created any views for the packages,
we should show a reasonable error to the user. This change propagates the
errors to the user.
Updates golang/go#35599
Change-Id: I49789d8ce18e154f111bc3584488f468a129e30c
Reviewed-on: https://go-review.googlesource.com/c/tools/+/207344
Run-TryBot: Rebecca Stambler <rstambler@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Michael Matloob <matloob@golang.org>
diff --git a/internal/lsp/cache/session.go b/internal/lsp/cache/session.go
index cb80f19..8f1158f 100644
--- a/internal/lsp/cache/session.go
+++ b/internal/lsp/cache/session.go
@@ -169,18 +169,21 @@
// ViewOf returns a view corresponding to the given URI.
// If the file is not already associated with a view, pick one using some heuristics.
-func (s *session) ViewOf(uri span.URI) source.View {
+func (s *session) ViewOf(uri span.URI) (source.View, error) {
s.viewMu.Lock()
defer s.viewMu.Unlock()
// Check if we already know this file.
if v, found := s.viewMap[uri]; found {
- return v
+ return v, nil
}
// Pick the best view for this file and memoize the result.
- v := s.bestView(uri)
+ v, err := s.bestView(uri)
+ if err != nil {
+ return nil, err
+ }
s.viewMap[uri] = v
- return v
+ return v, nil
}
func (s *session) viewsOf(uri span.URI) []*view {
@@ -208,7 +211,10 @@
// bestView finds the best view to associate a given URI with.
// viewMu must be held when calling this method.
-func (s *session) bestView(uri span.URI) source.View {
+func (s *session) bestView(uri span.URI) (source.View, error) {
+ if len(s.views) == 0 {
+ return nil, errors.Errorf("no views in the session")
+ }
// we need to find the best view for this file
var longest source.View
for _, view := range s.views {
@@ -220,10 +226,10 @@
}
}
if longest != nil {
- return longest
+ return longest, nil
}
// TODO: are there any more heuristics we can use?
- return s.views[0]
+ return s.views[0], nil
}
func (s *session) removeView(ctx context.Context, view *view) error {
@@ -291,7 +297,10 @@
}
// Make sure that the file gets added to the session's file watch map.
- view := s.bestView(uri)
+ view, err := s.bestView(uri)
+ if err != nil {
+ return err
+ }
if _, err := view.GetFile(ctx, uri); err != nil {
return err
}
diff --git a/internal/lsp/code_action.go b/internal/lsp/code_action.go
index cf66505..8db4b93 100644
--- a/internal/lsp/code_action.go
+++ b/internal/lsp/code_action.go
@@ -21,7 +21,10 @@
func (s *Server) codeAction(ctx context.Context, params *protocol.CodeActionParams) ([]protocol.CodeAction, error) {
uri := span.NewURI(params.TextDocument.URI)
- view := s.session.ViewOf(uri)
+ view, err := s.session.ViewOf(uri)
+ if err != nil {
+ return nil, err
+ }
f, err := view.GetFile(ctx, uri)
if err != nil {
return nil, err
diff --git a/internal/lsp/command.go b/internal/lsp/command.go
index 72e8647..5075e90 100644
--- a/internal/lsp/command.go
+++ b/internal/lsp/command.go
@@ -17,7 +17,10 @@
}
// Confirm that this action is being taken on a go.mod file.
uri := span.NewURI(params.Arguments[0].(string))
- view := s.session.ViewOf(uri)
+ view, err := s.session.ViewOf(uri)
+ if err != nil {
+ return nil, err
+ }
f, err := view.GetFile(ctx, uri)
if err != nil {
return nil, err
diff --git a/internal/lsp/completion.go b/internal/lsp/completion.go
index 949a433..4b1e390 100644
--- a/internal/lsp/completion.go
+++ b/internal/lsp/completion.go
@@ -18,7 +18,10 @@
func (s *Server) completion(ctx context.Context, params *protocol.CompletionParams) (*protocol.CompletionList, error) {
uri := span.NewURI(params.TextDocument.URI)
- view := s.session.ViewOf(uri)
+ view, err := s.session.ViewOf(uri)
+ if err != nil {
+ return nil, err
+ }
options := view.Options()
f, err := view.GetFile(ctx, uri)
if err != nil {
diff --git a/internal/lsp/completion_test.go b/internal/lsp/completion_test.go
index 0ee2e06..6fdffa1 100644
--- a/internal/lsp/completion_test.go
+++ b/internal/lsp/completion_test.go
@@ -120,12 +120,15 @@
func (r *runner) callCompletion(t *testing.T, src span.Span, options source.CompletionOptions) []protocol.CompletionItem {
t.Helper()
- view := r.server.session.ViewOf(src.URI())
+ view, err := r.server.session.ViewOf(src.URI())
+ if err != nil {
+ t.Fatal(err)
+ }
original := view.Options()
modified := original
modified.InsertTextFormat = protocol.SnippetTextFormat
modified.Completion = options
- view, err := view.SetOptions(r.ctx, modified)
+ view, err = view.SetOptions(r.ctx, modified)
if err != nil {
t.Error(err)
return nil
diff --git a/internal/lsp/definition.go b/internal/lsp/definition.go
index f8f18df..6e3cae2 100644
--- a/internal/lsp/definition.go
+++ b/internal/lsp/definition.go
@@ -14,7 +14,10 @@
func (s *Server) definition(ctx context.Context, params *protocol.DefinitionParams) ([]protocol.Location, error) {
uri := span.NewURI(params.TextDocument.URI)
- view := s.session.ViewOf(uri)
+ view, err := s.session.ViewOf(uri)
+ if err != nil {
+ return nil, err
+ }
f, err := view.GetFile(ctx, uri)
if err != nil {
return nil, err
@@ -37,7 +40,10 @@
func (s *Server) typeDefinition(ctx context.Context, params *protocol.TypeDefinitionParams) ([]protocol.Location, error) {
uri := span.NewURI(params.TextDocument.URI)
- view := s.session.ViewOf(uri)
+ view, err := s.session.ViewOf(uri)
+ if err != nil {
+ return nil, err
+ }
f, err := view.GetFile(ctx, uri)
if err != nil {
return nil, err
diff --git a/internal/lsp/folding_range.go b/internal/lsp/folding_range.go
index d0eecc4..5d88e74 100644
--- a/internal/lsp/folding_range.go
+++ b/internal/lsp/folding_range.go
@@ -10,7 +10,10 @@
func (s *Server) foldingRange(ctx context.Context, params *protocol.FoldingRangeParams) ([]protocol.FoldingRange, error) {
uri := span.NewURI(params.TextDocument.URI)
- view := s.session.ViewOf(uri)
+ view, err := s.session.ViewOf(uri)
+ if err != nil {
+ return nil, err
+ }
f, err := view.GetFile(ctx, uri)
if err != nil {
return nil, err
diff --git a/internal/lsp/format.go b/internal/lsp/format.go
index 33e9b8d..b16bfb6 100644
--- a/internal/lsp/format.go
+++ b/internal/lsp/format.go
@@ -14,7 +14,10 @@
func (s *Server) formatting(ctx context.Context, params *protocol.DocumentFormattingParams) ([]protocol.TextEdit, error) {
uri := span.NewURI(params.TextDocument.URI)
- view := s.session.ViewOf(uri)
+ view, err := s.session.ViewOf(uri)
+ if err != nil {
+ return nil, err
+ }
f, err := view.GetFile(ctx, uri)
if err != nil {
return nil, err
diff --git a/internal/lsp/general.go b/internal/lsp/general.go
index a18fdd2..caab70b 100644
--- a/internal/lsp/general.go
+++ b/internal/lsp/general.go
@@ -166,11 +166,23 @@
debug.PrintVersionInfo(buf, true, debug.PlainText)
log.Print(ctx, buf.String())
+ viewErrors := make(map[span.URI]error)
for _, folder := range s.pendingFolders {
+ uri := span.NewURI(folder.URI)
if _, err := s.addView(ctx, folder.Name, span.NewURI(folder.URI)); err != nil {
- return err
+ viewErrors[uri] = err
}
}
+ if len(viewErrors) > 0 {
+ errMsg := fmt.Sprintf("Error loading workspace folders (expected %v, got %v)\n", len(s.pendingFolders), len(s.session.Views()))
+ for uri, err := range viewErrors {
+ errMsg += fmt.Sprintf("failed to load view for %s: %v\n", uri, err)
+ }
+ s.client.ShowMessage(ctx, &protocol.ShowMessageParams{
+ Type: protocol.Error,
+ Message: errMsg,
+ })
+ }
s.pendingFolders = nil
return nil
diff --git a/internal/lsp/highlight.go b/internal/lsp/highlight.go
index 8a579a6..c873127 100644
--- a/internal/lsp/highlight.go
+++ b/internal/lsp/highlight.go
@@ -16,7 +16,10 @@
func (s *Server) documentHighlight(ctx context.Context, params *protocol.DocumentHighlightParams) ([]protocol.DocumentHighlight, error) {
uri := span.NewURI(params.TextDocument.URI)
- view := s.session.ViewOf(uri)
+ view, err := s.session.ViewOf(uri)
+ if err != nil {
+ return nil, err
+ }
rngs, err := source.Highlight(ctx, view, uri, params.Position)
if err != nil {
log.Error(ctx, "no highlight", err, telemetry.URI.Of(uri))
diff --git a/internal/lsp/hover.go b/internal/lsp/hover.go
index ddf1a32..4377e67 100644
--- a/internal/lsp/hover.go
+++ b/internal/lsp/hover.go
@@ -17,7 +17,10 @@
func (s *Server) hover(ctx context.Context, params *protocol.HoverParams) (*protocol.Hover, error) {
uri := span.NewURI(params.TextDocument.URI)
- view := s.session.ViewOf(uri)
+ view, err := s.session.ViewOf(uri)
+ if err != nil {
+ return nil, err
+ }
f, err := view.GetFile(ctx, uri)
if err != nil {
return nil, err
diff --git a/internal/lsp/implementation.go b/internal/lsp/implementation.go
index daca99f..09f18e4 100644
--- a/internal/lsp/implementation.go
+++ b/internal/lsp/implementation.go
@@ -14,7 +14,10 @@
func (s *Server) implementation(ctx context.Context, params *protocol.ImplementationParams) ([]protocol.Location, error) {
uri := span.NewURI(params.TextDocument.URI)
- view := s.session.ViewOf(uri)
+ view, err := s.session.ViewOf(uri)
+ if err != nil {
+ return nil, err
+ }
f, err := view.GetFile(ctx, uri)
if err != nil {
return nil, err
diff --git a/internal/lsp/link.go b/internal/lsp/link.go
index 92777fb..c9f89f2 100644
--- a/internal/lsp/link.go
+++ b/internal/lsp/link.go
@@ -22,7 +22,10 @@
func (s *Server) documentLink(ctx context.Context, params *protocol.DocumentLinkParams) ([]protocol.DocumentLink, error) {
uri := span.NewURI(params.TextDocument.URI)
- view := s.session.ViewOf(uri)
+ view, err := s.session.ViewOf(uri)
+ if err != nil {
+ return nil, err
+ }
f, err := view.GetFile(ctx, uri)
if err != nil {
return nil, err
diff --git a/internal/lsp/lsp_test.go b/internal/lsp/lsp_test.go
index c4daa4b..e2ee9af 100644
--- a/internal/lsp/lsp_test.go
+++ b/internal/lsp/lsp_test.go
@@ -98,13 +98,16 @@
func (r *runner) FoldingRanges(t *testing.T, spn span.Span) {
uri := spn.URI()
- view := r.server.session.ViewOf(uri)
+ view, err := r.server.session.ViewOf(uri)
+ if err != nil {
+ t.Fatal(err)
+ }
original := view.Options()
modified := original
// Test all folding ranges.
modified.LineFoldingOnly = false
- view, err := view.SetOptions(r.ctx, modified)
+ view, err = view.SetOptions(r.ctx, modified)
if err != nil {
t.Error(err)
return
@@ -326,7 +329,10 @@
func (r *runner) SuggestedFix(t *testing.T, spn span.Span) {
uri := spn.URI()
filename := uri.Filename()
- view := r.server.session.ViewOf(uri)
+ view, err := r.server.session.ViewOf(uri)
+ if err != nil {
+ t.Fatal(err)
+ }
f, err := view.GetFile(r.ctx, uri)
if err != nil {
t.Fatal(err)
diff --git a/internal/lsp/references.go b/internal/lsp/references.go
index 6930a6c..a292035 100644
--- a/internal/lsp/references.go
+++ b/internal/lsp/references.go
@@ -16,7 +16,10 @@
func (s *Server) references(ctx context.Context, params *protocol.ReferenceParams) ([]protocol.Location, error) {
uri := span.NewURI(params.TextDocument.URI)
- view := s.session.ViewOf(uri)
+ view, err := s.session.ViewOf(uri)
+ if err != nil {
+ return nil, err
+ }
f, err := view.GetFile(ctx, uri)
if err != nil {
return nil, err
diff --git a/internal/lsp/rename.go b/internal/lsp/rename.go
index 9664b5c..cc9f1a1 100644
--- a/internal/lsp/rename.go
+++ b/internal/lsp/rename.go
@@ -14,7 +14,10 @@
func (s *Server) rename(ctx context.Context, params *protocol.RenameParams) (*protocol.WorkspaceEdit, error) {
uri := span.NewURI(params.TextDocument.URI)
- view := s.session.ViewOf(uri)
+ view, err := s.session.ViewOf(uri)
+ if err != nil {
+ return nil, err
+ }
f, err := view.GetFile(ctx, uri)
if err != nil {
return nil, err
@@ -43,7 +46,10 @@
func (s *Server) prepareRename(ctx context.Context, params *protocol.PrepareRenameParams) (*protocol.Range, error) {
uri := span.NewURI(params.TextDocument.URI)
- view := s.session.ViewOf(uri)
+ view, err := s.session.ViewOf(uri)
+ if err != nil {
+ return nil, err
+ }
f, err := view.GetFile(ctx, uri)
if err != nil {
return nil, err
diff --git a/internal/lsp/signature_help.go b/internal/lsp/signature_help.go
index 5771ef1..f65a7a3 100644
--- a/internal/lsp/signature_help.go
+++ b/internal/lsp/signature_help.go
@@ -16,7 +16,10 @@
func (s *Server) signatureHelp(ctx context.Context, params *protocol.SignatureHelpParams) (*protocol.SignatureHelp, error) {
uri := span.NewURI(params.TextDocument.URI)
- view := s.session.ViewOf(uri)
+ view, err := s.session.ViewOf(uri)
+ if err != nil {
+ return nil, err
+ }
f, err := view.GetFile(ctx, uri)
if err != nil {
return nil, err
diff --git a/internal/lsp/source/view.go b/internal/lsp/source/view.go
index 16907b5..2e433fd 100644
--- a/internal/lsp/source/view.go
+++ b/internal/lsp/source/view.go
@@ -165,7 +165,7 @@
View(name string) View
// ViewOf returns a view corresponding to the given URI.
- ViewOf(uri span.URI) View
+ ViewOf(uri span.URI) (View, error)
// Views returns the set of active views built by this session.
Views() []View
diff --git a/internal/lsp/symbols.go b/internal/lsp/symbols.go
index 66f1654..b4d3a3f 100644
--- a/internal/lsp/symbols.go
+++ b/internal/lsp/symbols.go
@@ -18,7 +18,10 @@
defer done()
uri := span.NewURI(params.TextDocument.URI)
- view := s.session.ViewOf(uri)
+ view, err := s.session.ViewOf(uri)
+ if err != nil {
+ return nil, err
+ }
f, err := view.GetFile(ctx, uri)
if err != nil {
return nil, err
diff --git a/internal/lsp/text_synchronization.go b/internal/lsp/text_synchronization.go
index fbb67bf..680d46b 100644
--- a/internal/lsp/text_synchronization.go
+++ b/internal/lsp/text_synchronization.go
@@ -29,7 +29,10 @@
// Open the file.
s.session.DidOpen(ctx, uri, fileKind, version, text)
- view := s.session.ViewOf(uri)
+ view, err := s.session.ViewOf(uri)
+ if err != nil {
+ return err
+ }
// Run diagnostics on the newly-changed file.
go s.diagnostics(view, uri)
@@ -64,7 +67,10 @@
}
}
// Cache the new file content and send fresh diagnostics.
- view := s.session.ViewOf(uri)
+ view, err := s.session.ViewOf(uri)
+ if err != nil {
+ return err
+ }
wasFirstChange, err := view.SetContent(ctx, uri, params.TextDocument.Version, []byte(text))
if err != nil {
return err
@@ -139,7 +145,10 @@
uri := span.NewURI(params.TextDocument.URI)
ctx = telemetry.URI.With(ctx, uri)
s.session.DidClose(uri)
- view := s.session.ViewOf(uri)
+ view, err := s.session.ViewOf(uri)
+ if err != nil {
+ return err
+ }
if _, err := view.SetContent(ctx, uri, -1, nil); err != nil {
return err
}