hclsyntax: report expr and ctx correctly in ForExpr diagnostics

We previously weren't returning appropriate Expression and EvalContext
references inside many of the diagnostics for ForExpr.

First, it was using the top-level expression instead of one of the nested
expressions in many cases. Secondly, it was using the given context
rather than the child context when talking about expressions that get
evaluated once per iteration.

As a result of this reporting we must now produce a new EvalContext for
each iteration, rather than sharing and mutating as we did before, but
in retrospect that's less likely to cause other confusing bugs anyway,
since we don't generally expect EvalContexts to be mutated.
This commit is contained in:
Martin Atkins 2018-07-28 15:24:39 -07:00
parent 5919f80710
commit 627c12b67c

View File

@ -859,6 +859,7 @@ func (e *ForExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
Detail: "A null value cannot be used as the collection in a 'for' expression.", Detail: "A null value cannot be used as the collection in a 'for' expression.",
Subject: e.CollExpr.Range().Ptr(), Subject: e.CollExpr.Range().Ptr(),
Context: &e.SrcRange, Context: &e.SrcRange,
Expression: e.CollExpr,
EvalContext: ctx, EvalContext: ctx,
}) })
return cty.DynamicVal, diags return cty.DynamicVal, diags
@ -876,6 +877,7 @@ func (e *ForExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
), ),
Subject: e.CollExpr.Range().Ptr(), Subject: e.CollExpr.Range().Ptr(),
Context: &e.SrcRange, Context: &e.SrcRange,
Expression: e.CollExpr,
EvalContext: ctx, EvalContext: ctx,
}) })
return cty.DynamicVal, diags return cty.DynamicVal, diags
@ -884,14 +886,13 @@ func (e *ForExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
return cty.DynamicVal, diags return cty.DynamicVal, diags
} }
childCtx := ctx.NewChild()
childCtx.Variables = map[string]cty.Value{}
// Before we start we'll do an early check to see if any CondExpr we've // Before we start we'll do an early check to see if any CondExpr we've
// been given is of the wrong type. This isn't 100% reliable (it may // been given is of the wrong type. This isn't 100% reliable (it may
// be DynamicVal until real values are given) but it should catch some // be DynamicVal until real values are given) but it should catch some
// straightforward cases and prevent a barrage of repeated errors. // straightforward cases and prevent a barrage of repeated errors.
if e.CondExpr != nil { if e.CondExpr != nil {
childCtx := ctx.NewChild()
childCtx.Variables = map[string]cty.Value{}
if e.KeyVar != "" { if e.KeyVar != "" {
childCtx.Variables[e.KeyVar] = cty.DynamicVal childCtx.Variables[e.KeyVar] = cty.DynamicVal
} }
@ -906,6 +907,7 @@ func (e *ForExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
Detail: "The value of the 'if' clause must not be null.", Detail: "The value of the 'if' clause must not be null.",
Subject: e.CondExpr.Range().Ptr(), Subject: e.CondExpr.Range().Ptr(),
Context: &e.SrcRange, Context: &e.SrcRange,
Expression: e.CondExpr,
EvalContext: ctx, EvalContext: ctx,
}) })
return cty.DynamicVal, diags return cty.DynamicVal, diags
@ -918,6 +920,7 @@ func (e *ForExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
Detail: fmt.Sprintf("The 'if' clause value is invalid: %s.", err.Error()), Detail: fmt.Sprintf("The 'if' clause value is invalid: %s.", err.Error()),
Subject: e.CondExpr.Range().Ptr(), Subject: e.CondExpr.Range().Ptr(),
Context: &e.SrcRange, Context: &e.SrcRange,
Expression: e.CondExpr,
EvalContext: ctx, EvalContext: ctx,
}) })
return cty.DynamicVal, diags return cty.DynamicVal, diags
@ -942,6 +945,8 @@ func (e *ForExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
known := true known := true
for it.Next() { for it.Next() {
k, v := it.Element() k, v := it.Element()
childCtx := ctx.NewChild()
childCtx.Variables = map[string]cty.Value{}
if e.KeyVar != "" { if e.KeyVar != "" {
childCtx.Variables[e.KeyVar] = k childCtx.Variables[e.KeyVar] = k
} }
@ -958,7 +963,8 @@ func (e *ForExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
Detail: "The value of the 'if' clause must not be null.", Detail: "The value of the 'if' clause must not be null.",
Subject: e.CondExpr.Range().Ptr(), Subject: e.CondExpr.Range().Ptr(),
Context: &e.SrcRange, Context: &e.SrcRange,
EvalContext: ctx, Expression: e.CondExpr,
EvalContext: childCtx,
}) })
} }
known = false known = false
@ -973,7 +979,8 @@ func (e *ForExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
Detail: fmt.Sprintf("The 'if' clause value is invalid: %s.", err.Error()), Detail: fmt.Sprintf("The 'if' clause value is invalid: %s.", err.Error()),
Subject: e.CondExpr.Range().Ptr(), Subject: e.CondExpr.Range().Ptr(),
Context: &e.SrcRange, Context: &e.SrcRange,
EvalContext: ctx, Expression: e.CondExpr,
EvalContext: childCtx,
}) })
} }
known = false known = false
@ -1000,7 +1007,8 @@ func (e *ForExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
Detail: "Key expression in 'for' expression must not produce a null value.", Detail: "Key expression in 'for' expression must not produce a null value.",
Subject: e.KeyExpr.Range().Ptr(), Subject: e.KeyExpr.Range().Ptr(),
Context: &e.SrcRange, Context: &e.SrcRange,
EvalContext: ctx, Expression: e.KeyExpr,
EvalContext: childCtx,
}) })
} }
known = false known = false
@ -1020,7 +1028,8 @@ func (e *ForExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
Detail: fmt.Sprintf("The key expression produced an invalid result: %s.", err.Error()), Detail: fmt.Sprintf("The key expression produced an invalid result: %s.", err.Error()),
Subject: e.KeyExpr.Range().Ptr(), Subject: e.KeyExpr.Range().Ptr(),
Context: &e.SrcRange, Context: &e.SrcRange,
EvalContext: ctx, Expression: e.KeyExpr,
EvalContext: childCtx,
}) })
} }
known = false known = false
@ -1045,7 +1054,8 @@ func (e *ForExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
), ),
Subject: e.KeyExpr.Range().Ptr(), Subject: e.KeyExpr.Range().Ptr(),
Context: &e.SrcRange, Context: &e.SrcRange,
EvalContext: ctx, Expression: e.KeyExpr,
EvalContext: childCtx,
}) })
} else { } else {
vals[key.AsString()] = val vals[key.AsString()] = val
@ -1075,6 +1085,8 @@ func (e *ForExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
known := true known := true
for it.Next() { for it.Next() {
k, v := it.Element() k, v := it.Element()
childCtx := ctx.NewChild()
childCtx.Variables = map[string]cty.Value{}
if e.KeyVar != "" { if e.KeyVar != "" {
childCtx.Variables[e.KeyVar] = k childCtx.Variables[e.KeyVar] = k
} }
@ -1091,7 +1103,8 @@ func (e *ForExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
Detail: "The value of the 'if' clause must not be null.", Detail: "The value of the 'if' clause must not be null.",
Subject: e.CondExpr.Range().Ptr(), Subject: e.CondExpr.Range().Ptr(),
Context: &e.SrcRange, Context: &e.SrcRange,
EvalContext: ctx, Expression: e.CondExpr,
EvalContext: childCtx,
}) })
} }
known = false known = false
@ -1114,7 +1127,8 @@ func (e *ForExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
Detail: fmt.Sprintf("The 'if' clause value is invalid: %s.", err.Error()), Detail: fmt.Sprintf("The 'if' clause value is invalid: %s.", err.Error()),
Subject: e.CondExpr.Range().Ptr(), Subject: e.CondExpr.Range().Ptr(),
Context: &e.SrcRange, Context: &e.SrcRange,
EvalContext: ctx, Expression: e.CondExpr,
EvalContext: childCtx,
}) })
} }
known = false known = false
@ -1205,6 +1219,7 @@ func (e *SplatExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
Detail: "Splat expressions (with the * symbol) cannot be applied to null values.", Detail: "Splat expressions (with the * symbol) cannot be applied to null values.",
Subject: e.Source.Range().Ptr(), Subject: e.Source.Range().Ptr(),
Context: hcl.RangeBetween(e.Source.Range(), e.MarkerRange).Ptr(), Context: hcl.RangeBetween(e.Source.Range(), e.MarkerRange).Ptr(),
Expression: e.Source,
EvalContext: ctx, EvalContext: ctx,
}) })
return cty.DynamicVal, diags return cty.DynamicVal, diags