views/json: Fix diag crash with invalid highlight

Some diagnostic sources (I'm looking at you, HCL) fail to set the end of
the subject range. This is a bug in those code paths, but we can ensure
that we generate valid JSON diagnostics by checking for it here.

By doing so before the range normalization, we ensure that we generate a
unit width highlight whenever possible, so that at least something
useful is displayed.
This commit is contained in:
Alisdair McDiarmid 2021-05-04 08:33:39 -04:00
parent e9c9092c40
commit 3a9b369b43
3 changed files with 75 additions and 0 deletions

View File

@ -133,6 +133,12 @@ func NewDiagnostic(diag tfdiags.Diagnostic, sources map[string][]byte) *Diagnost
// We'll borrow HCL's range implementation here, because it has some // We'll borrow HCL's range implementation here, because it has some
// handy features to help us produce a nice source code snippet. // handy features to help us produce a nice source code snippet.
highlightRange := sourceRefs.Subject.ToHCL() highlightRange := sourceRefs.Subject.ToHCL()
// Some diagnostic sources fail to set the end of the subject range.
if highlightRange.End == (hcl.Pos{}) {
highlightRange.End = highlightRange.Start
}
snippetRange := highlightRange snippetRange := highlightRange
if sourceRefs.Context != nil { if sourceRefs.Context != nil {
snippetRange = sourceRefs.Context.ToHCL() snippetRange = sourceRefs.Context.ToHCL()

View File

@ -246,6 +246,44 @@ func TestNewDiagnostic(t *testing.T) {
}, },
}, },
}, },
"error with unset highlight end position": {
&hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "There is no end",
Detail: "But there is a beginning",
Subject: &hcl.Range{
Filename: "test.tf",
Start: hcl.Pos{Line: 1, Column: 16, Byte: 15},
End: hcl.Pos{Line: 0, Column: 0, Byte: 0},
},
},
&Diagnostic{
Severity: "error",
Summary: "There is no end",
Detail: "But there is a beginning",
Range: &DiagnosticRange{
Filename: "test.tf",
Start: Pos{
Line: 1,
Column: 16,
Byte: 15,
},
End: Pos{
Line: 1,
Column: 17,
Byte: 16,
},
},
Snippet: &DiagnosticSnippet{
Context: strPtr(`resource "test_resource" "test"`),
Code: `resource "test_resource" "test" {`,
StartLine: 1,
HighlightStartOffset: 15,
HighlightEndOffset: 16,
Values: []DiagnosticExpressionValue{},
},
},
},
"error with source code subject and known expression": { "error with source code subject and known expression": {
&hcl.Diagnostic{ &hcl.Diagnostic{
Severity: hcl.DiagError, Severity: hcl.DiagError,
@ -698,6 +736,11 @@ func TestNewDiagnostic(t *testing.T) {
"diagnostic", "diagnostic",
fmt.Sprintf("%s.json", strings.ReplaceAll(name, " ", "-")), fmt.Sprintf("%s.json", strings.ReplaceAll(name, " ", "-")),
) )
// Generate golden reference by uncommenting the next two lines:
// gotBytes = append(gotBytes, '\n')
// os.WriteFile(filename, gotBytes, 0644)
wantFile, err := os.Open(filename) wantFile, err := os.Open(filename)
if err != nil { if err != nil {
t.Fatalf("failed to open golden file: %s", err) t.Fatalf("failed to open golden file: %s", err)

View File

@ -0,0 +1,26 @@
{
"severity": "error",
"summary": "There is no end",
"detail": "But there is a beginning",
"range": {
"filename": "test.tf",
"start": {
"line": 1,
"column": 16,
"byte": 15
},
"end": {
"line": 1,
"column": 17,
"byte": 16
}
},
"snippet": {
"context": "resource \"test_resource\" \"test\"",
"code": "resource \"test_resource\" \"test\" {",
"start_line": 1,
"highlight_start_offset": 15,
"highlight_end_offset": 16,
"values": []
}
}