Skip to content

Commit 7956c30

Browse files
stamblerreclintjedwards
authored andcommitted
internal/lsp: distinguish parse errors from actual errors
Parse errors need to be treated separately from actual errors when parsing a file. Parse errors are treated more like values, whereas actual errors should not be propagated to the user. This enables us to delete some of the special handling for context.Canceled errors. Change-Id: I93a02f22b3f54beccbd6bcf26f04bb8da0202c25 Reviewed-on: https://go-review.googlesource.com/c/tools/+/195997 Run-TryBot: Rebecca Stambler <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Ian Cottrell <[email protected]>
1 parent 6d17007 commit 7956c30

20 files changed

+141
-169
lines changed

internal/lsp/cache/builtin.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,8 @@ func (view *view) buildBuiltinPackage(ctx context.Context) error {
4343
fh := view.session.GetFile(span.FileURI(filename))
4444
ph := view.session.cache.ParseGoHandle(fh, source.ParseFull)
4545
view.builtin.files = append(view.builtin.files, ph)
46-
file, _, err := ph.Parse(ctx)
47-
if file == nil {
46+
file, _, _, err := ph.Parse(ctx)
47+
if err != nil {
4848
return err
4949
}
5050
files[filename] = file

internal/lsp/cache/check.go

+4-7
Original file line numberDiff line numberDiff line change
@@ -258,15 +258,12 @@ func (imp *importer) typeCheck(ctx context.Context, cph *checkPackageHandle, m *
258258
go func(i int, ph source.ParseGoHandle) {
259259
defer wg.Done()
260260

261-
files[i], _, parseErrors[i] = ph.Parse(ctx)
261+
files[i], _, parseErrors[i], _ = ph.Parse(ctx)
262262
}(i, ph)
263263
}
264264
wg.Wait()
265265

266266
for _, err := range parseErrors {
267-
if err == context.Canceled {
268-
return nil, errors.Errorf("parsing files for %s: %v", m.pkgPath, err)
269-
}
270267
if err != nil {
271268
imp.view.session.cache.appendPkgError(pkg, err)
272269
}
@@ -350,9 +347,9 @@ func (imp *importer) cachePerFile(ctx context.Context, gof *goFile, ph source.Pa
350347
}
351348
gof.cphs[cph.m.id] = cph
352349

353-
file, _, err := ph.Parse(ctx)
354-
if file == nil {
355-
return errors.Errorf("no AST for %s: %v", ph.File().Identity().URI, err)
350+
file, _, _, err := ph.Parse(ctx)
351+
if err != nil {
352+
return err
356353
}
357354
gof.imports = file.Imports
358355
return nil

internal/lsp/cache/load.go

+23-19
Original file line numberDiff line numberDiff line change
@@ -88,25 +88,33 @@ func (view *view) load(ctx context.Context, f *goFile, fh source.FileHandle) ([]
8888

8989
// checkMetadata determines if we should run go/packages.Load for this file.
9090
// If yes, update the metadata for the file and its package.
91-
func (v *view) checkMetadata(ctx context.Context, f *goFile, fh source.FileHandle) ([]*metadata, error) {
91+
func (v *view) checkMetadata(ctx context.Context, f *goFile, fh source.FileHandle) (metadata []*metadata, err error) {
9292
// Check if we need to re-run go/packages before loading the package.
93-
f.mu.Lock()
94-
runGopackages := v.shouldRunGopackages(ctx, f, fh)
95-
metadata := f.metadata()
96-
f.mu.Unlock()
93+
var runGopackages bool
94+
func() {
95+
f.mu.Lock()
96+
defer f.mu.Unlock()
97+
98+
runGopackages, err = v.shouldRunGopackages(ctx, f, fh)
99+
metadata = f.metadata()
100+
}()
101+
if err != nil {
102+
return nil, err
103+
}
97104

98105
// The package metadata is correct as-is, so just return it.
99106
if !runGopackages {
100107
return metadata, nil
101108
}
102109

103-
// Check if the context has been canceled before calling packages.Load.
110+
// Don't bother running go/packages if the context has been canceled.
104111
if ctx.Err() != nil {
105112
return nil, ctx.Err()
106113
}
107114

108115
ctx, done := trace.StartSpan(ctx, "packages.Load", telemetry.File.Of(f.filename()))
109116
defer done()
117+
110118
pkgs, err := packages.Load(v.Config(ctx), fmt.Sprintf("file=%s", f.filename()))
111119
if len(pkgs) == 0 {
112120
if err == nil {
@@ -185,40 +193,36 @@ func sameSet(x, y map[packagePath]struct{}) bool {
185193
// shouldRunGopackages reparses a file's package and import declarations to
186194
// determine if they have changed.
187195
// It assumes that the caller holds the lock on the f.mu lock.
188-
func (v *view) shouldRunGopackages(ctx context.Context, f *goFile, fh source.FileHandle) (result bool) {
196+
func (v *view) shouldRunGopackages(ctx context.Context, f *goFile, fh source.FileHandle) (result bool, err error) {
189197
if len(f.meta) == 0 || len(f.missingImports) > 0 {
190-
return true
198+
return true, nil
191199
}
192200
// Get file content in case we don't already have it.
193-
parsed, _, err := v.session.cache.ParseGoHandle(fh, source.ParseHeader).Parse(ctx)
194-
if err == context.Canceled {
195-
log.Error(ctx, "parsing file header", err, tag.Of("file", f.URI()))
196-
return false
197-
}
198-
if parsed == nil {
199-
return true
201+
parsed, _, _, err := v.session.cache.ParseGoHandle(fh, source.ParseHeader).Parse(ctx)
202+
if err != nil {
203+
return false, err
200204
}
201205
// Check if the package's name has changed, by checking if this is a filename
202206
// we already know about, and if so, check if its package name has changed.
203207
for _, m := range f.meta {
204208
for _, uri := range m.files {
205209
if span.CompareURI(uri, f.URI()) == 0 {
206210
if m.name != parsed.Name.Name {
207-
return true
211+
return true, nil
208212
}
209213
}
210214
}
211215
}
212216
// If the package's imports have changed, re-run `go list`.
213217
if len(f.imports) != len(parsed.Imports) {
214-
return true
218+
return true, nil
215219
}
216220
for i, importSpec := range f.imports {
217221
if importSpec.Path.Value != parsed.Imports[i].Path.Value {
218-
return true
222+
return true, nil
219223
}
220224
}
221-
return false
225+
return false, nil
222226
}
223227

224228
type importGraph struct {

internal/lsp/cache/parse.go

+37-38
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,10 @@ type parseGoHandle struct {
4141
type parseGoData struct {
4242
memoize.NoCopy
4343

44-
ast *ast.File
45-
mapper *protocol.ColumnMapper
46-
err error
44+
ast *ast.File
45+
parseError error // errors associated with parsing the file
46+
mapper *protocol.ColumnMapper
47+
err error
4748
}
4849

4950
func (c *cache) ParseGoHandle(fh source.FileHandle, mode source.ParseMode) source.ParseGoHandle {
@@ -53,18 +54,7 @@ func (c *cache) ParseGoHandle(fh source.FileHandle, mode source.ParseMode) sourc
5354
}
5455
h := c.store.Bind(key, func(ctx context.Context) interface{} {
5556
data := &parseGoData{}
56-
data.ast, data.err = parseGo(ctx, c, fh, mode)
57-
tok := c.FileSet().File(data.ast.Pos())
58-
if tok == nil {
59-
return data
60-
}
61-
uri := fh.Identity().URI
62-
content, _, err := fh.Read(ctx)
63-
if err != nil {
64-
data.err = err
65-
return data
66-
}
67-
data.mapper = newColumnMapper(uri, c.FileSet(), tok, content)
57+
data.ast, data.mapper, data.parseError, data.err = parseGo(ctx, c, fh, mode)
6858
return data
6959
})
7060
return &parseGoHandle{
@@ -73,13 +63,6 @@ func (c *cache) ParseGoHandle(fh source.FileHandle, mode source.ParseMode) sourc
7363
mode: mode,
7464
}
7565
}
76-
func newColumnMapper(uri span.URI, fset *token.FileSet, tok *token.File, content []byte) *protocol.ColumnMapper {
77-
return &protocol.ColumnMapper{
78-
URI: uri,
79-
Converter: span.NewTokenConverter(fset, tok),
80-
Content: content,
81-
}
82-
}
8366

8467
func (h *parseGoHandle) File() source.FileHandle {
8568
return h.file
@@ -89,22 +72,22 @@ func (h *parseGoHandle) Mode() source.ParseMode {
8972
return h.mode
9073
}
9174

92-
func (h *parseGoHandle) Parse(ctx context.Context) (*ast.File, *protocol.ColumnMapper, error) {
75+
func (h *parseGoHandle) Parse(ctx context.Context) (*ast.File, *protocol.ColumnMapper, error, error) {
9376
v := h.handle.Get(ctx)
9477
if v == nil {
95-
return nil, nil, ctx.Err()
78+
return nil, nil, nil, ctx.Err()
9679
}
9780
data := v.(*parseGoData)
98-
return data.ast, data.mapper, data.err
81+
return data.ast, data.mapper, data.parseError, data.err
9982
}
10083

101-
func (h *parseGoHandle) Cached(ctx context.Context) (*ast.File, *protocol.ColumnMapper, error) {
84+
func (h *parseGoHandle) Cached(ctx context.Context) (*ast.File, *protocol.ColumnMapper, error, error) {
10285
v := h.handle.Cached()
10386
if v == nil {
104-
return nil, nil, errors.Errorf("no cached value for %s", h.file.Identity().URI)
87+
return nil, nil, nil, errors.Errorf("no cached AST for %s", h.file.Identity().URI)
10588
}
10689
data := v.(*parseGoData)
107-
return data.ast, data.mapper, data.err
90+
return data.ast, data.mapper, data.parseError, data.err
10891
}
10992

11093
func hashParseKey(ph source.ParseGoHandle) string {
@@ -122,35 +105,51 @@ func hashParseKeys(phs []source.ParseGoHandle) string {
122105
return hashContents(b.Bytes())
123106
}
124107

125-
func parseGo(ctx context.Context, c *cache, fh source.FileHandle, mode source.ParseMode) (*ast.File, error) {
108+
func parseGo(ctx context.Context, c *cache, fh source.FileHandle, mode source.ParseMode) (file *ast.File, mapper *protocol.ColumnMapper, parseError error, err error) {
126109
ctx, done := trace.StartSpan(ctx, "cache.parseGo", telemetry.File.Of(fh.Identity().URI.Filename()))
127110
defer done()
128111

129112
buf, _, err := fh.Read(ctx)
130113
if err != nil {
131-
return nil, err
114+
return nil, nil, nil, err
132115
}
133116
parseLimit <- struct{}{}
134117
defer func() { <-parseLimit }()
135118
parserMode := parser.AllErrors | parser.ParseComments
136119
if mode == source.ParseHeader {
137120
parserMode = parser.ImportsOnly | parser.ParseComments
138121
}
139-
ast, err := parser.ParseFile(c.fset, fh.Identity().URI.Filename(), buf, parserMode)
140-
if ast != nil {
122+
file, parseError = parser.ParseFile(c.fset, fh.Identity().URI.Filename(), buf, parserMode)
123+
if file != nil {
141124
if mode == source.ParseExported {
142-
trimAST(ast)
125+
trimAST(file)
143126
}
144127
// Fix any badly parsed parts of the AST.
145-
tok := c.fset.File(ast.Pos())
146-
if err := fix(ctx, ast, tok, buf); err != nil {
128+
tok := c.fset.File(file.Pos())
129+
if err := fix(ctx, file, tok, buf); err != nil {
147130
log.Error(ctx, "failed to fix AST", err)
148131
}
149132
}
150-
if ast == nil {
151-
return nil, err
133+
// If the file is nil only due to parse errors,
134+
// the parse errors are the actual errors.
135+
if file == nil {
136+
return nil, nil, parseError, parseError
137+
}
138+
tok := c.FileSet().File(file.Pos())
139+
if tok == nil {
140+
return nil, nil, parseError, err
141+
}
142+
uri := fh.Identity().URI
143+
content, _, err := fh.Read(ctx)
144+
if err != nil {
145+
return nil, nil, parseError, err
146+
}
147+
m := &protocol.ColumnMapper{
148+
URI: uri,
149+
Converter: span.NewTokenConverter(c.FileSet(), tok),
150+
Content: content,
152151
}
153-
return ast, err
152+
return file, m, parseError, nil
154153
}
155154

156155
// trimAST clears any part of the AST not relevant to type checking

internal/lsp/cache/pkg.go

+11-2
Original file line numberDiff line numberDiff line change
@@ -151,11 +151,20 @@ func (pkg *pkg) Files() []source.ParseGoHandle {
151151
return pkg.files
152152
}
153153

154+
func (pkg *pkg) File(uri span.URI) (source.ParseGoHandle, error) {
155+
for _, ph := range pkg.Files() {
156+
if ph.File().Identity().URI == uri {
157+
return ph, nil
158+
}
159+
}
160+
return nil, errors.Errorf("no ParseGoHandle for %s", uri)
161+
}
162+
154163
func (pkg *pkg) GetSyntax(ctx context.Context) []*ast.File {
155164
var syntax []*ast.File
156165
for _, ph := range pkg.files {
157-
file, _, _ := ph.Cached(ctx)
158-
if file != nil {
166+
file, _, _, err := ph.Cached(ctx)
167+
if err == nil {
159168
syntax = append(syntax, file)
160169
}
161170
}

internal/lsp/diagnostics.go

+4-1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ func (s *Server) diagnostics(view source.View, uri span.URI) error {
2323
defer done()
2424

2525
ctx = telemetry.File.With(ctx, uri)
26+
2627
f, err := view.GetFile(ctx, uri)
2728
if err != nil {
2829
return err
@@ -45,8 +46,9 @@ func (s *Server) diagnostics(view source.View, uri span.URI) error {
4546
if s.undelivered == nil {
4647
s.undelivered = make(map[span.URI][]source.Diagnostic)
4748
}
48-
log.Error(ctx, "failed to deliver diagnostic (will retry)", err, telemetry.File)
4949
s.undelivered[uri] = diagnostics
50+
51+
log.Error(ctx, "failed to deliver diagnostic (will retry)", err, telemetry.File)
5052
continue
5153
}
5254
// In case we had old, undelivered diagnostics.
@@ -58,6 +60,7 @@ func (s *Server) diagnostics(view source.View, uri span.URI) error {
5860
if err := s.publishDiagnostics(ctx, uri, diagnostics); err != nil {
5961
log.Error(ctx, "failed to deliver diagnostic for (will not retry)", err, telemetry.File)
6062
}
63+
6164
// If we fail to deliver the same diagnostics twice, just give up.
6265
delete(s.undelivered, uri)
6366
}

internal/lsp/link.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,8 @@ func (s *Server) documentLink(ctx context.Context, params *protocol.DocumentLink
2828
return nil, err
2929
}
3030
fh := f.Handle(ctx)
31-
file, m, err := view.Session().Cache().ParseGoHandle(fh, source.ParseFull).Parse(ctx)
32-
if file == nil {
31+
file, m, _, err := view.Session().Cache().ParseGoHandle(fh, source.ParseFull).Parse(ctx)
32+
if err != nil {
3333
return nil, err
3434
}
3535
var links []protocol.DocumentLink

internal/lsp/source/completion.go

+5-7
Original file line numberDiff line numberDiff line change
@@ -376,14 +376,12 @@ func Completion(ctx context.Context, view View, f GoFile, pos protocol.Position,
376376
if err != nil {
377377
return nil, nil, err
378378
}
379-
var ph ParseGoHandle
380-
for _, h := range pkg.Files() {
381-
if h.File().Identity().URI == f.URI() {
382-
ph = h
383-
}
379+
ph, err := pkg.File(f.URI())
380+
if err != nil {
381+
return nil, nil, err
384382
}
385-
file, m, err := ph.Cached(ctx)
386-
if file == nil {
383+
file, m, _, err := ph.Cached(ctx)
384+
if err != nil {
387385
return nil, nil, err
388386
}
389387
spn, err := m.PointSpan(pos)

internal/lsp/source/completion_format.go

+2-3
Original file line numberDiff line numberDiff line change
@@ -123,14 +123,13 @@ func (c *completer) item(cand candidate) (CompletionItem, error) {
123123
if !pos.IsValid() {
124124
return item, nil
125125
}
126-
127126
uri := span.FileURI(pos.Filename)
128127
ph, pkg, err := c.pkg.FindFile(c.ctx, uri)
129128
if err != nil {
130129
return CompletionItem{}, err
131130
}
132-
file, _, err := ph.Cached(c.ctx)
133-
if file == nil {
131+
file, _, _, err := ph.Cached(c.ctx)
132+
if err != nil {
134133
return CompletionItem{}, err
135134
}
136135
if !(file.Pos() <= obj.Pos() && obj.Pos() <= file.End()) {

0 commit comments

Comments
 (0)