internal/lsp: add more error handling to analysis
Updates golang/go#30786
Change-Id: Icf054b9bcd62b36e3aa55288946a9f0d1c845300
Reviewed-on: https://go-review.googlesource.com/c/tools/+/172972
Run-TryBot: Rebecca Stambler <rstambler@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ian Cottrell <iancottrell@google.com>
diff --git a/internal/lsp/source/analysis.go b/internal/lsp/source/analysis.go
index 6cf8df4..b72095e 100644
--- a/internal/lsp/source/analysis.go
+++ b/internal/lsp/source/analysis.go
@@ -18,17 +18,22 @@
"sync"
"time"
+ "golang.org/x/sync/errgroup"
"golang.org/x/tools/go/analysis"
)
-func analyze(ctx context.Context, v View, pkgs []Package, analyzers []*analysis.Analyzer) []*Action {
+func analyze(ctx context.Context, v View, pkgs []Package, analyzers []*analysis.Analyzer) ([]*Action, error) {
+ if ctx.Err() != nil {
+ return nil, ctx.Err()
+ }
+
// Build nodes for initial packages.
var roots []*Action
for _, a := range analyzers {
for _, pkg := range pkgs {
root, err := pkg.GetActionGraph(ctx, a)
if err != nil {
- continue
+ return nil, err
}
root.isroot = true
roots = append(roots, root)
@@ -36,9 +41,9 @@
}
// Execute the graph in parallel.
- execAll(v.FileSet(), roots)
+ execAll(ctx, v.FileSet(), roots)
- return roots
+ return roots, nil
}
// An action represents one unit of analysis work: the application of
@@ -75,28 +80,27 @@
return fmt.Sprintf("%s@%s", act.Analyzer, act.Pkg)
}
-func execAll(fset *token.FileSet, actions []*Action) {
- var wg sync.WaitGroup
+func execAll(ctx context.Context, fset *token.FileSet, actions []*Action) error {
+ g, ctx := errgroup.WithContext(ctx)
for _, act := range actions {
- wg.Add(1)
- work := func(act *Action) {
- act.exec(fset)
- wg.Done()
- }
- go work(act)
+ act := act
+ g.Go(func() error {
+ act.exec(ctx, fset)
+ return nil
+ })
}
- wg.Wait()
+ return g.Wait()
}
-func (act *Action) exec(fset *token.FileSet) {
+func (act *Action) exec(ctx context.Context, fset *token.FileSet) {
act.once.Do(func() {
- act.execOnce(fset)
+ act.execOnce(ctx, fset)
})
}
-func (act *Action) execOnce(fset *token.FileSet) {
+func (act *Action) execOnce(ctx context.Context, fset *token.FileSet) {
// Analyze dependencies.
- execAll(fset, act.Deps)
+ execAll(ctx, fset, act.Deps)
// Report an error if any dependency failed.
var failed []string
diff --git a/internal/lsp/source/diagnostics.go b/internal/lsp/source/diagnostics.go
index fb600f7..b052be5 100644
--- a/internal/lsp/source/diagnostics.go
+++ b/internal/lsp/source/diagnostics.go
@@ -101,13 +101,12 @@
return reports, nil
}
// Type checking and parsing succeeded. Run analyses.
- runAnalyses(ctx, v, pkg, func(a *analysis.Analyzer, diag analysis.Diagnostic) {
+ runAnalyses(ctx, v, pkg, func(a *analysis.Analyzer, diag analysis.Diagnostic) error {
r := span.NewRange(v.FileSet(), diag.Pos, 0)
s, err := r.Span()
if err != nil {
- //TODO: we could not process the diag.Pos, and thus have no valid span
- //we don't have anywhere to put this error though
- v.Logger().Errorf(ctx, "%v", err)
+ // The diagnostic has an invalid position, so we don't have a valid span.
+ return err
}
category := a.Name
if diag.Category != "" {
@@ -119,6 +118,7 @@
Message: diag.Message,
Severity: SeverityWarning,
})
+ return nil
})
return reports, nil
@@ -168,8 +168,8 @@
}
}
-func runAnalyses(ctx context.Context, v View, pkg Package, report func(a *analysis.Analyzer, diag analysis.Diagnostic)) error {
- // the traditional vet suite:
+func runAnalyses(ctx context.Context, v View, pkg Package, report func(a *analysis.Analyzer, diag analysis.Diagnostic) error) error {
+ // The traditional vet suite:
analyzers := []*analysis.Analyzer{
asmdecl.Analyzer,
assign.Analyzer,
@@ -195,7 +195,10 @@
unusedresult.Analyzer,
}
- roots := analyze(ctx, v, []Package{pkg}, analyzers)
+ roots, err := analyze(ctx, v, []Package{pkg}, analyzers)
+ if err != nil {
+ return err
+ }
// Report diagnostics and errors from root analyzers.
for _, r := range roots {
@@ -205,9 +208,10 @@
// which isn't super useful...
return r.err
}
- report(r.Analyzer, diag)
+ if err := report(r.Analyzer, diag); err != nil {
+ return err
+ }
}
}
-
return nil
}