hcl: Include Expression reference in diagnostics

If a diagnostic occurs while we're evaluating an expression, we'll now
include a reference to that expression in the diagnostic object. We
previously added the corresponding EvalContext here too, and so with these
together it is now possible for a diagnostic renderer to see not only
what was in scope when the problem occurred but also what parts of that
scope the expression was relying on (via method Expression.Variables).
This commit is contained in:
Martin Atkins 2018-07-28 13:36:55 -07:00
parent 956c336d40
commit 6356254632
7 changed files with 52 additions and 17 deletions

View File

@ -47,6 +47,7 @@ func (b *expandBody) decodeSpec(blockS *hcl.BlockHeaderSchema, rawSpec *hcl.Bloc
Summary: "Invalid dynamic for_each value",
Detail: fmt.Sprintf("Cannot use a value of type %s in for_each. An iterable collection is required.", eachVal.Type()),
Subject: eachAttr.Expr.Range().Ptr(),
Expression: eachAttr.Expr,
EvalContext: b.forEachCtx,
})
return nil, diags
@ -57,6 +58,7 @@ func (b *expandBody) decodeSpec(blockS *hcl.BlockHeaderSchema, rawSpec *hcl.Bloc
Summary: "Invalid dynamic for_each value",
Detail: "Cannot use a null value in for_each.",
Subject: eachAttr.Expr.Range().Ptr(),
Expression: eachAttr.Expr,
EvalContext: b.forEachCtx,
})
return nil, diags
@ -165,6 +167,7 @@ func (s *expandSpec) newBlock(i *iteration, ctx *hcl.EvalContext) (*hcl.Block, h
Summary: "Invalid dynamic block label",
Detail: fmt.Sprintf("Cannot use this value as a dynamic block label: %s.", convErr),
Subject: labelExpr.Range().Ptr(),
Expression: labelExpr,
EvalContext: lCtx,
})
return nil, diags
@ -175,6 +178,7 @@ func (s *expandSpec) newBlock(i *iteration, ctx *hcl.EvalContext) (*hcl.Block, h
Summary: "Invalid dynamic block label",
Detail: "Cannot use a null value as a dynamic block label.",
Subject: labelExpr.Range().Ptr(),
Expression: labelExpr,
EvalContext: lCtx,
})
return nil, diags
@ -185,6 +189,7 @@ func (s *expandSpec) newBlock(i *iteration, ctx *hcl.EvalContext) (*hcl.Block, h
Summary: "Invalid dynamic block label",
Detail: "This value is not yet known. Dynamic block labels must be immediately-known values.",
Subject: labelExpr.Range().Ptr(),
Expression: labelExpr,
EvalContext: lCtx,
})
return nil, diags

View File

@ -46,22 +46,22 @@ type Diagnostic struct {
Subject *Range
Context *Range
// For diagnostics that occur when evaluating an expression, EvalContext
// may point to the EvalContext that was active when evaluating that
// expression, which may allow for the inclusion of additional useful
// information when rendering a diagnostic message to the user.
// For diagnostics that occur when evaluating an expression, Expression
// may refer to that expression and EvalContext may point to the
// EvalContext that was active when evaluating it. This may allow for the
// inclusion of additional useful information when rendering a diagnostic
// message to the user.
//
// It is not always possible to select a single EvalContext for a
// diagnostic, and so in some cases this field may be nil even when an
// expression causes a problem. Therefore it is not valid to use the
// nil-ness of this field to definitively decide whether a diagnostic
// relates to an expression.
// expression causes a problem.
//
// EvalContexts form a tree, so the given EvalContext may refer to a parent
// which in turn refers to another parent, etc. For a full picture of all
// of the active variables and functions the caller must walk up this
// chain, preferring definitions that are "closer" to the expression in
// case of colliding names.
Expression Expression
EvalContext *EvalContext
}

View File

