internal/lsp: pass a parsed module to go mod tidy

This allows us to show parsing errors as part of module diagnostics.

Change-Id: I558b95c145135482fdfceef8a5c68c62a6d32721
Reviewed-on: https://go-review.googlesource.com/c/tools/+/271630
Trust: Rebecca Stambler <rstambler@golang.org>
Run-TryBot: Rebecca Stambler <rstambler@golang.org>
gopls-CI: kokoro <noreply+kokoro@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Heschi Kreinick <heschi@google.com>
This commit is contained in:
Rebecca Stambler 2020-11-19 14:30:37 -05:00
Родитель c0d5e89189
Коммит a7380940e0
4 изменённых файлов: 33 добавлений и 43 удалений

31
internal/lsp/cache/mod.go поставляемый
Просмотреть файл

@ -69,25 +69,24 @@ func (s *snapshot) ParseMod(ctx context.Context, modFH source.FileHandle) (*sour
Converter: span.NewContentConverter(modFH.URI().Filename(), contents),
Content: contents,
}
data := &parseModData{
file, err := modfile.Parse(modFH.URI().Filename(), contents, nil)
// Attempt to convert the error to a standardized parse error.
var parseErrors []source.Error
if err != nil {
if parseErr, extractErr := extractModParseErrors(modFH.URI(), m, err, contents); extractErr == nil {
parseErrors = []source.Error{*parseErr}
}
}
return &parseModData{
parsed: &source.ParsedModule{
Mapper: m,
URI: modFH.URI(),
Mapper: m,
File: file,
ParseErrors: parseErrors,
},
err: err,
}
data.parsed.File, data.err = modfile.Parse(modFH.URI().Filename(), contents, nil)
if data.err != nil {
// Attempt to convert the error to a standardized parse error.
if parseErr, extractErr := extractModParseErrors(modFH.URI(), m, data.err, contents); extractErr == nil {
data.parsed.ParseErrors = []source.Error{*parseErr}
}
// If the file was still parsed, we don't want to treat this as a
// fatal error. Note: This currently cannot happen as modfile.Parse
// always returns an error when the file is nil.
if data.parsed.File != nil {
data.err = nil
}
}
return data
}, nil)
pmh := &parseModHandle{handle: h}

31
internal/lsp/cache/mod_tidy.go поставляемый
Просмотреть файл

@ -53,13 +53,17 @@ func (mth *modTidyHandle) tidy(ctx context.Context, snapshot *snapshot) (*source
return data.tidied, data.err
}
func (s *snapshot) ModTidy(ctx context.Context, fh source.FileHandle) (*source.TidiedModule, error) {
if fh.Kind() != source.Mod {
return nil, fmt.Errorf("%s is not a go.mod file", fh.URI())
func (s *snapshot) ModTidy(ctx context.Context, pm *source.ParsedModule) (*source.TidiedModule, error) {
if pm.File == nil {
return nil, fmt.Errorf("cannot tidy unparseable go.mod file: %v", pm.URI)
}
if handle := s.getModTidyHandle(fh.URI()); handle != nil {
if handle := s.getModTidyHandle(pm.URI); handle != nil {
return handle.tidy(ctx, s)
}
fh, err := s.GetFile(ctx, pm.URI)
if err != nil {
return nil, err
}
// If the file handle is an overlay, it may not be written to disk.
// The go.mod file has to be on disk for `go mod tidy` to work.
if _, ok := fh.(*overlay); ok {
@ -96,23 +100,6 @@ func (s *snapshot) ModTidy(ctx context.Context, fh source.FileHandle) (*source.T
defer done()
snapshot := arg.(*snapshot)
pm, err := snapshot.ParseMod(ctx, fh)
if err != nil || len(pm.ParseErrors) > 0 {
if err == nil {
err = fmt.Errorf("could not parse module to tidy: %v", pm.ParseErrors)
}
var errors []source.Error
if pm != nil {
errors = pm.ParseErrors
}
return &modTidyData{
tidied: &source.TidiedModule{
Parsed: pm,
Errors: errors,
},
err: err,
}
}
inv := &gocommand.Invocation{
Verb: "mod",
Args: []string{"tidy"},
@ -149,7 +136,6 @@ func (s *snapshot) ModTidy(ctx context.Context, fh source.FileHandle) (*source.T
return &modTidyData{
tidied: &source.TidiedModule{
Errors: errors,
Parsed: pm,
TidiedContent: tempContents,
},
}
@ -187,7 +173,6 @@ func (s *snapshot) parseModErrors(ctx context.Context, fh source.FileHandle, err
return nil, false
}
return &source.TidiedModule{
Parsed: pmf,
Errors: []source.Error{{
URI: fh.URI(),
Range: rng,

Просмотреть файл

@ -52,7 +52,14 @@ func Diagnostics(ctx context.Context, snapshot source.Snapshot) (map[source.Vers
}
func ErrorsForMod(ctx context.Context, snapshot source.Snapshot, fh source.FileHandle) ([]source.Error, error) {
tidied, err := snapshot.ModTidy(ctx, fh)
pm, err := snapshot.ParseMod(ctx, fh)
if err != nil {
if pm == nil || len(pm.ParseErrors) == 0 {
return nil, err
}
return pm.ParseErrors, nil
}
tidied, err := snapshot.ModTidy(ctx, pm)
if source.IsNonFatalGoModError(err) {
return nil, nil

Просмотреть файл

@ -111,7 +111,7 @@ type Snapshot interface {
// ModTidy returns the results of `go mod tidy` for the module specified by
// the given go.mod file.
ModTidy(ctx context.Context, fh FileHandle) (*TidiedModule, error)
ModTidy(ctx context.Context, pm *ParsedModule) (*TidiedModule, error)
// GoModForFile returns the URI of the go.mod file for the given URI.
GoModForFile(ctx context.Context, uri span.URI) span.URI
@ -249,6 +249,7 @@ type ParsedGoFile struct {
// A ParsedModule contains the results of parsing a go.mod file.
type ParsedModule struct {
URI span.URI
File *modfile.File
Mapper *protocol.ColumnMapper
ParseErrors []Error
@ -256,8 +257,6 @@ type ParsedModule struct {
// A TidiedModule contains the results of running `go mod tidy` on a module.
type TidiedModule struct {
// The parsed module, which is guaranteed to have parsed successfully.
Parsed *ParsedModule
// Diagnostics representing changes made by `go mod tidy`.
Errors []Error
// The bytes of the go.mod file after it was tidied.