zclsyntax: catch and test for more errors in for expressions
This commit is contained in:
parent
b625d4b90e
commit
a226ea120d
@ -695,6 +695,44 @@ func (e *ForExpr) Value(ctx *zcl.EvalContext) (cty.Value, zcl.Diagnostics) {
|
|||||||
childCtx := ctx.NewChild()
|
childCtx := ctx.NewChild()
|
||||||
childCtx.Variables = map[string]cty.Value{}
|
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 {
|
if e.KeyExpr != nil {
|
||||||
// Producing an object
|
// Producing an object
|
||||||
var vals map[string]cty.Value
|
var vals map[string]cty.Value
|
||||||
@ -731,14 +769,6 @@ func (e *ForExpr) Value(ctx *zcl.EvalContext) (cty.Value, zcl.Diagnostics) {
|
|||||||
known = false
|
known = false
|
||||||
continue
|
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)
|
include, err := convert.Convert(includeRaw, cty.Bool)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if known {
|
if known {
|
||||||
@ -753,6 +783,10 @@ func (e *ForExpr) Value(ctx *zcl.EvalContext) (cty.Value, zcl.Diagnostics) {
|
|||||||
known = false
|
known = false
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
if !include.IsKnown() {
|
||||||
|
known = false
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
if include.False() {
|
if include.False() {
|
||||||
// Skip this element
|
// Skip this element
|
||||||
|
@ -607,30 +607,56 @@ upper(
|
|||||||
1, // can't iterate over a string (even if it's unknown)
|
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{
|
&zcl.EvalContext{
|
||||||
Variables: map[string]cty.Value{
|
Variables: map[string]cty.Value{
|
||||||
"unk": cty.UnknownVal(cty.Bool),
|
"unkbool": cty.UnknownVal(cty.Bool),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
cty.DynamicVal,
|
cty.DynamicVal,
|
||||||
0,
|
0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
`[for v in ["a", "b"]: v if unk]`,
|
`[for v in ["a", "b"]: v if nullbool]`,
|
||||||
&zcl.EvalContext{
|
&zcl.EvalContext{
|
||||||
Variables: map[string]cty.Value{
|
Variables: map[string]cty.Value{
|
||||||
"unk": cty.UnknownVal(cty.Number),
|
"nullbool": cty.NullVal(cty.Bool),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
cty.DynamicVal,
|
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{
|
&zcl.EvalContext{
|
||||||
Variables: map[string]cty.Value{
|
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{
|
cty.TupleVal([]cty.Value{
|
||||||
|
Loading…
Reference in New Issue
Block a user