@ -13,9 +13,10 @@ import (
// been seen by a caller. Its purpose is to apply additional context to a
// set of diagnostics produced by a "deeper" component as the stack unwinds
// during expression evaluation.
func setDiagEvalContext(diags hcl.Diagnostics, ctx *hcl.EvalContext) {
func setDiagEvalContext(diags hcl.Diagnostics, expr hcl.Expression, ctx *hcl.EvalContext) {
for _, diag := range diags {
if diag.EvalContext == nil {
if diag.Expression == nil {
diag.Expression = expr
diag.EvalContext = ctx
}
}

View File

@ -106,7 +106,7 @@ func (e *ScopeTraversalExpr) walkChildNodes(w internalWalkFunc) {
func (e *ScopeTraversalExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
val, diags := e.Traversal.TraverseAbs(ctx)
setDiagEvalContext(diags, ctx)
setDiagEvalContext(diags, e, ctx)
return val, diags
}
@ -138,7 +138,7 @@ func (e *RelativeTraversalExpr) walkChildNodes(w internalWalkFunc) {
func (e *RelativeTraversalExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
src, diags := e.Source.Value(ctx)
ret, travDiags := e.Traversal.TraverseRel(src)
setDiagEvalContext(travDiags, ctx)
setDiagEvalContext(travDiags, e, ctx)
diags = append(diags, travDiags...)
return ret, diags
}
@ -214,6 +214,7 @@ func (e *FunctionCallExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnosti
Summary: "Function calls not allowed",
Detail: "Functions may not be called here.",
Subject: e.Range().Ptr(),
Expression: e,
EvalContext: ctx,
},
}
@ -235,6 +236,7 @@ func (e *FunctionCallExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnosti
Detail: fmt.Sprintf("There is no function named %q.%s", e.Name, suggestion),
Subject: &e.NameRange,
Context: e.Range().Ptr(),
Expression: e,
EvalContext: ctx,
},
}
@ -263,8 +265,9 @@ func (e *FunctionCallExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnosti
Severity: hcl.DiagError,
Summary: "Invalid expanding argument value",
Detail: "The expanding argument (indicated by ...) must not be null.",
Context: expandExpr.Range().Ptr(),
Subject: e.Range().Ptr(),
Subject: expandExpr.Range().Ptr(),
Context: e.Range().Ptr(),
Expression: expandExpr,
EvalContext: ctx,
})
return cty.DynamicVal, diags
@ -289,8 +292,9 @@ func (e *FunctionCallExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnosti
Severity: hcl.DiagError,
Summary: "Invalid expanding argument value",
Detail: "The expanding argument (indicated by ...) must be of a tuple, list, or set type.",
Context: expandExpr.Range().Ptr(),
Subject: e.Range().Ptr(),
Subject: expandExpr.Range().Ptr(),
Context: e.Range().Ptr(),
Expression: expandExpr,
EvalContext: ctx,
})
return cty.DynamicVal, diags
@ -313,6 +317,7 @@ func (e *FunctionCallExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnosti
),
Subject: &e.CloseParenRange,
Context: e.Range().Ptr(),
Expression: e,
EvalContext: ctx,
},
}
@ -329,6 +334,7 @@ func (e *FunctionCallExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnosti
),
Subject: args[len(params)].StartRange().Ptr(),
Context: e.Range().Ptr(),
Expression: e,
EvalContext: ctx,
},
}
@ -361,6 +367,7 @@ func (e *FunctionCallExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnosti
),
Subject: argExpr.StartRange().Ptr(),
Context: e.Range().Ptr(),
Expression: argExpr,
EvalContext: ctx,
})
}
@ -399,6 +406,7 @@ func (e *FunctionCallExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnosti
),
Subject: argExpr.StartRange().Ptr(),
Context: e.Range().Ptr(),
Expression: argExpr,
EvalContext: ctx,
})
@ -412,6 +420,7 @@ func (e *FunctionCallExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnosti
),
Subject: e.StartRange().Ptr(),
Context: e.Range().Ptr(),
Expression: e,
EvalContext: ctx,
})
}
@ -481,6 +490,7 @@ func (e *ConditionalExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostic
),
Subject: hcl.RangeBetween(e.TrueResult.Range(), e.FalseResult.Range()).Ptr(),
Context: &e.SrcRange,
Expression: e,
EvalContext: ctx,
},
}
@ -495,6 +505,7 @@ func (e *ConditionalExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostic
Detail: "The condition value is null. Conditions must either be true or false.",
Subject: e.Condition.Range().Ptr(),
Context: &e.SrcRange,
Expression: e.Condition,
EvalContext: ctx,
})
return cty.UnknownVal(resultType), diags
@ -510,6 +521,7 @@ func (e *ConditionalExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostic
Detail: fmt.Sprintf("The condition expression must be of type bool."),
Subject: e.Condition.Range().Ptr(),
Context: &e.SrcRange,
Expression: e.Condition,
EvalContext: ctx,
})
return cty.UnknownVal(resultType), diags
@ -531,6 +543,7 @@ func (e *ConditionalExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostic
),
Subject: e.TrueResult.Range().Ptr(),
Context: &e.SrcRange,
Expression: e.TrueResult,
EvalContext: ctx,
})
trueResult = cty.UnknownVal(resultType)
@ -551,8 +564,9 @@ func (e *ConditionalExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostic
"The false result value has the wrong type: %s.",
err.Error(),
),
Subject: e.TrueResult.Range().Ptr(),
Subject: e.FalseResult.Range().Ptr(),
Context: &e.SrcRange,
Expression: e.FalseResult,
EvalContext: ctx,
})
falseResult = cty.UnknownVal(resultType)
@ -697,6 +711,7 @@ func (e *ObjectConsExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics
Summary: "Null value as key",
Detail: "Can't use a null value as a key.",
Subject: item.ValueExpr.Range().Ptr(),
Expression: item.KeyExpr,
EvalContext: ctx,
})
known = false
@ -711,6 +726,7 @@ func (e *ObjectConsExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics
Summary: "Incorrect key type",
Detail: fmt.Sprintf("Can't use this value as a key: %s.", err.Error()),
Subject: item.ValueExpr.Range().Ptr(),
Expression: item.ValueExpr,
EvalContext: ctx,
})
known = false

