Skip to content

Commit d88f798

Browse files
evandigbyianthehat
authored andcommitted
internal/lsp: fix swallowed package errors
If a package has an error that makes it completely unparseable, such as containing a .go file with no "package" statement, the error was previously unreported. Such errors would manifest as other errors. Fixes golang/go#31712 Change-Id: I11b8d0e2e4d64b03fbcb4c35e7f0b02fccc83fad GitHub-Last-Rev: 1581cbe GitHub-Pull-Request: #102 Reviewed-on: https://go-review.googlesource.com/c/tools/+/177605 Run-TryBot: Ian Cottrell <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Ian Cottrell <[email protected]>
1 parent f217c98 commit d88f798

File tree

8 files changed

+116
-13
lines changed

8 files changed

+116
-13
lines changed

internal/lsp/cmd/cmd.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -339,7 +339,7 @@ func (c *cmdClient) getFile(ctx context.Context, uri span.URI) *cmdFile {
339339
}
340340
f := c.fset.AddFile(fname, -1, len(content))
341341
f.SetLinesForContent(content)
342-
file.mapper = protocol.NewColumnMapper(uri, c.fset, f, content)
342+
file.mapper = protocol.NewColumnMapper(uri, fname, c.fset, f, content)
343343
}
344344
return file
345345
}

internal/lsp/diagnostics.go

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ package lsp
66

