1
0
mirror of https://github.com/golang/go synced 2024-11-18 13:04:46 -07:00

go/analysis, internal/lsp: add support for related information

This CL adds support for "related information", which allows
associating additional source positions and messages with a
diagnostic.

Change-Id: Ifc0634f68c9f3724b6508dc6331c62c819a24f78
Reviewed-on: https://go-review.googlesource.com/c/tools/+/200597
Reviewed-by: Michael Matloob <matloob@golang.org>
Reviewed-by: Rebecca Stambler <rstambler@golang.org>
This commit is contained in:
Dominik Honnef 2019-10-11 11:39:09 +02:00
parent 747b8b11d4
commit ce0314c87e
4 changed files with 91 additions and 17 deletions

View File

@ -22,6 +22,19 @@ type Diagnostic struct {
// Diagnostics should not contain SuggestedFixes that overlap.
// Experimental: This API is experimental and may change in the future.
SuggestedFixes []SuggestedFix // optional
// Experimental: This API is experimental and may change in the future.
Related []RelatedInformation // optional
}
// RelatedInformation contains information related to a diagnostic.
// For example, a diagnostic that flags duplicated declarations of a
// variable may include one RelatedInformation per existing
// declaration.
type RelatedInformation struct {
Pos token.Pos
End token.Pos
Message string
}
// A SuggestedFix is a code change associated with a Diagnostic that a user can choose

View File

@ -78,11 +78,17 @@ func run(pass *analysis.Pass) (interface{}, error) {
if ident.Name == from {
msg := fmt.Sprintf("renaming %q to %q", from, to)
pass.Report(analysis.Diagnostic{
ident.Pos(), ident.End(), "", msg,
[]analysis.SuggestedFix{
{msg, []analysis.TextEdit{
{ident.Pos(), ident.End(), []byte(to)}},
Pos: ident.Pos(),
End: ident.End(),
Message: msg,
SuggestedFixes: []analysis.SuggestedFix{{
Message: msg,
TextEdits: []analysis.TextEdit{{
Pos: ident.Pos(),
End: ident.End(),
NewText: []byte(to),
}},
}},
})
}
})

View File

@ -78,12 +78,23 @@ func (s *Server) publishDiagnostics(ctx context.Context, uri span.URI, diagnosti
func toProtocolDiagnostics(ctx context.Context, diagnostics []source.Diagnostic) []protocol.Diagnostic {
reports := []protocol.Diagnostic{}
for _, diag := range diagnostics {
related := make([]protocol.DiagnosticRelatedInformation, 0, len(diag.Related))
for _, rel := range diag.Related {
related = append(related, protocol.DiagnosticRelatedInformation{
Location: protocol.Location{
URI: protocol.NewURI(rel.URI),
Range: rel.Range,
},
Message: rel.Message,
})
}
reports = append(reports, protocol.Diagnostic{
Message: strings.TrimSpace(diag.Message), // go list returns errors prefixed by newline
Range: diag.Range,
Severity: diag.Severity,
Source: diag.Source,
Tags: diag.Tags,
Message: strings.TrimSpace(diag.Message), // go list returns errors prefixed by newline
Range: diag.Range,
Severity: diag.Severity,
Source: diag.Source,
Tags: diag.Tags,
RelatedInformation: related,
})
}
return reports

View File

@ -26,6 +26,13 @@ type Diagnostic struct {
Tags []protocol.DiagnosticTag
SuggestedFixes []SuggestedFix
Related []RelatedInformation
}
type RelatedInformation struct {
URI span.URI
Range protocol.Range
Message string
}
type DiagnosticSeverity int
@ -170,26 +177,30 @@ func analyses(ctx context.Context, snapshot Snapshot, cph CheckPackageHandle, di
return nil
}
func toDiagnostic(ctx context.Context, view View, diag *analysis.Diagnostic, category string) (Diagnostic, error) {
spn, err := span.NewRange(view.Session().Cache().FileSet(), diag.Pos, diag.End).Span()
if err != nil {
return Diagnostic{}, err
}
func packageForSpan(ctx context.Context, view View, spn span.Span) (Package, error) {
f, err := view.GetFile(ctx, spn.URI())
if err != nil {
return Diagnostic{}, err
return nil, err
}
// If the package has changed since these diagnostics were computed,
// this may be incorrect. Should the package be associated with the diagnostic?
_, cphs, err := view.CheckPackageHandles(ctx, f)
if err != nil {
return Diagnostic{}, err
return nil, err
}
cph, err := NarrowestCheckPackageHandle(cphs)
if err != nil {
return nil, err
}
return cph.Cached(ctx)
}
func toDiagnostic(ctx context.Context, view View, diag *analysis.Diagnostic, category string) (Diagnostic, error) {
spn, err := span.NewRange(view.Session().Cache().FileSet(), diag.Pos, diag.End).Span()
if err != nil {
return Diagnostic{}, err
}
pkg, err := cph.Cached(ctx)
pkg, err := packageForSpan(ctx, view, spn)
if err != nil {
return Diagnostic{}, err
}
@ -209,6 +220,12 @@ func toDiagnostic(ctx context.Context, view View, diag *analysis.Diagnostic, cat
if err != nil {
return Diagnostic{}, err
}
related, err := relatedInformation(ctx, view, diag)
if err != nil {
return Diagnostic{}, err
}
// This is a bit of a hack, but clients > 3.15 will be able to grey out unnecessary code.
// If we are deleting code as part of all of our suggested fixes, assume that this is dead code.
// TODO(golang/go/#34508): Return these codes from the diagnostics themselves.
@ -227,9 +244,36 @@ func toDiagnostic(ctx context.Context, view View, diag *analysis.Diagnostic, cat
Severity: protocol.SeverityWarning,
SuggestedFixes: fixes,
Tags: tags,
Related: related,
}, nil
}
func relatedInformation(ctx context.Context, view View, diag *analysis.Diagnostic) ([]RelatedInformation, error) {
var out []RelatedInformation
for _, related := range diag.Related {
r := span.NewRange(view.Session().Cache().FileSet(), related.Pos, related.End)
spn, err := r.Span()
if err != nil {
return nil, err
}
pkg, err := packageForSpan(ctx, view, spn)
if err != nil {
return nil, err
}
rng, err := spanToRange(ctx, view, pkg, spn, false)
if err != nil {
return nil, err
}
out = append(out, RelatedInformation{
URI: spn.URI(),
Range: rng,
Message: related.Message,
})
}
return out, nil
}
func clearReports(v View, reports map[span.URI][]Diagnostic, uri span.URI) {
if v.Ignore(uri) {
return