summaryrefslogtreecommitdiff
path: root/ext/typeexpr
diff options
context:
space:
mode:
authorLiam Cervante <liam.cervante@hashicorp.com>2023-08-28 14:04:04 +0200
committerLiam Cervante <liam.cervante@hashicorp.com>2023-08-28 14:04:51 +0200
commit4259e4ba13b9bc9807f95b101c770bfa8b3490e9 (patch)
treeb1efc3a8123f977b5e7ecbcd7ec4cd0c10b8fae7 /ext/typeexpr
parent527ec318631dc6db59da91e8ca7f205fc989ebc5 (diff)
Check for duplicate keys in objects when building types from expressions
Diffstat (limited to 'ext/typeexpr')
-rw-r--r--ext/typeexpr/get_type.go15
-rw-r--r--ext/typeexpr/get_type_test.go27
2 files changed, 40 insertions, 2 deletions
diff --git a/ext/typeexpr/get_type.go b/ext/typeexpr/get_type.go
index 64e3995..8535933 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 7976bb8..d6f4484 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 {