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>
This commit is contained in:
Rebecca Stambler 2019-04-22 17:05:43 -04:00
parent 3ff669b183
commit c6b416e8a4
2 changed files with 34 additions and 26 deletions

View File

@ -18,17 +18,22 @@ import (
"sync" "sync"
"time" "time"
"golang.org/x/sync/errgroup"
"golang.org/x/tools/go/analysis" "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. // Build nodes for initial packages.
var roots []*Action var roots []*Action
for _, a := range analyzers { for _, a := range analyzers {
for _, pkg := range pkgs { for _, pkg := range pkgs {
root, err := pkg.GetActionGraph(ctx, a) root, err := pkg.GetActionGraph(ctx, a)
if err != nil { if err != nil {
continue return nil, err
} }
root.isroot = true root.isroot = true
roots = append(roots, root) roots = append(roots, root)
@ -36,9 +41,9 @@ func analyze(ctx context.Context, v View, pkgs []Package, analyzers []*analysis.
} }
// Execute the graph in parallel. // 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 // An action represents one unit of analysis work: the application of
@ -75,28 +80,27 @@ func (act *Action) String() string {
return fmt.Sprintf("%s@%s", act.Analyzer, act.Pkg) return fmt.Sprintf("%s@%s", act.Analyzer, act.Pkg)
} }
func execAll(fset *token.FileSet, actions []*Action) { func execAll(ctx context.Context, fset *token.FileSet, actions []*Action) error {
var wg sync.WaitGroup g, ctx := errgroup.WithContext(ctx)
for _, act := range actions { for _, act := range actions {
wg.Add(1) act := act
work := func(act *Action) { g.Go(func() error {
act.exec(fset) act.exec(ctx, fset)
wg.Done() return nil
} })
go work(act)
} }
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.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. // Analyze dependencies.
execAll(fset, act.Deps) execAll(ctx, fset, act.Deps)
// Report an error if any dependency failed. // Report an error if any dependency failed.
var failed []string var failed []string

View File

@ -101,13 +101,12 @@ func Diagnostics(ctx context.Context, v View, uri span.URI) (map[span.URI][]Diag
return reports, nil return reports, nil
} }
// Type checking and parsing succeeded. Run analyses. // 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) r := span.NewRange(v.FileSet(), diag.Pos, 0)
s, err := r.Span() s, err := r.Span()
if err != nil { if err != nil {
//TODO: we could not process the diag.Pos, and thus have no valid span // The diagnostic has an invalid position, so we don't have a valid span.
//we don't have anywhere to put this error though return err
v.Logger().Errorf(ctx, "%v", err)
} }
category := a.Name category := a.Name
if diag.Category != "" { if diag.Category != "" {
@ -119,6 +118,7 @@ func Diagnostics(ctx context.Context, v View, uri span.URI) (map[span.URI][]Diag
Message: diag.Message, Message: diag.Message,
Severity: SeverityWarning, Severity: SeverityWarning,
}) })
return nil
}) })
return reports, nil return reports, nil
@ -168,8 +168,8 @@ func singleDiagnostic(uri span.URI, format string, a ...interface{}) map[span.UR
} }
} }
func runAnalyses(ctx context.Context, v View, pkg Package, report func(a *analysis.Analyzer, diag analysis.Diagnostic)) error { func runAnalyses(ctx context.Context, v View, pkg Package, report func(a *analysis.Analyzer, diag analysis.Diagnostic) error) error {
// the traditional vet suite: // The traditional vet suite:
analyzers := []*analysis.Analyzer{ analyzers := []*analysis.Analyzer{
asmdecl.Analyzer, asmdecl.Analyzer,
assign.Analyzer, assign.Analyzer,
@ -195,7 +195,10 @@ func runAnalyses(ctx context.Context, v View, pkg Package, report func(a *analys
unusedresult.Analyzer, 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. // Report diagnostics and errors from root analyzers.
for _, r := range roots { for _, r := range roots {
@ -205,9 +208,10 @@ func runAnalyses(ctx context.Context, v View, pkg Package, report func(a *analys
// which isn't super useful... // which isn't super useful...
return r.err return r.err
} }
report(r.Analyzer, diag) if err := report(r.Analyzer, diag); err != nil {
return err
}
} }
} }
return nil return nil
} }