add addresses to diagnostics

Add an address argument to tfdiags.InConfigBody, and store the address
string the diagnostics details. Since nearly every place where we want
to annotate the diagnostics with the config context we also have some
sort of address, we can use the same call to insert them both into the
diagnostic.

Perhaps we should rename InConfigBody and ElaborateFromConfigBody to
reflect the additional address parameter, but for now we can verify this
is a pattern that suits us.
This commit is contained in:
James Bardin 2021-04-02 16:49:25 -04:00
parent 973ed6eae6
commit 503c413de2
4 changed files with 38 additions and 8 deletions

View File

@ -19,17 +19,20 @@ import (
// contextualFromConfig is an interface type implemented by diagnostic types
// that can elaborate themselves when given information about the configuration
// body they are embedded in.
// body they are embedded in, as well as the runtime address associated with
// that configuration.
//
// Usually this entails extracting source location information in order to
// populate the "Subject" range.
type contextualFromConfigBody interface {
ElaborateFromConfigBody(hcl.Body) Diagnostic
ElaborateFromConfigBody(hcl.Body, string) Diagnostic
}
// InConfigBody returns a copy of the receiver with any config-contextual
// diagnostics elaborated in the context of the given body.
func (diags Diagnostics) InConfigBody(body hcl.Body) Diagnostics {
// diagnostics elaborated in the context of the given body. An optional address
// argument may be added to indicate which instance of the configuration the
// error related to.
func (diags Diagnostics) InConfigBody(body hcl.Body, addr string) Diagnostics {
if len(diags) == 0 {
return nil
}
@ -37,7 +40,7 @@ func (diags Diagnostics) InConfigBody(body hcl.Body) Diagnostics {
ret := make(Diagnostics, len(diags))
for i, srcDiag := range diags {
if cd, isCD := srcDiag.(contextualFromConfigBody); isCD {
ret[i] = cd.ElaborateFromConfigBody(body)
ret[i] = cd.ElaborateFromConfigBody(body, addr)
} else {
ret[i] = srcDiag
}
@ -112,7 +115,12 @@ type attributeDiagnostic struct {
// source location information is still available, for more accuracy. This
// is not always possible due to system architecture, so this serves as a
// "best effort" fallback behavior for such situations.
func (d *attributeDiagnostic) ElaborateFromConfigBody(body hcl.Body) Diagnostic {
func (d *attributeDiagnostic) ElaborateFromConfigBody(body hcl.Body, addr string) Diagnostic {
// don't change an existing address
if d.address == "" {
d.address = addr
}
if len(d.attrPath) < 1 {
// Should never happen, but we'll allow it rather than crashing.
return d
@ -353,7 +361,12 @@ type wholeBodyDiagnostic struct {
subject *SourceRange // populated only after ElaborateFromConfigBody
}
func (d *wholeBodyDiagnostic) ElaborateFromConfigBody(body hcl.Body) Diagnostic {
func (d *wholeBodyDiagnostic) ElaborateFromConfigBody(body hcl.Body, addr string) Diagnostic {
// don't change an existing address
if d.address == "" {
d.address = addr
}
if d.subject != nil {
// Don't modify an already-elaborated diagnostic.
return d

View File

@ -185,6 +185,7 @@ simple_attr = "val"
diagnosticBase: diagnosticBase{
summary: "preexisting",
detail: "detail",
address: "original",
},
subject: &SourceRange{
Filename: "somewhere_else.tf",
@ -535,9 +536,22 @@ simple_attr = "val"
for i, tc := range testCases {
t.Run(fmt.Sprintf("%d:%s", i, tc.Diag.Description()), func(t *testing.T) {
var diags Diagnostics
origAddr := tc.Diag.Description().Address
diags = diags.Append(tc.Diag)
gotDiags := diags.InConfigBody(f.Body)
gotDiags := diags.InConfigBody(f.Body, "test.addr")
gotRange := gotDiags[0].Source().Subject
gotAddr := gotDiags[0].Description().Address
switch {
case origAddr != "":
if gotAddr != origAddr {
t.Errorf("original diagnostic address modified from %s to %s", origAddr, gotAddr)
}
case gotAddr != "test.addr":
t.Error("missing detail address")
}
for _, problem := range deep.Equal(gotRange, tc.ExpectedRange) {
t.Error(problem)

View File

@ -25,6 +25,7 @@ const (
)
type Description struct {
Address string
Summary string
Detail string
}

View File

@ -9,6 +9,7 @@ type diagnosticBase struct {
severity Severity
summary string
detail string
address string
}
func (d diagnosticBase) Severity() Severity {
@ -19,6 +20,7 @@ func (d diagnosticBase) Description() Description {
return Description{
Summary: d.summary,
Detail: d.detail,
Address: d.address,
}
}