mirror of
https://github.com/golang/go.git
synced 2025-05-05 23:53:05 +00: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.
|
// Diagnostics should not contain SuggestedFixes that overlap.
|
||||||
// Experimental: This API is experimental and may change in the future.
|
// Experimental: This API is experimental and may change in the future.
|
||||||
SuggestedFixes []SuggestedFix // optional
|
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
|
// 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 {
|
if ident.Name == from {
|
||||||
msg := fmt.Sprintf("renaming %q to %q", from, to)
|
msg := fmt.Sprintf("renaming %q to %q", from, to)
|
||||||
pass.Report(analysis.Diagnostic{
|
pass.Report(analysis.Diagnostic{
|
||||||
ident.Pos(), ident.End(), "", msg,
|
Pos: ident.Pos(),
|
||||||
[]analysis.SuggestedFix{
|
End: ident.End(),
|
||||||
{msg, []analysis.TextEdit{
|
Message: msg,
|
||||||
{ident.Pos(), ident.End(), []byte(to)}},
|
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 {
|
func toProtocolDiagnostics(ctx context.Context, diagnostics []source.Diagnostic) []protocol.Diagnostic {
|
||||||
reports := []protocol.Diagnostic{}
|
reports := []protocol.Diagnostic{}
|
||||||
for _, diag := range diagnostics {
|
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{
|
reports = append(reports, protocol.Diagnostic{
|
||||||
Message: strings.TrimSpace(diag.Message), // go list returns errors prefixed by newline
|
Message: strings.TrimSpace(diag.Message), // go list returns errors prefixed by newline
|
||||||
Range: diag.Range,
|
Range: diag.Range,
|
||||||
Severity: diag.Severity,
|
Severity: diag.Severity,
|
||||||
Source: diag.Source,
|
Source: diag.Source,
|
||||||
Tags: diag.Tags,
|
Tags: diag.Tags,
|
||||||
|
RelatedInformation: related,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return reports
|
return reports
|
||||||
|
@ -26,6 +26,13 @@ type Diagnostic struct {
|
|||||||
Tags []protocol.DiagnosticTag
|
Tags []protocol.DiagnosticTag
|
||||||
|
|
||||||
SuggestedFixes []SuggestedFix
|
SuggestedFixes []SuggestedFix
|
||||||
|
Related []RelatedInformation
|
||||||
|
}
|
||||||
|
|
||||||
|
type RelatedInformation struct {
|
||||||
|
URI span.URI
|
||||||
|
Range protocol.Range
|
||||||
|
Message string
|
||||||
}
|
}
|
||||||
|
|
||||||
type DiagnosticSeverity int
|
type DiagnosticSeverity int
|
||||||
@ -170,26 +177,30 @@ func analyses(ctx context.Context, snapshot Snapshot, cph CheckPackageHandle, di
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func toDiagnostic(ctx context.Context, view View, diag *analysis.Diagnostic, category string) (Diagnostic, error) {
|
func packageForSpan(ctx context.Context, view View, spn span.Span) (Package, error) {
|
||||||
spn, err := span.NewRange(view.Session().Cache().FileSet(), diag.Pos, diag.End).Span()
|
|
||||||
if err != nil {
|
|
||||||
return Diagnostic{}, err
|
|
||||||
}
|
|
||||||
f, err := view.GetFile(ctx, spn.URI())
|
f, err := view.GetFile(ctx, spn.URI())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Diagnostic{}, err
|
return nil, err
|
||||||
}
|
}
|
||||||
// If the package has changed since these diagnostics were computed,
|
// If the package has changed since these diagnostics were computed,
|
||||||
// this may be incorrect. Should the package be associated with the diagnostic?
|
// this may be incorrect. Should the package be associated with the diagnostic?
|
||||||
_, cphs, err := view.CheckPackageHandles(ctx, f)
|
_, cphs, err := view.CheckPackageHandles(ctx, f)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Diagnostic{}, err
|
return nil, err
|
||||||
}
|
}
|
||||||
cph, err := NarrowestCheckPackageHandle(cphs)
|
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 {
|
if err != nil {
|
||||||
return Diagnostic{}, err
|
return Diagnostic{}, err
|
||||||
}
|
}
|
||||||
pkg, err := cph.Cached(ctx)
|
pkg, err := packageForSpan(ctx, view, spn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Diagnostic{}, err
|
return Diagnostic{}, err
|
||||||
}
|
}
|
||||||
@ -209,6 +220,12 @@ func toDiagnostic(ctx context.Context, view View, diag *analysis.Diagnostic, cat
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return Diagnostic{}, err
|
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.
|
// 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.
|
// 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.
|
// 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,
|
Severity: protocol.SeverityWarning,
|
||||||
SuggestedFixes: fixes,
|
SuggestedFixes: fixes,
|
||||||
Tags: tags,
|
Tags: tags,
|
||||||
|
Related: related,
|
||||||
}, nil
|
}, 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) {
|
func clearReports(v View, reports map[span.URI][]Diagnostic, uri span.URI) {
|
||||||
if v.Ignore(uri) {
|
if v.Ignore(uri) {
|
||||||
return
|
return
|
||||||
|
Loading…
x
Reference in New Issue
Block a user