zclsyntax: catch and test for more errors in for expressions

This commit is contained in:
Martin Atkins 2017-06-18 09:36:06 -07:00
parent b625d4b90e
commit a226ea120d
2 changed files with 75 additions and 15 deletions

View File

@ -695,6 +695,44 @@ func (e *ForExpr) Value(ctx *zcl.EvalContext) (cty.Value, zcl.Diagnostics) {
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
// 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
// straightforward cases and prevent a barrage of repeated errors.
if e.CondExpr != nil {
if e.KeyVar != "" {
childCtx.Variables[e.KeyVar] = cty.DynamicVal
}
childCtx.Variables[e.ValVar] = cty.DynamicVal
result, condDiags := e.CondExpr.Value(childCtx)
diags = append(diags, condDiags...)
if result.IsNull() {
diags = append(diags, &zcl.Diagnostic{
Severity: zcl.DiagError,
Summary: "Condition is null",
Detail: "The value of the 'if' clause must not be null.",
Subject: e.CondExpr.Range().Ptr(),
Context: &e.SrcRange,
})
return cty.DynamicVal, diags
}
_, err := convert.Convert(result, cty.Bool)
if err != nil {
diags = append(diags, &zcl.Diagnostic{
Severity: zcl.DiagError,
Summary: "Invalid 'for' condition",
Detail: fmt.Sprintf("The 'if' clause value is invalid: %s.", err.Error()),
Subject: e.CondExpr.Range().Ptr(),
Context: &e.SrcRange,
})
return cty.DynamicVal, diags
}
if condDiags.HasErrors() {
return cty.DynamicVal, diags
}
}
if e.KeyExpr != nil {
// Producing an object
var vals map[string]cty.Value
@ -731,14 +769,6 @@ func (e *ForExpr) Value(ctx *zcl.EvalContext) (cty.Value, zcl.Diagnostics) {
known = false
continue
}
if !includeRaw.IsKnown() {
// We will eventually return DynamicVal, but we'll continue
// iterating in case there are other diagnostics to gather
// for later elements.
known = false
continue
}
include, err := convert.Convert(includeRaw, cty.Bool)
if err != nil {
if known {
@ -753,6 +783,10 @@ func (e *ForExpr) Value(ctx *zcl.EvalContext) (cty.Value, zcl.Diagnostics) {
known = false
continue
}
if !include.IsKnown() {
known = false
continue
}
if include.False() {
// Skip this element

View File

@ -607,30 +607,56 @@ upper(
1, // can't iterate over a string (even if it's unknown)
},
{
`[for v in ["a", "b"]: v if unk]`,
`[for v in ["a", "b"]: v if unkbool]`,
&zcl.EvalContext{
Variables: map[string]cty.Value{
"unk": cty.UnknownVal(cty.Bool),
"unkbool": cty.UnknownVal(cty.Bool),
},
},
cty.DynamicVal,
0,
},
{
`[for v in ["a", "b"]: v if unk]`,
`[for v in ["a", "b"]: v if nullbool]`,
&zcl.EvalContext{
Variables: map[string]cty.Value{
"unk": cty.UnknownVal(cty.Number),
"nullbool": cty.NullVal(cty.Bool),
},
},
cty.DynamicVal,
0, // if expression must be bool
1, // value of if clause must not be null
},
{
`[for v in ["a", "b"]: unk]`,
`[for v in ["a", "b"]: v if dyn]`,
&zcl.EvalContext{
Variables: map[string]cty.Value{
"unk": cty.UnknownVal(cty.String),
"dyn": cty.DynamicVal,
},
},
cty.DynamicVal,
0,
},
{
`[for v in ["a", "b"]: v if unknum]`,
&zcl.EvalContext{
Variables: map[string]cty.Value{
"unknum": cty.UnknownVal(cty.List(cty.Number)),
},
},
cty.DynamicVal,
1, // if expression must be bool
},
{
`[for i, v in ["a", "b"]: v if i + i]`,
nil,
cty.DynamicVal,
1, // if expression must be bool
},
{
`[for v in ["a", "b"]: unkstr]`,
&zcl.EvalContext{
Variables: map[string]cty.Value{
"unkstr": cty.UnknownVal(cty.String),
},
},
cty.TupleVal([]cty.Value{