diff --git a/zcl/zclsyntax/expression.go b/zcl/zclsyntax/expression.go index bad6fbe..203dd14 100644 --- a/zcl/zclsyntax/expression.go +++ b/zcl/zclsyntax/expression.go @@ -802,7 +802,21 @@ func (e *ForExpr) Value(ctx *zcl.EvalContext) (cty.Value, zcl.Diagnostics) { k := key.AsString() groupVals[k] = append(groupVals[k], val) } else { - vals[key.AsString()] = val + k := key.AsString() + if _, exists := vals[k]; exists { + diags = append(diags, &zcl.Diagnostic{ + Severity: zcl.DiagError, + Summary: "Duplicate object key", + Detail: fmt.Sprintf( + "Two different items produced the key %q in this for expression. If duplicates are expected, use the ellipsis (...) after the value expression to enable grouping by key.", + k, + ), + Subject: e.KeyExpr.Range().Ptr(), + Context: &e.SrcRange, + }) + } else { + vals[key.AsString()] = val + } } } diff --git a/zcl/zclsyntax/expression_test.go b/zcl/zclsyntax/expression_test.go index 73a4149..7b00007 100644 --- a/zcl/zclsyntax/expression_test.go +++ b/zcl/zclsyntax/expression_test.go @@ -529,6 +529,17 @@ upper( }), 0, }, + { + `{for i, v in ["a", "b", "c", "b", "d"]: v => i}`, + nil, + cty.ObjectVal(map[string]cty.Value{ + "a": cty.NumberIntVal(0), + "b": cty.NumberIntVal(1), + "c": cty.NumberIntVal(2), + "d": cty.NumberIntVal(4), + }), + 1, // duplicate key "b" + }, { `[for v in {hello: "world"}: v...]`, nil,