From 861df7a9de016533814099da25afd58d9cde961c Mon Sep 17 00:00:00 2001 From: Radek Simko Date: Tue, 21 Feb 2023 10:58:17 +0000 Subject: [PATCH] decoder: Implement reference targets for Object --- decoder/expr_object.go | 8 --- decoder/expr_object_ref_targets.go | 103 +++++++++++++++++++++++++++++ 2 files changed, 103 insertions(+), 8 deletions(-) create mode 100644 decoder/expr_object_ref_targets.go diff --git a/decoder/expr_object.go b/decoder/expr_object.go index 0ff506dd..ee39ff64 100644 --- a/decoder/expr_object.go +++ b/decoder/expr_object.go @@ -1,9 +1,6 @@ package decoder import ( - "context" - - "github.com/hashicorp/hcl-lang/reference" "github.com/hashicorp/hcl-lang/schema" "github.com/hashicorp/hcl/v2" ) @@ -13,8 +10,3 @@ type Object struct { cons schema.Object pathCtx *PathContext } - -func (obj Object) ReferenceTargets(ctx context.Context, targetCtx *TargetContext) reference.Targets { - // TODO - return nil -} diff --git a/decoder/expr_object_ref_targets.go b/decoder/expr_object_ref_targets.go new file mode 100644 index 00000000..c1a6f06b --- /dev/null +++ b/decoder/expr_object_ref_targets.go @@ -0,0 +1,103 @@ +package decoder + +import ( + "context" + + "github.com/hashicorp/hcl-lang/lang" + "github.com/hashicorp/hcl-lang/reference" + "github.com/hashicorp/hcl/v2/hclsyntax" + "github.com/zclconf/go-cty/cty" +) + +func (obj Object) ReferenceTargets(ctx context.Context, targetCtx *TargetContext) reference.Targets { + eType, ok := obj.expr.(*hclsyntax.ObjectConsExpr) + if !ok { + return reference.Targets{} + } + + if len(eType.Items) == 0 || len(obj.cons.Attributes) == 0 { + return reference.Targets{} + } + + attrTargets := make(reference.Targets, 0) + + for _, item := range eType.Items { + keyName, _, ok := rawObjectKey(item.KeyExpr) + if !ok { + // avoid collecting item w/ invalid key + continue + } + + aSchema, ok := obj.cons.Attributes[keyName] + if !ok { + // avoid collecting for unknown attribute + continue + } + + expr := newExpression(obj.pathCtx, item.ValueExpr, aSchema.Constraint) + if e, ok := expr.(ReferenceTargetsExpression); ok { + if targetCtx == nil { + // collect any targets inside the expression + // if attribute itself isn't targetable + attrTargets = append(attrTargets, e.ReferenceTargets(ctx, nil)...) + continue + } + + elemCtx := targetCtx.Copy() + elemCtx.ParentAddress = append(elemCtx.ParentAddress, lang.IndexStep{ + Key: cty.StringVal(keyName), + }) + if elemCtx.ParentLocalAddress != nil { + elemCtx.ParentLocalAddress = append(elemCtx.ParentLocalAddress, lang.IndexStep{ + Key: cty.StringVal(keyName), + }) + } + + attrTargets = append(attrTargets, e.ReferenceTargets(ctx, elemCtx)...) + } + } + + // TODO: targets for undeclared attributes w/out range + + targets := make(reference.Targets, 0) + + if targetCtx != nil { + // collect target for the whole object + + // type-aware + if targetCtx.AsExprType { + objType, ok := obj.cons.ConstraintType() + if ok { + targets = append(targets, reference.Target{ + Addr: targetCtx.ParentAddress, + Name: targetCtx.FriendlyName, + Type: objType, + ScopeId: targetCtx.ScopeId, + RangePtr: obj.expr.Range().Ptr(), + NestedTargets: attrTargets, + LocalAddr: targetCtx.ParentLocalAddress, + TargetableFromRangePtr: targetCtx.TargetableFromRangePtr, + }) + } + } + + // type-unaware + if targetCtx.AsReference { + targets = append(targets, reference.Target{ + Addr: targetCtx.ParentAddress, + Name: targetCtx.FriendlyName, + ScopeId: targetCtx.ScopeId, + RangePtr: obj.expr.Range().Ptr(), + NestedTargets: attrTargets, + LocalAddr: targetCtx.ParentLocalAddress, + TargetableFromRangePtr: targetCtx.TargetableFromRangePtr, + }) + } + } else { + // treat element targets as 1st class ones + // if the object itself isn't targetable + targets = attrTargets + } + + return targets +}