Merge pull request #28598 from hashicorp/alisdair/fix-diagnostic-snippet-crash
cli: Fix diagnostic snippet crash
This commit is contained in:
commit
53fab10b26
|
@ -240,6 +240,16 @@ func appendSourceSnippets(buf *bytes.Buffer, diag *viewsjson.Diagnostic, color *
|
||||||
// Split the snippet and render the highlighted section with underlines
|
// Split the snippet and render the highlighted section with underlines
|
||||||
start := snippet.HighlightStartOffset
|
start := snippet.HighlightStartOffset
|
||||||
end := snippet.HighlightEndOffset
|
end := snippet.HighlightEndOffset
|
||||||
|
|
||||||
|
// Only buggy diagnostics can have an end range before the start, but
|
||||||
|
// we need to ensure we don't crash here if that happens.
|
||||||
|
if end < start {
|
||||||
|
end = start + 1
|
||||||
|
if end > len(code) {
|
||||||
|
end = len(code)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
before, highlight, after := code[0:start], code[start:end], code[end:]
|
before, highlight, after := code[0:start], code[start:end], code[end:]
|
||||||
code = fmt.Sprintf(color.Color("%s[underline]%s[reset]%s"), before, highlight, after)
|
code = fmt.Sprintf(color.Color("%s[underline]%s[reset]%s"), before, highlight, after)
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,8 @@ import (
|
||||||
"github.com/mitchellh/colorstring"
|
"github.com/mitchellh/colorstring"
|
||||||
"github.com/zclconf/go-cty/cty"
|
"github.com/zclconf/go-cty/cty"
|
||||||
|
|
||||||
|
viewsjson "github.com/hashicorp/terraform/command/views/json"
|
||||||
|
|
||||||
"github.com/hashicorp/terraform/tfdiags"
|
"github.com/hashicorp/terraform/tfdiags"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -699,3 +701,56 @@ eventually make it onto multiple lines. THE END
|
||||||
t.Fatalf("unexpected output: got:\n%s\nwant\n%s\n", output, expected)
|
t.Fatalf("unexpected output: got:\n%s\nwant\n%s\n", output, expected)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Test cases covering invalid JSON diagnostics which should still render
|
||||||
|
// correctly. These JSON diagnostic values cannot be generated from the
|
||||||
|
// json.NewDiagnostic code path, but we may read and display JSON diagnostics
|
||||||
|
// in future from other sources.
|
||||||
|
func TestDiagnosticFromJSON_invalid(t *testing.T) {
|
||||||
|
tests := map[string]struct {
|
||||||
|
Diag *viewsjson.Diagnostic
|
||||||
|
Want string
|
||||||
|
}{
|
||||||
|
"zero-value end range and highlight end byte": {
|
||||||
|
&viewsjson.Diagnostic{
|
||||||
|
Severity: viewsjson.DiagnosticSeverityError,
|
||||||
|
Summary: "Bad end",
|
||||||
|
Detail: "It all went wrong.",
|
||||||
|
Range: &viewsjson.DiagnosticRange{
|
||||||
|
Filename: "ohno.tf",
|
||||||
|
Start: viewsjson.Pos{Line: 1, Column: 23, Byte: 22},
|
||||||
|
End: viewsjson.Pos{Line: 0, Column: 0, Byte: 0},
|
||||||
|
},
|
||||||
|
Snippet: &viewsjson.DiagnosticSnippet{
|
||||||
|
Code: `resource "foo_bar "baz" {`,
|
||||||
|
StartLine: 1,
|
||||||
|
HighlightStartOffset: 22,
|
||||||
|
HighlightEndOffset: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
`[red]╷[reset]
|
||||||
|
[red]│[reset] [bold][red]Error: [reset][bold]Bad end[reset]
|
||||||
|
[red]│[reset]
|
||||||
|
[red]│[reset] on ohno.tf line 1:
|
||||||
|
[red]│[reset] 1: resource "foo_bar "baz[underline]"[reset] {
|
||||||
|
[red]│[reset]
|
||||||
|
[red]│[reset] It all went wrong.
|
||||||
|
[red]╵[reset]
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// This empty Colorize just passes through all of the formatting codes
|
||||||
|
// untouched, because it doesn't define any formatting keywords.
|
||||||
|
colorize := &colorstring.Colorize{}
|
||||||
|
|
||||||
|
for name, test := range tests {
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
got := strings.TrimSpace(DiagnosticFromJSON(test.Diag, colorize, 40))
|
||||||
|
want := strings.TrimSpace(test.Want)
|
||||||
|
if got != want {
|
||||||
|
t.Errorf("wrong result\ngot:\n%s\n\nwant:\n%s\n\n", got, want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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)
|
||||||
|
|
26
command/views/json/testdata/diagnostic/error-with-unset-highlight-end-position.json
vendored
Normal file
26
command/views/json/testdata/diagnostic/error-with-unset-highlight-end-position.json
vendored
Normal 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": []
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue