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>
diff --git a/internal/lsp/cache/mod.go b/internal/lsp/cache/mod.go
index 95a9132..0504799 100644
--- a/internal/lsp/cache/mod.go
+++ b/internal/lsp/cache/mod.go
@@ -69,25 +69,24 @@
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}
diff --git a/internal/lsp/cache/mod_tidy.go b/internal/lsp/cache/mod_tidy.go
index 76479ee..4d94090 100644
--- a/internal/lsp/cache/mod_tidy.go
+++ b/internal/lsp/cache/mod_tidy.go
@@ -53,13 +53,17 @@
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 @@
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 @@
return &modTidyData{
tidied: &source.TidiedModule{
Errors: errors,
- Parsed: pm,
TidiedContent: tempContents,
},
}
@@ -187,7 +173,6 @@
return nil, false
}
return &source.TidiedModule{
- Parsed: pmf,
Errors: []source.Error{{
URI: fh.URI(),
Range: rng,
diff --git a/internal/lsp/mod/diagnostics.go b/internal/lsp/mod/diagnostics.go
index 41a462c..20fd606 100644
--- a/internal/lsp/mod/diagnostics.go
+++ b/internal/lsp/mod/diagnostics.go
@@ -52,7 +52,14 @@
}
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
diff --git a/internal/lsp/source/view.go b/internal/lsp/source/view.go
index 5a9ee40..cc7b207 100644
--- a/internal/lsp/source/view.go
+++ b/internal/lsp/source/view.go
@@ -111,7 +111,7 @@
// 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 @@
// 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 @@
// 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.