77
import (
88
"context"
9+
"strings"
910

1011
"golang.org/x/tools/internal/lsp/protocol"
1112
"golang.org/x/tools/internal/lsp/source"
@@ -40,8 +41,10 @@ func (s *Server) Diagnostics(ctx context.Context, view source.View, uri span.URI
4041
// Anytime we compute diagnostics, make sure to also send along any
4142
// undelivered ones (only for remaining URIs).
4243
for uri, diagnostics := range s.undelivered {
43-
s.publishDiagnostics(ctx, view, uri, diagnostics)
44-
44+
err := s.publishDiagnostics(ctx, view, uri, diagnostics)
45+
if err != nil {
46+
s.session.Logger().Errorf(ctx, "failed to deliver diagnostic for %s: %v", uri, err)
47+
}
4548
// If we fail to deliver the same diagnostics twice, just give up.
4649
delete(s.undelivered, uri)
4750
}
@@ -78,7 +81,7 @@ func toProtocolDiagnostics(ctx context.Context, v source.View, diagnostics []sou
7881
return nil, err
7982
}
8083
reports = append(reports, protocol.Diagnostic{
81-
Message: diag.Message,
84+
Message: strings.TrimSpace(diag.Message), // go list returns errors prefixed by newline
8285
Range: rng,
8386
Severity: severity,
8487
Source: diag.Source,

internal/lsp/link.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ package lsp
66

77
import (
88
"context"
9+
"fmt"
910
"strconv"
1011

1112
"golang.org/x/tools/internal/lsp/protocol"
@@ -21,6 +22,10 @@ func (s *Server) documentLink(ctx context.Context, params *protocol.DocumentLink
2122
}
2223
// find the import block
2324
ast := f.GetAST(ctx)
25+
if ast == nil {
26+
return nil, fmt.Errorf("no AST for %v", uri)
27+
}
28+
2429
var result []protocol.DocumentLink
2530
for _, imp := range ast.Imports {
2631
spn, err := span.NewRange(f.GetFileSet(ctx), imp.Pos(), imp.End()).Span()

internal/lsp/lsp_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -594,7 +594,7 @@ func (r *runner) mapper(uri span.URI) (*protocol.ColumnMapper, error) {
594594
if err != nil {
595595
return nil, err
596596
}
597-
return protocol.NewColumnMapper(uri, fset, f, content), nil
597+
return protocol.NewColumnMapper(uri, filename, fset, f, content), nil
598598
}
599599

600600
func TestBytesOffset(t *testing.T) {
@@ -624,7 +624,7 @@ func TestBytesOffset(t *testing.T) {
624624
fset := token.NewFileSet()
625625
f := fset.AddFile(fname, -1, len(test.text))
626626
f.SetLinesForContent([]byte(test.text))
627-
mapper := protocol.NewColumnMapper(span.FileURI(fname), fset, f, []byte(test.text))
627+
mapper := protocol.NewColumnMapper(span.FileURI(fname), fname, fset, f, []byte(test.text))
628628
got, err := mapper.Point(test.pos)
629629
if err != nil && test.want != -1 {
630630
t.Errorf("unexpected error: %v", err)

internal/lsp/protocol/span.go

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,17 @@ func NewURI(uri span.URI) string {
2323
return string(uri)
2424
}
2525

26-
func NewColumnMapper(uri span.URI, fset *token.FileSet, f *token.File, content []byte) *ColumnMapper {
26+
func NewColumnMapper(uri span.URI, fn string, fset *token.FileSet, f *token.File, content []byte) *ColumnMapper {
27+
var converter *span.TokenConverter
28+
if f == nil {
29+
converter = span.NewContentConverter(fn, content)
30+
} else {
31+
converter = span.NewTokenConverter(fset, f)
32+
}
33+
2734
return &ColumnMapper{
2835
URI: uri,
29-
Converter: span.NewTokenConverter(fset, f),
36+
Converter: converter,
3037
Content: content,
3138
}
3239
}

internal/lsp/source/diagnostics.go

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"bytes"
99
"context"
1010
"fmt"
11+
"strings"
1112

1213
"golang.org/x/tools/go/analysis"
1314
"golang.org/x/tools/go/analysis/passes/asmdecl"
@@ -72,6 +73,12 @@ func Diagnostics(ctx context.Context, v View, uri span.URI) (map[span.URI][]Diag
7273
}
7374
reports[uri] = []Diagnostic{}
7475
}
76+
77+
// Prepare reports for package errors
78+
for _, pkgErr := range pkg.GetErrors() {
79+
reports[packageErrorSpan(pkgErr).URI()] = []Diagnostic{}
80+
}
81+
7582
// Run diagnostics for the package that this URI belongs to.
7683
if !diagnostics(ctx, v, pkg, reports) {
7784
// If we don't have any list, parse, or type errors, run analyses.
@@ -117,7 +124,7 @@ func diagnostics(ctx context.Context, v View, pkg Package, reports map[span.URI]
117124
diags = listErrors
118125
}
119126
for _, diag := range diags {
120-
spn := span.Parse(diag.Pos)
127+
spn := packageErrorSpan(diag)
121128
if spn.IsPoint() && diag.Kind == packages.TypeError {
122129
spn = pointToSpan(ctx, v, spn)
123130
}
@@ -161,6 +168,29 @@ func analyses(ctx context.Context, v View, pkg Package, reports map[span.URI][]D
161168
return nil
162169
}
163170

171+
// parseDiagnosticMessage attempts to parse a standard error message by stripping off the trailing error message.
172+
// Works only on errors where the message is prefixed by ": ".
173+
// e.g.:
174+
// attributes.go:13:1: expected 'package', found 'type'
175+
func parseDiagnosticMessage(input string) span.Span {
176+
input = strings.TrimSpace(input)
177+
178+
msgIndex := strings.Index(input, ": ")
179+
if msgIndex < 0 {
180+
return span.Parse(input)
181+
}
182+
183+
return span.Parse(input[:msgIndex])
184+
}
185+
186+
func packageErrorSpan(pkgErr packages.Error) span.Span {
187+
if pkgErr.Pos == "" {
188+
return parseDiagnosticMessage(pkgErr.Msg)
189+
}
190+
191+
return span.Parse(pkgErr.Pos)
192+
}
193+
164194
func pointToSpan(ctx context.Context, v View, spn span.Span) span.Span {
165195
// Don't set a range if it's anything other than a type error.
166196
f, err := v.GetFile(ctx, spn.URI())
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
// Copyright 2019 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
package source
6+
7+
import (
8+
"strings"
9+
"testing"
10+
)
11+
12+
func TestParseErrorMessage(t *testing.T) {
13+
tests := []struct {
14+
name string
15+
in string
16+
expectedFileName string
17+
expectedLine int
18+
expectedColumn int
19+
}{
20+
{
21+
name: "from go list output",
22+
in: "\nattributes.go:13:1: expected 'package', found 'type'",
23+
expectedFileName: "attributes.go",
24+
expectedLine: 13,
25+
expectedColumn: 1,
26+
},
27+
}
28+
29+
for _, tt := range tests {
30+
t.Run(tt.name, func(t *testing.T) {
31+
spn := parseDiagnosticMessage(tt.in)
32+
fn, err := spn.URI().Filename()
33+
if err != nil {
34+
t.Fatalf("unexpected error: %v", err)
35+
}
36+
37+
if !strings.HasSuffix(fn, tt.expectedFileName) {
38+
t.Errorf("expected filename with suffix %v but got %v", tt.expectedFileName, fn)
39+
}
40+
41+
if !spn.HasPosition() {
42+
t.Fatalf("expected span to have position")
43+
}
44+
45+
pos := spn.Start()
46+
if pos.Line() != tt.expectedLine {
47+
t.Errorf("expected line %v but got %v", tt.expectedLine, pos.Line())
48+
}
49+
50+
if pos.Column() != tt.expectedColumn {
51+
t.Errorf("expected line %v but got %v", tt.expectedLine, pos.Line())
52+
}
53+
})
54+
}
55+
}

internal/lsp/util.go

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,11 +51,14 @@ func getSourceFile(ctx context.Context, v source.View, uri span.URI) (source.Fil
5151
if err != nil {
5252
return nil, nil, err
5353
}
54-
tok := f.GetToken(ctx)
55-
if tok == nil {
56-
return nil, nil, fmt.Errorf("no file information for %v", f.URI())
54+
55+
fname, err := f.URI().Filename()
56+
if err != nil {
57+
return nil, nil, err
5758
}
58-
m := protocol.NewColumnMapper(f.URI(), f.GetFileSet(ctx), tok, f.GetContent(ctx))
59+
60+
m := protocol.NewColumnMapper(f.URI(), fname, f.GetFileSet(ctx), f.GetToken(ctx), f.GetContent(ctx))
61+
5962
return f, m, nil
6063
}
6164

0 commit comments

Comments
 (0)