From 4259e4ba13b9bc9807f95b101c770bfa8b3490e9 Mon Sep 17 00:00:00 2001 From: Liam Cervante Date: Mon, 28 Aug 2023 14:04:04 +0200 Subject: [PATCH] Check for duplicate keys in objects when building types from expressions --- ext/typeexpr/get_type.go | 15 ++++++++++++++- ext/typeexpr/get_type_test.go | 27 ++++++++++++++++++++++++++- 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/ext/typeexpr/get_type.go b/ext/typeexpr/get_type.go index 64e3995a..8535933f 100644 --- a/ext/typeexpr/get_type.go +++ b/ext/typeexpr/get_type.go @@ -6,9 +6,10 @@ package typeexpr import ( "fmt" - "github.com/hashicorp/hcl/v2" "github.com/zclconf/go-cty/cty" "github.com/zclconf/go-cty/cty/convert" + + "github.com/hashicorp/hcl/v2" ) const invalidTypeSummary = "Invalid type specification" @@ -179,6 +180,18 @@ func getType(expr hcl.Expression, constraint, withDefaults bool) (cty.Type, *Def }) continue } + + if _, exists := atys[attrName]; exists { + diags = append(diags, &hcl.Diagnostic{ + Severity: hcl.DiagError, + Summary: invalidTypeSummary, + Detail: "Object constructor map keys must be unique.", + Subject: attrDef.Key.Range().Ptr(), + Context: expr.Range().Ptr(), + }) + continue + } + atyExpr := attrDef.Value // the attribute type expression might be wrapped in the special diff --git a/ext/typeexpr/get_type_test.go b/ext/typeexpr/get_type_test.go index 7976bb87..d6f44844 100644 --- a/ext/typeexpr/get_type_test.go +++ b/ext/typeexpr/get_type_test.go @@ -10,10 +10,11 @@ import ( "github.com/hashicorp/hcl/v2/gohcl" "github.com/google/go-cmp/cmp" + "github.com/zclconf/go-cty/cty" + "github.com/hashicorp/hcl/v2" "github.com/hashicorp/hcl/v2/hclsyntax" "github.com/hashicorp/hcl/v2/json" - "github.com/zclconf/go-cty/cty" ) var ( @@ -700,6 +701,30 @@ func TestGetTypeDefaults(t *testing.T) { nil, `Optional attribute modifier expects at most two arguments: the attribute type, and a default value.`, }, + + // Duplicate arguments. + { + `map(object({operations=optional(list(string), []),type=optional(string, "ABC"),type=optional(number)}))`, + &Defaults{ + Type: cty.Map(cty.ObjectWithOptionalAttrs(map[string]cty.Type{ + "operations": cty.List(cty.String), + "type": cty.String, + }, []string{"operations", "type"})), + Children: map[string]*Defaults{ + "": { + Type: cty.ObjectWithOptionalAttrs(map[string]cty.Type{ + "operations": cty.List(cty.String), + "type": cty.String, + }, []string{"operations", "type"}), + DefaultValues: map[string]cty.Value{ + "operations": cty.ListValEmpty(cty.String), + "type": cty.StringVal("ABC"), + }, + }, + }, + }, + "Object constructor map keys must be unique.", + }, } for _, test := range tests {