Skip to content

Commit ba07594

Browse files
authored
Merge pull request #719 from hashicorp/jbardin/short-circuits
fix boolean short-circuit diagnostic handling
2 parents 0e87e76 + d69d8de commit ba07594

File tree

2 files changed

+29
-7
lines changed

2 files changed

+29
-7
lines changed

hclsyntax/expression_ops.go

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,13 @@ var (
3838
switch {
3939
// if both are unknown, we don't short circuit anything
4040
case !lhs.IsKnown() && !rhs.IsKnown():
41+
// short-circuit left-to-right when encountering a good unknown
42+
// value and both are unknown.
43+
if !lhsDiags.HasErrors() {
44+
return cty.UnknownVal(cty.Bool).RefineNotNull(), lhsDiags
45+
}
46+
// If the LHS has an error, the RHS might too. Don't
47+
// short-circuit so both diags get collected.
4148
return cty.NilVal, nil
4249

4350
// for ||, a single true is the controlling condition
@@ -46,7 +53,7 @@ var (
4653
case rhs.IsKnown() && rhs.True():
4754
return cty.True, rhsDiags
4855

49-
// if the opposing side is false we can't sort-circuit based on
56+
// if the opposing side is false we can't short-circuit based on
5057
// boolean logic, so an unknown becomes the controlling condition
5158
case !lhs.IsKnown() && rhs.False():
5259
return cty.UnknownVal(cty.Bool).RefineNotNull(), lhsDiags
@@ -62,9 +69,16 @@ var (
6269
Type: cty.Bool,
6370

6471
ShortCircuit: func(lhs, rhs cty.Value, lhsDiags, rhsDiags hcl.Diagnostics) (cty.Value, hcl.Diagnostics) {
72+
6573
switch {
66-
// if both are unknown, we don't short circuit anything
6774
case !lhs.IsKnown() && !rhs.IsKnown():
75+
// short-circuit left-to-right when encountering a good unknown
76+
// value and both are unknown.
77+
if !lhsDiags.HasErrors() {
78+
return cty.UnknownVal(cty.Bool).RefineNotNull(), lhsDiags
79+
}
80+
// If the LHS has an error, the RHS might too. Don't
81+
// short-circuit so both diags get collected.
6882
return cty.NilVal, nil
6983

7084
// For &&, a single false is the controlling condition
@@ -73,14 +87,13 @@ var (
7387
case rhs.IsKnown() && rhs.False():
7488
return cty.False, rhsDiags
7589

76-
// if the opposing side is true we can't sort-circuit based on
90+
// if the opposing side is true we can't short-circuit based on
7791
// boolean logic, so an unknown becomes the controlling condition
7892
case !lhs.IsKnown() && rhs.True():
7993
return cty.UnknownVal(cty.Bool).RefineNotNull(), lhsDiags
8094
case !rhs.IsKnown() && lhs.True():
8195
return cty.UnknownVal(cty.Bool).RefineNotNull(), rhsDiags
8296
}
83-
8497
return cty.NilVal, nil
8598
},
8699
}
@@ -242,9 +255,6 @@ func (e *BinaryOpExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics)
242255
lhsVal, lhsMarks := lhsVal.Unmark()
243256
rhsVal, rhsMarks := rhsVal.Unmark()
244257

245-
// If we short-circuited above and still passed the type-check of RHS then
246-
// we'll halt here and return the short-circuit result rather than actually
247-
// executing the operation.
248258
if e.Op.ShortCircuit != nil {
249259
forceResult, diags := e.Op.ShortCircuit(lhsVal, rhsVal, lhsDiags, rhsDiags)
250260
if forceResult != cty.NilVal {
@@ -256,6 +266,8 @@ func (e *BinaryOpExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics)
256266
}
257267
}
258268

269+
diags = append(diags, lhsDiags...)
270+
diags = append(diags, rhsDiags...)
259271
if diags.HasErrors() {
260272
// Don't actually try the call if we have errors, since the this will
261273
// probably just produce confusing duplicate diagnostics.

hclsyntax/expression_test.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2664,6 +2664,16 @@ func TestExpressionErrorMessages(t *testing.T) {
26642664
"Function calls not allowed",
26652665
`Functions may not be called here.`,
26662666
},
2667+
{
2668+
`map != null || map["key"] == "value"`,
2669+
&hcl.EvalContext{
2670+
Variables: map[string]cty.Value{
2671+
"map": cty.NullVal(cty.Map(cty.String)),
2672+
},
2673+
},
2674+
"Attempt to index null value",
2675+
`This value is null, so it does not have any indices.`,
2676+
},
26672677
}
26682678

26692679
for _, test := range tests {

0 commit comments

Comments
 (0)