Skip to content

Commit

Permalink
Fix target collection from for-expressions (#434)
Browse files Browse the repository at this point in the history
* Enable collection of object targets from for-expressions

* Enable collection of tuple targets from for-expressions
  • Loading branch information
dbanck authored Dec 9, 2024
1 parent e51d983 commit 4f7c1c9
Show file tree
Hide file tree
Showing 4 changed files with 140 additions and 0 deletions.
26 changes: 26 additions & 0 deletions decoder/expr_object_ref_targets.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,32 @@ func (obj Object) ReferenceTargets(ctx context.Context, targetCtx *TargetContext
return obj.wholeObjectReferenceTargets(targetCtx, attrTargets)
}

// An object can be the result of a for-expression, so we explicitly check for
// this before trying to convert it to a static map
//
// While we can't evaluate the whole for-expression yet, we can still
// collect a target for the whole object with a dynamic type
if _, ok := obj.expr.(*hclsyntax.ForExpr); ok && targetCtx != nil {
var rangePtr *hcl.Range
if targetCtx.ParentRangePtr != nil {
rangePtr = targetCtx.ParentRangePtr
} else {
rangePtr = obj.expr.Range().Ptr()
}

return reference.Targets{
{
Addr: targetCtx.ParentAddress,
LocalAddr: targetCtx.ParentLocalAddress,
TargetableFromRangePtr: targetCtx.TargetableFromRangePtr,
ScopeId: targetCtx.ScopeId,
RangePtr: rangePtr,
DefRangePtr: targetCtx.ParentDefRangePtr,
Type: cty.DynamicPseudoType,
},
}
}

items, diags := hcl.ExprMap(obj.expr)
if diags.HasErrors() {
return reference.Targets{}
Expand Down
45 changes: 45 additions & 0 deletions decoder/expr_object_ref_targets_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -620,6 +620,51 @@ func TestCollectRefTargets_exprObject_hcl(t *testing.T) {
},
},
},
{
"object from for-expression",
map[string]*schema.AttributeSchema{
"attr": {
Constraint: schema.Object{
Attributes: schema.ObjectAttributes{
"foo": {
Constraint: schema.LiteralType{
Type: cty.String,
},
IsOptional: true,
},
},
},
IsOptional: true,
Address: &schema.AttributeAddrSchema{
Steps: schema.Address{
schema.AttrNameStep{},
},
ScopeId: lang.ScopeId("test"),
AsExprType: true,
},
},
},
`attr = { for s in ["a", "b"] : s => "s${s}" }`,
reference.Targets{
{
Addr: lang.Address{
lang.RootStep{Name: "attr"},
},
ScopeId: lang.ScopeId("test"),
RangePtr: &hcl.Range{
Filename: "test.hcl",
Start: hcl.Pos{Line: 1, Column: 1, Byte: 0},
End: hcl.Pos{Line: 1, Column: 46, Byte: 45},
},
DefRangePtr: &hcl.Range{
Filename: "test.hcl",
Start: hcl.Pos{Line: 1, Column: 1, Byte: 0},
End: hcl.Pos{Line: 1, Column: 5, Byte: 4},
},
Type: cty.DynamicPseudoType,
},
},
},
}
for i, tc := range testCases {
t.Run(fmt.Sprintf("%d-%s", i, tc.testName), func(t *testing.T) {
Expand Down
27 changes: 27 additions & 0 deletions decoder/expr_tuple_ref_targets.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"github.com/hashicorp/hcl-lang/lang"
"github.com/hashicorp/hcl-lang/reference"
"github.com/hashicorp/hcl/v2"
"github.com/hashicorp/hcl/v2/hclsyntax"
"github.com/zclconf/go-cty/cty"
)

Expand All @@ -17,6 +18,32 @@ func (tuple Tuple) ReferenceTargets(ctx context.Context, targetCtx *TargetContex
return tuple.wholeTupleReferenceTargets(targetCtx, tuple.collectTupleElemTargets(ctx, targetCtx, []hcl.Expression{}))
}

// A tuple can be the result of a for-expression, so we explicitly check for
// this before trying to convert it to a static list
//
// While we can't evaluate the whole for-expression yet, we can still
// collect a target for the whole tuple with a dynamic type
if _, ok := tuple.expr.(*hclsyntax.ForExpr); ok && targetCtx != nil {
var rangePtr *hcl.Range
if targetCtx.ParentRangePtr != nil {
rangePtr = targetCtx.ParentRangePtr
} else {
rangePtr = tuple.expr.Range().Ptr()
}

return reference.Targets{
{
Addr: targetCtx.ParentAddress,
LocalAddr: targetCtx.ParentLocalAddress,
TargetableFromRangePtr: targetCtx.TargetableFromRangePtr,
ScopeId: targetCtx.ScopeId,
RangePtr: rangePtr,
DefRangePtr: targetCtx.ParentDefRangePtr,
Type: cty.DynamicPseudoType,
},
}
}

elems, diags := hcl.ExprList(tuple.expr)
if diags.HasErrors() {
return reference.Targets{}
Expand Down
42 changes: 42 additions & 0 deletions decoder/expr_tuple_ref_targets_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -452,6 +452,48 @@ func TestCollectRefTargets_exprTuple_hcl(t *testing.T) {
},
},
},
{
"tuple from for-expression",
map[string]*schema.AttributeSchema{
"attr": {
Constraint: schema.Tuple{
Elems: []schema.Constraint{
schema.LiteralType{
Type: cty.String,
},
},
},
IsOptional: true,
Address: &schema.AttributeAddrSchema{
Steps: schema.Address{
schema.AttrNameStep{},
},
ScopeId: lang.ScopeId("test"),
AsExprType: true,
},
},
},
`attr = [for sa in ["one", "two"]: sa]`,
reference.Targets{
{
Addr: lang.Address{
lang.RootStep{Name: "attr"},
},
RangePtr: &hcl.Range{
Filename: "test.hcl",
Start: hcl.Pos{Line: 1, Column: 1, Byte: 0},
End: hcl.Pos{Line: 1, Column: 38, Byte: 37},
},
DefRangePtr: &hcl.Range{
Filename: "test.hcl",
Start: hcl.Pos{Line: 1, Column: 1, Byte: 0},
End: hcl.Pos{Line: 1, Column: 5, Byte: 4},
},
ScopeId: lang.ScopeId("test"),
Type: cty.DynamicPseudoType,
},
},
},
}
for i, tc := range testCases {
t.Run(fmt.Sprintf("%d-%s", i, tc.testName), func(t *testing.T) {
Expand Down

0 comments on commit 4f7c1c9

Please sign in to comment.