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:
parent
747b8b11d4
commit
ce0314c87e
@ -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
|
||||
|
@ -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),
|
||||
}},
|
||||
}},
|
||||
})
|
||||
}
|
||||
})
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user