View File

@ -154,6 +154,7 @@ func (e *BinaryOpExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics)
Detail: fmt.Sprintf("Unsuitable value for left operand: %s.", err),
Subject: e.LHS.Range().Ptr(),
Context: &e.SrcRange,
Expression: e.LHS,
EvalContext: ctx,
})
}
@ -165,6 +166,7 @@ func (e *BinaryOpExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics)
Detail: fmt.Sprintf("Unsuitable value for right operand: %s.", err),
Subject: e.RHS.Range().Ptr(),
Context: &e.SrcRange,
Expression: e.RHS,
EvalContext: ctx,
})
}
@ -184,6 +186,7 @@ func (e *BinaryOpExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics)
Summary: "Operation failed",
Detail: fmt.Sprintf("Error during operation: %s.", err),
Subject: &e.SrcRange,
Expression: e,
EvalContext: ctx,
})
return cty.UnknownVal(e.Op.Type), diags
@ -227,6 +230,7 @@ func (e *UnaryOpExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
Detail: fmt.Sprintf("Unsuitable value for unary operand: %s.", err),
Subject: e.Val.Range().Ptr(),
Context: &e.SrcRange,
Expression: e.Val,
EvalContext: ctx,
})
}
@ -246,6 +250,7 @@ func (e *UnaryOpExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
Summary: "Operation failed",
Detail: fmt.Sprintf("Error during operation: %s.", err),
Subject: &e.SrcRange,
Expression: e,
EvalContext: ctx,
})
return cty.UnknownVal(e.Op.Type), diags

View File

@ -39,6 +39,7 @@ func (e *TemplateExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics)
),
Subject: part.Range().Ptr(),
Context: &e.SrcRange,
Expression: part,
EvalContext: ctx,
})
continue
@ -64,6 +65,7 @@ func (e *TemplateExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics)
),
Subject: part.Range().Ptr(),
Context: &e.SrcRange,
Expression: part,
EvalContext: ctx,
})
continue
@ -130,6 +132,7 @@ func (e *TemplateJoinExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnosti
"An iteration result is null. Cannot include a null value in a string template.",
),
Subject: e.Range().Ptr(),
Expression: e,
EvalContext: ctx,
})
continue
@ -147,6 +150,7 @@ func (e *TemplateJoinExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnosti
err.Error(),
),
Subject: e.Range().Ptr(),
Expression: e,
EvalContext: ctx,
})
continue

View File

@ -432,7 +432,8 @@ func (e *expression) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
Value: jsonAttr.Name,
SrcRange: jsonAttr.NameRange,
}}).Value(ctx)
val, valDiags := (&expression{src: jsonAttr.Value}).Value(ctx)
valExpr := &expression{src: jsonAttr.Value}
val, valDiags := valExpr.Value(ctx)
diags = append(diags, nameDiags...)
diags = append(diags, valDiags...)
@ -444,6 +445,7 @@ func (e *expression) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
Summary: "Invalid object key expression",
Detail: fmt.Sprintf("Cannot use this expression as an object key: %s.", err),
Subject: &jsonAttr.NameRange,
Expression: valExpr,
EvalContext: ctx,
})
continue
@ -454,6 +456,7 @@ func (e *expression) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
Summary: "Invalid object key expression",
Detail: "Cannot use null value as an object key.",
Subject: &jsonAttr.NameRange,
Expression: valExpr,
EvalContext: ctx,
})
continue
@ -477,6 +480,7 @@ func (e *expression) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
Summary: "Duplicate object attribute",
Detail: fmt.Sprintf("An attribute named %q was already defined at %s.", nameStr, attrRanges[nameStr]),
Subject: &jsonAttr.NameRange,
Expression: e,
EvalContext: ctx,
})
continue