1
0
mirror of https://github.com/golang/go synced 2024-10-01 09:28:37 -06:00
go/internal/lsp/cache/parse_mod.go
Rohan Challa 30cae5f2fb internal/lsp: surface diagnostics for invalid go.mod files
This change will surface errors that come from the mod package. It will handle incorrect usages, invalid directives, and other errors that occur when parsing go.mod files.

Updates golang/go#31999

Change-Id: Icd817c02a4b656b2a71914ee60be4dbe2bea062d
Reviewed-on: https://go-review.googlesource.com/c/tools/+/213779
Run-TryBot: Rohan Challa <rohan@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
2020-01-13 15:48:38 +00:00

96 lines
2.5 KiB
Go

// Copyright 2019 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package cache
import (
"context"
"regexp"
"strconv"
"strings"
"golang.org/x/mod/modfile"
"golang.org/x/tools/internal/lsp/protocol"
"golang.org/x/tools/internal/lsp/source"
"golang.org/x/tools/internal/lsp/telemetry"
"golang.org/x/tools/internal/memoize"
"golang.org/x/tools/internal/telemetry/log"
"golang.org/x/tools/internal/telemetry/trace"
errors "golang.org/x/xerrors"
)
type parseModHandle struct {
handle *memoize.Handle
file source.FileHandle
}
type parseModData struct {
memoize.NoCopy
modfile *modfile.File
err error
}
func (c *cache) ParseModHandle(fh source.FileHandle) source.ParseModHandle {
h := c.store.Bind(fh.Identity(), func(ctx context.Context) interface{} {
data := &parseModData{}
data.modfile, data.err = parseMod(ctx, fh)
return data
})
return &parseModHandle{
handle: h,
file: fh,
}
}
func parseMod(ctx context.Context, fh source.FileHandle) (*modfile.File, error) {
ctx, done := trace.StartSpan(ctx, "cache.parseMod", telemetry.File.Of(fh.Identity().URI.Filename()))
defer done()
buf, _, err := fh.Read(ctx)
if err != nil {
return nil, err
}
f, err := modfile.Parse(fh.Identity().URI.Filename(), buf, nil)
if err != nil {
// TODO(golang/go#36486): This can be removed when modfile.Parse returns structured errors.
re := regexp.MustCompile(`.*:([\d]+): (.+)`)
matches := re.FindStringSubmatch(strings.TrimSpace(err.Error()))
if len(matches) < 3 {
log.Error(ctx, "could not parse golang/x/mod error message", err)
return nil, err
}
line, e := strconv.Atoi(matches[1])
if e != nil {
return nil, err
}
contents := strings.Split(string(buf), "\n")[line-1]
return nil, &source.Error{
Message: matches[2],
Range: protocol.Range{
Start: protocol.Position{Line: float64(line - 1), Character: float64(0)},
End: protocol.Position{Line: float64(line - 1), Character: float64(len(contents))},
},
}
}
return f, nil
}
func (pgh *parseModHandle) String() string {
return pgh.File().Identity().URI.Filename()
}
func (pgh *parseModHandle) File() source.FileHandle {
return pgh.file
}
func (pgh *parseModHandle) Parse(ctx context.Context) (*modfile.File, error) {
v := pgh.handle.Get(ctx)
if v == nil {
return nil, errors.Errorf("no parsed file for %s", pgh.File().Identity().URI)
}
data := v.(*parseModData)
return data.modfile, data.err
}