Skip to content

Commit

Permalink
Fix for expression collection constraints (#375)
Browse files Browse the repository at this point in the history
* Update contraints for `for` collection expr

* Add test case for `for` in `for_each` attr

* Fix existing `for` tests
  • Loading branch information
dbanck authored Feb 20, 2024
1 parent b96e56c commit 6ff0526
Show file tree
Hide file tree
Showing 3 changed files with 136 additions and 5 deletions.
10 changes: 9 additions & 1 deletion decoder/expr_any_for.go
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,15 @@ func (a Any) refOriginsForForExpr(ctx context.Context, allowSelfRefs bool) (refe
// TODO: eType.KeyVarExpr.Range() to collect key as origin
// TODO: eType.ValVarExpr.Range() to collect value as origin

if collExpr, ok := newExpression(a.pathCtx, eType.CollExpr, a.cons).(ReferenceOriginsExpression); ok {
// A for expression's input can be a list, a set, a tuple, a map, or an object
collCons := schema.OneOf{
schema.AnyExpression{OfType: cty.List(cty.DynamicPseudoType)},
schema.AnyExpression{OfType: cty.Set(cty.DynamicPseudoType)},
schema.AnyExpression{OfType: cty.EmptyTuple},
schema.AnyExpression{OfType: cty.Map(cty.DynamicPseudoType)},
schema.AnyExpression{OfType: cty.EmptyObject},
}
if collExpr, ok := newExpression(a.pathCtx, eType.CollExpr, collCons).(ReferenceOriginsExpression); ok {
origins = append(origins, collExpr.ReferenceOrigins(ctx, allowSelfRefs)...)
}

Expand Down
129 changes: 126 additions & 3 deletions decoder/expr_any_ref_origins_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1049,7 +1049,11 @@ func TestCollectRefOrigins_exprAny_forExpr_hcl(t *testing.T) {
End: hcl.Pos{Line: 1, Column: 33, Byte: 32},
},
Constraints: reference.OriginConstraints{
{OfType: cty.List(cty.String)},
{OfType: cty.List(cty.DynamicPseudoType)},
{OfType: cty.Set(cty.DynamicPseudoType)},
{OfType: cty.EmptyTuple},
{OfType: cty.Map(cty.DynamicPseudoType)},
{OfType: cty.EmptyObject},
},
},
reference.LocalOrigin{
Expand Down Expand Up @@ -1090,7 +1094,11 @@ func TestCollectRefOrigins_exprAny_forExpr_hcl(t *testing.T) {
End: hcl.Pos{Line: 1, Column: 33, Byte: 32},
},
Constraints: reference.OriginConstraints{
{OfType: cty.Set(cty.String)},
{OfType: cty.List(cty.DynamicPseudoType)},
{OfType: cty.Set(cty.DynamicPseudoType)},
{OfType: cty.EmptyTuple},
{OfType: cty.Map(cty.DynamicPseudoType)},
{OfType: cty.EmptyObject},
},
},
reference.LocalOrigin{
Expand Down Expand Up @@ -1131,7 +1139,11 @@ func TestCollectRefOrigins_exprAny_forExpr_hcl(t *testing.T) {
End: hcl.Pos{Line: 1, Column: 33, Byte: 32},
},
Constraints: reference.OriginConstraints{
{OfType: cty.List(cty.DynamicPseudoType)},
{OfType: cty.Set(cty.DynamicPseudoType)},
{OfType: cty.EmptyTuple},
{OfType: cty.Map(cty.DynamicPseudoType)},
{OfType: cty.EmptyObject},
},
},
reference.LocalOrigin{
Expand Down Expand Up @@ -1172,7 +1184,11 @@ func TestCollectRefOrigins_exprAny_forExpr_hcl(t *testing.T) {
End: hcl.Pos{Line: 1, Column: 33, Byte: 32},
},
Constraints: reference.OriginConstraints{
{OfType: cty.Map(cty.String)},
{OfType: cty.List(cty.DynamicPseudoType)},
{OfType: cty.Set(cty.DynamicPseudoType)},
{OfType: cty.EmptyTuple},
{OfType: cty.Map(cty.DynamicPseudoType)},
{OfType: cty.EmptyObject},
},
},
reference.LocalOrigin{
Expand Down Expand Up @@ -1226,6 +1242,10 @@ func TestCollectRefOrigins_exprAny_forExpr_hcl(t *testing.T) {
End: hcl.Pos{Line: 1, Column: 33, Byte: 32},
},
Constraints: reference.OriginConstraints{
{OfType: cty.List(cty.DynamicPseudoType)},
{OfType: cty.Set(cty.DynamicPseudoType)},
{OfType: cty.EmptyTuple},
{OfType: cty.Map(cty.DynamicPseudoType)},
{OfType: cty.EmptyObject},
},
},
Expand Down Expand Up @@ -1288,6 +1308,109 @@ func TestCollectRefOrigins_exprAny_forExpr_hcl(t *testing.T) {
}
}

func TestCollectRefOrigins_exprAny_forExpr_forEach(t *testing.T) {
bodySchema := &schema.BodySchema{
Blocks: map[string]*schema.BlockSchema{
"resource": {
Labels: []*schema.LabelSchema{
{Name: "type"}, {Name: "name"},
},
Body: &schema.BodySchema{
Extensions: &schema.BodyExtensions{
ForEach: true,
},
},
},
},
}

cfg := `resource "aws_instance" "foo" {
for_each = { for i in var.coll : i.name => i }
}`

f, diags := hclsyntax.ParseConfig([]byte(cfg), "test.tf", hcl.InitialPos)
if len(diags) > 0 {
t.Error(diags)
}
d := testPathDecoder(t, &PathContext{
Schema: bodySchema,
Files: map[string]*hcl.File{
"test.tf": f,
},
})

origins, err := d.CollectReferenceOrigins()
if err != nil {
t.Fatal(err)
}

expectedRefOrigins := reference.Origins{
reference.LocalOrigin{
Addr: lang.Address{
lang.RootStep{Name: "var"},
lang.AttrStep{Name: "coll"},
},
Range: hcl.Range{
Filename: "test.tf",
Start: hcl.Pos{Line: 2, Column: 25, Byte: 56},
End: hcl.Pos{Line: 2, Column: 33, Byte: 64},
},
Constraints: reference.OriginConstraints{
{OfType: cty.List(cty.DynamicPseudoType)},
{OfType: cty.Set(cty.DynamicPseudoType)},
{OfType: cty.EmptyTuple},
{OfType: cty.Map(cty.DynamicPseudoType)},
{OfType: cty.EmptyObject},
{OfType: cty.List(cty.DynamicPseudoType)},
{OfType: cty.Set(cty.DynamicPseudoType)},
{OfType: cty.EmptyTuple},
{OfType: cty.Map(cty.DynamicPseudoType)},
{OfType: cty.EmptyObject},
{OfType: cty.List(cty.DynamicPseudoType)},
{OfType: cty.Set(cty.DynamicPseudoType)},
{OfType: cty.EmptyTuple},
{OfType: cty.Map(cty.DynamicPseudoType)},
{OfType: cty.EmptyObject},
},
},
reference.LocalOrigin{
Addr: lang.Address{
lang.RootStep{Name: "i"},
lang.AttrStep{Name: "name"},
},
Range: hcl.Range{
Filename: "test.tf",
Start: hcl.Pos{Line: 2, Column: 36, Byte: 67},
End: hcl.Pos{Line: 2, Column: 42, Byte: 73},
},
Constraints: reference.OriginConstraints{
{OfType: cty.String},
{OfType: cty.String},
{OfType: cty.String},
},
},
reference.LocalOrigin{
Addr: lang.Address{
lang.RootStep{Name: "i"},
},
Range: hcl.Range{
Filename: "test.tf",
Start: hcl.Pos{Line: 2, Column: 46, Byte: 77},
End: hcl.Pos{Line: 2, Column: 47, Byte: 78},
},
Constraints: reference.OriginConstraints{
{OfType: cty.DynamicPseudoType},
{OfType: cty.String},
{OfType: cty.DynamicPseudoType},
},
},
}

if diff := cmp.Diff(expectedRefOrigins, origins, ctydebug.CmpOptions); diff != "" {
t.Fatalf("unexpected origins: %s", diff)
}
}

func TestCollectRefOrigins_exprAny_operators_json(t *testing.T) {
testCases := []struct {
testName string
Expand Down
2 changes: 1 addition & 1 deletion decoder/expr_one_of_ref_origins.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ func appendOrigins(origins, newOrigins reference.Origins) reference.Origins {
if ok &&
existingOrigin.Address().Equals(newMatchableOrigin.Address()) &&
rangesEqual(existingOrigin.OriginRange(), newMatchableOrigin.OriginRange()) {

// TODO? deduplicate constraints
origins[i] = existingOrigin.AppendConstraints(newMatchableOrigin.OriginConstraints())
foundMatch = true
break
Expand Down

0 comments on commit 6ff0526

Please sign in to comment.