Skip to content

Commit 5a10955

Browse files
radeksimkodbanck
andauthored
Introduce support for self.* references (#163)
* schema: Introduce DependentBodySelfRef to BlockAddrSchema * Collect self.* LocalAddr for attributes and object blocks * Collect self.* LocalAddr for list blocks * Collect self.* LocalAddr for set blocks * Collect self.* LocalAddr for map blocks * Add SelfRefs flag to BodyExtensions * decoder: Check Extension.SelfRefs when collecting origins * decoder: Split table tests into count/for_each/self & add more SelfRef tests * decoder/reference: Recognize self refs in completion + hover + go-to-* * reference: Ensure Target.Address() renders correct address Since appropriate address now depends on the position of the origin (which is pointing to the target), this change reflects that. Co-authored-by: Daniel Banck <[email protected]>
1 parent 4ad2560 commit 5a10955

18 files changed

+2391
-56
lines changed

decoder/body_extensions_test.go

Lines changed: 592 additions & 10 deletions
Large diffs are not rendered by default.

decoder/candidates.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,9 @@ func (d *PathDecoder) candidatesAtPos(ctx context.Context, body *hclsyntax.Body,
7070

7171
for _, attr := range body.Attributes {
7272
if d.isPosInsideAttrExpr(attr, pos) {
73+
if bodySchema.Extensions != nil && bodySchema.Extensions.SelfRefs {
74+
ctx = schema.WithActiveSelfRefs(ctx)
75+
}
7376
if bodySchema.Extensions != nil && bodySchema.Extensions.Count && attr.Name == "count" {
7477
return d.attrValueCandidatesAtPos(ctx, attr, countAttributeSchema(), outerBodyRng, pos)
7578
}

decoder/expression_candidates.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -472,7 +472,7 @@ func (d *PathDecoder) candidatesForTraversalConstraint(ctx context.Context, tc s
472472
prefix, _ := d.bytesFromRange(prefixRng)
473473

474474
d.pathCtx.ReferenceTargets.MatchWalk(ctx, tc, string(prefix), outermostBodyRng, editRng, func(target reference.Target) error {
475-
address := target.Address(ctx).String()
475+
address := target.Address(ctx, editRng.Start).String()
476476

477477
candidates = append(candidates, lang.Candidate{
478478
Label: address,

decoder/hover.go

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,10 @@ func (d *PathDecoder) hoverAtPos(ctx context.Context, body *hclsyntax.Body, body
6464
for name, attr := range body.Attributes {
6565
if attr.Range().ContainsPos(pos) {
6666
var aSchema *schema.AttributeSchema
67+
if bodySchema.Extensions != nil && bodySchema.Extensions.SelfRefs {
68+
ctx = schema.WithActiveSelfRefs(ctx)
69+
}
70+
6771
if bodySchema.Extensions != nil && bodySchema.Extensions.Count && name == "count" {
6872
aSchema = countAttributeSchema()
6973
} else if bodySchema.Extensions != nil && bodySchema.Extensions.ForEach && name == "for_each" {
@@ -643,14 +647,14 @@ func (d *PathDecoder) hoverContentForTraversalExpr(ctx context.Context, traversa
643647
}
644648

645649
// TODO: Reflect additional found targets here?
646-
return hoverContentForReferenceTarget(ctx, targets[0])
650+
return hoverContentForReferenceTarget(ctx, targets[0], pos)
647651
}
648652

649653
return "", &reference.NoTargetFound{}
650654
}
651655

652-
func hoverContentForReferenceTarget(ctx context.Context, ref reference.Target) (string, error) {
653-
content := fmt.Sprintf("`%s`", ref.Address(ctx))
656+
func hoverContentForReferenceTarget(ctx context.Context, ref reference.Target, pos hcl.Pos) (string, error) {
657+
content := fmt.Sprintf("`%s`", ref.Address(ctx, pos))
654658

655659
var friendlyName string
656660
if ref.Type != cty.NilType {

decoder/reference_origins.go

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,11 @@ func (d *PathDecoder) referenceOriginsInBody(body hcl.Body, bodySchema *schema.B
156156
})
157157
}
158158

159-
origins = append(origins, d.findOriginsInExpression(attr.Expr, aSchema.Expr)...)
159+
allowSelfRefs := false
160+
if bodySchema.Extensions != nil && bodySchema.Extensions.SelfRefs {
161+
allowSelfRefs = true
162+
}
163+
origins = append(origins, d.findOriginsInExpression(attr.Expr, aSchema.Expr, allowSelfRefs)...)
160164
}
161165

162166
for _, block := range content.Blocks {
@@ -180,31 +184,31 @@ func (d *PathDecoder) referenceOriginsInBody(body hcl.Body, bodySchema *schema.B
180184
return origins, impliedOrigins
181185
}
182186

183-
func (d *PathDecoder) findOriginsInExpression(expr hcl.Expression, ec schema.ExprConstraints) reference.Origins {
187+
func (d *PathDecoder) findOriginsInExpression(expr hcl.Expression, ec schema.ExprConstraints, allowSelfRefs bool) reference.Origins {
184188
origins := make(reference.Origins, 0)
185189

186190
switch eType := expr.(type) {
187191
case *hclsyntax.TupleConsExpr:
188192
tce, ok := ExprConstraints(ec).TupleConsExpr()
189193
if ok {
190194
for _, elemExpr := range eType.ExprList() {
191-
origins = append(origins, d.findOriginsInExpression(elemExpr, tce.AnyElem)...)
195+
origins = append(origins, d.findOriginsInExpression(elemExpr, tce.AnyElem, allowSelfRefs)...)
192196
}
193197
break
194198
}
195199

196200
le, ok := ExprConstraints(ec).ListExpr()
197201
if ok {
198202
for _, elemExpr := range eType.ExprList() {
199-
origins = append(origins, d.findOriginsInExpression(elemExpr, le.Elem)...)
203+
origins = append(origins, d.findOriginsInExpression(elemExpr, le.Elem, allowSelfRefs)...)
200204
}
201205
break
202206
}
203207

204208
se, ok := ExprConstraints(ec).SetExpr()
205209
if ok {
206210
for _, elemExpr := range eType.ExprList() {
207-
origins = append(origins, d.findOriginsInExpression(elemExpr, se.Elem)...)
211+
origins = append(origins, d.findOriginsInExpression(elemExpr, se.Elem, allowSelfRefs)...)
208212
}
209213
break
210214
}
@@ -215,7 +219,7 @@ func (d *PathDecoder) findOriginsInExpression(expr hcl.Expression, ec schema.Exp
215219
if len(tue.Elems) < i+1 {
216220
break
217221
}
218-
origins = append(origins, d.findOriginsInExpression(elemExpr, tue.Elems[i])...)
222+
origins = append(origins, d.findOriginsInExpression(elemExpr, tue.Elems[i], allowSelfRefs)...)
219223
}
220224
}
221225
case *hclsyntax.ObjectConsExpr:
@@ -235,14 +239,14 @@ func (d *PathDecoder) findOriginsInExpression(expr hcl.Expression, ec schema.Exp
235239
continue
236240
}
237241

238-
origins = append(origins, d.findOriginsInExpression(item.ValueExpr, attr.Expr)...)
242+
origins = append(origins, d.findOriginsInExpression(item.ValueExpr, attr.Expr, allowSelfRefs)...)
239243
}
240244
}
241245

242246
me, ok := ExprConstraints(ec).MapExpr()
243247
if ok {
244248
for _, item := range eType.Items {
245-
origins = append(origins, d.findOriginsInExpression(item.ValueExpr, me.Elem)...)
249+
origins = append(origins, d.findOriginsInExpression(item.ValueExpr, me.Elem, allowSelfRefs)...)
246250
}
247251
}
248252
case *hclsyntax.AnonSymbolExpr,
@@ -266,7 +270,7 @@ func (d *PathDecoder) findOriginsInExpression(expr hcl.Expression, ec schema.Exp
266270
// see https://github.com/hashicorp/terraform-ls/issues/496
267271
tes, ok := ExprConstraints(ec).TraversalExprs()
268272
if ok {
269-
origins = append(origins, reference.TraversalsToLocalOrigins(expr.Variables(), tes)...)
273+
origins = append(origins, reference.TraversalsToLocalOrigins(expr.Variables(), tes, allowSelfRefs)...)
270274
}
271275
case *hclsyntax.LiteralValueExpr:
272276
// String constant may also be a traversal in some cases, but currently not recognized
@@ -279,7 +283,7 @@ func (d *PathDecoder) findOriginsInExpression(expr hcl.Expression, ec schema.Exp
279283
// This may result in less accurate decoding where even origins
280284
// which do not actually conform to the constraints are recognized.
281285
// TODO: https://github.com/hashicorp/terraform-ls/issues/675
282-
origins = append(origins, reference.TraversalsToLocalOrigins(expr.Variables(), schema.TraversalExprs{})...)
286+
origins = append(origins, reference.TraversalsToLocalOrigins(expr.Variables(), schema.TraversalExprs{}, allowSelfRefs)...)
283287
}
284288

285289
return origins
@@ -297,7 +301,11 @@ func (d *PathDecoder) referenceOriginAtPos(body *hclsyntax.Body, bodySchema *sch
297301
aSchema = bodySchema.AnyAttribute
298302
}
299303

300-
for _, origin := range d.findOriginsInExpression(attr.Expr, aSchema.Expr) {
304+
allowSelfRefs := false
305+
if bodySchema.Extensions != nil && bodySchema.Extensions.SelfRefs {
306+
allowSelfRefs = true
307+
}
308+
for _, origin := range d.findOriginsInExpression(attr.Expr, aSchema.Expr, allowSelfRefs) {
301309
if origin.OriginRange().ContainsPos(pos) {
302310
return &origin, nil
303311
}

decoder/reference_targets.go

Lines changed: 85 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ func (d *PathDecoder) decodeReferenceTargetsForBody(body hcl.Body, parentBlock *
181181

182182
if bSchema.Address.InferBody && bSchema.Body != nil {
183183
bodyRef.NestedTargets = append(bodyRef.NestedTargets,
184-
d.collectInferredReferenceTargetsForBody(addr, bSchema.Address.ScopeId, blk.Body, bSchema.Body)...)
184+
d.collectInferredReferenceTargetsForBody(addr, bSchema.Address, blk.Body, bSchema.Body, nil, lang.Address{})...)
185185
}
186186

187187
bodyRef.Type = bodyToDataType(bSchema.Type, bSchema.Body)
@@ -214,8 +214,17 @@ func (d *PathDecoder) decodeReferenceTargetsForBody(body hcl.Body, parentBlock *
214214
bodyRef.Type = bodyToDataType(bSchema.Type, fullSchema)
215215

216216
if bSchema.Address.InferDependentBody && len(bSchema.DependentBody) > 0 {
217+
if bSchema.Address.DependentBodySelfRef {
218+
bodyRef.LocalAddr = lang.Address{
219+
lang.RootStep{Name: "self"},
220+
}
221+
bodyRef.TargetableFromRangePtr = blk.Range.Ptr()
222+
} else {
223+
bodyRef.LocalAddr = lang.Address{}
224+
}
225+
217226
bodyRef.NestedTargets = append(bodyRef.NestedTargets,
218-
d.collectInferredReferenceTargetsForBody(addr, bSchema.Address.ScopeId, blk.Body, fullSchema)...)
227+
d.collectInferredReferenceTargetsForBody(addr, bSchema.Address, blk.Body, fullSchema, nil, bodyRef.LocalAddr)...)
219228
}
220229

221230
if !bSchema.Address.BodyAsData {
@@ -652,10 +661,15 @@ func bodySchemaAsAttrTypes(bodySchema *schema.BodySchema) map[string]cty.Type {
652661
return attrTypes
653662
}
654663

655-
func (d *PathDecoder) collectInferredReferenceTargetsForBody(addr lang.Address, scopeId lang.ScopeId, body hcl.Body, bodySchema *schema.BodySchema) reference.Targets {
664+
func (d *PathDecoder) collectInferredReferenceTargetsForBody(addr lang.Address, bAddrSchema *schema.BlockAddrSchema, body hcl.Body, bodySchema *schema.BodySchema, selfRefBodyRangePtr *hcl.Range, selfRefAddr lang.Address) reference.Targets {
656665
refs := make(reference.Targets, 0)
657666

658667
content := decodeBody(body, bodySchema)
668+
// We don't get body range for JSON here
669+
// TODO? calculate or implement upstream
670+
if bAddrSchema.DependentBodySelfRef && content.RangePtr != nil && selfRefBodyRangePtr == nil {
671+
selfRefBodyRangePtr = content.RangePtr
672+
}
659673

660674
for name, aSchema := range bodySchema.Attributes {
661675
attrType, ok := exprConstraintToDataType(aSchema.Expr)
@@ -668,12 +682,21 @@ func (d *PathDecoder) collectInferredReferenceTargetsForBody(addr lang.Address,
668682

669683
ref := reference.Target{
670684
Addr: attrAddr,
671-
ScopeId: scopeId,
685+
ScopeId: bAddrSchema.ScopeId,
672686
Type: attrType,
673687
Description: aSchema.Description,
674688
RangePtr: body.MissingItemRange().Ptr(),
675689
}
676690

691+
if bAddrSchema.DependentBodySelfRef && selfRefBodyRangePtr != nil {
692+
localAddr := make(lang.Address, len(selfRefAddr))
693+
copy(localAddr, selfRefAddr)
694+
localAddr = append(localAddr, lang.AttrStep{Name: name})
695+
696+
ref.LocalAddr = localAddr
697+
ref.TargetableFromRangePtr = selfRefBodyRangePtr.Ptr()
698+
}
699+
677700
var attrExpr hcl.Expression
678701
if attr, ok := content.Attributes[name]; ok {
679702
ref.RangePtr = attr.Range.Ptr()
@@ -683,7 +706,7 @@ func (d *PathDecoder) collectInferredReferenceTargetsForBody(addr lang.Address,
683706

684707
if attrExpr != nil && !attrType.IsPrimitiveType() {
685708
ref.NestedTargets = make(reference.Targets, 0)
686-
ref.NestedTargets = append(ref.NestedTargets, decodeReferenceTargetsForComplexTypeExpr(attrAddr, attrExpr, attrType, scopeId)...)
709+
ref.NestedTargets = append(ref.NestedTargets, decodeReferenceTargetsForComplexTypeExpr(attrAddr, attrExpr, attrType, bAddrSchema.ScopeId)...)
687710
}
688711

689712
refs = append(refs, ref)
@@ -700,14 +723,21 @@ func (d *PathDecoder) collectInferredReferenceTargetsForBody(addr lang.Address,
700723

701724
blockRef := reference.Target{
702725
Addr: blockAddr,
703-
ScopeId: scopeId,
726+
LocalAddr: make(lang.Address, len(selfRefAddr)),
727+
ScopeId: bAddrSchema.ScopeId,
704728
Type: cty.Object(bodySchemaAsAttrTypes(bCollection.Schema.Body)),
705729
Description: bCollection.Schema.Description,
706730
DefRangePtr: blk.DefRange.Ptr(),
707731
RangePtr: blk.Range.Ptr(),
708-
NestedTargets: d.collectInferredReferenceTargetsForBody(
709-
blockAddr, scopeId, blk.Body, bCollection.Schema.Body),
710732
}
733+
if bAddrSchema.DependentBodySelfRef && selfRefBodyRangePtr != nil {
734+
copy(blockRef.LocalAddr, selfRefAddr)
735+
blockRef.LocalAddr = append(blockRef.LocalAddr, lang.AttrStep{Name: bType})
736+
blockRef.TargetableFromRangePtr = selfRefBodyRangePtr.Ptr()
737+
}
738+
blockRef.NestedTargets = d.collectInferredReferenceTargetsForBody(
739+
blockAddr, bAddrSchema, blk.Body, bCollection.Schema.Body, selfRefBodyRangePtr, blockRef.LocalAddr)
740+
711741
sort.Sort(blockRef.NestedTargets)
712742
refs = append(refs, blockRef)
713743
}
@@ -719,11 +749,17 @@ func (d *PathDecoder) collectInferredReferenceTargetsForBody(addr lang.Address,
719749

720750
blockRef := reference.Target{
721751
Addr: blockAddr,
722-
ScopeId: scopeId,
752+
LocalAddr: make(lang.Address, len(selfRefAddr)),
753+
ScopeId: bAddrSchema.ScopeId,
723754
Type: cty.List(cty.Object(bodySchemaAsAttrTypes(bCollection.Schema.Body))),
724755
Description: bCollection.Schema.Description,
725756
RangePtr: body.MissingItemRange().Ptr(),
726757
}
758+
if bAddrSchema.DependentBodySelfRef && selfRefBodyRangePtr != nil {
759+
copy(blockRef.LocalAddr, selfRefAddr)
760+
blockRef.LocalAddr = append(blockRef.LocalAddr, lang.AttrStep{Name: bType})
761+
blockRef.TargetableFromRangePtr = selfRefBodyRangePtr.Ptr()
762+
}
727763

728764
for i, b := range bCollection.Blocks {
729765
elemAddr := make(lang.Address, len(blockAddr))
@@ -734,14 +770,25 @@ func (d *PathDecoder) collectInferredReferenceTargetsForBody(addr lang.Address,
734770

735771
elemRef := reference.Target{
736772
Addr: elemAddr,
737-
ScopeId: scopeId,
773+
LocalAddr: make(lang.Address, len(blockRef.LocalAddr)),
774+
ScopeId: bAddrSchema.ScopeId,
738775
Type: cty.Object(bodySchemaAsAttrTypes(bCollection.Schema.Body)),
739776
Description: bCollection.Schema.Description,
740777
DefRangePtr: b.DefRange.Ptr(),
741778
RangePtr: b.Range.Ptr(),
742-
NestedTargets: d.collectInferredReferenceTargetsForBody(
743-
elemAddr, scopeId, b.Body, bCollection.Schema.Body),
744779
}
780+
781+
if bAddrSchema.DependentBodySelfRef && selfRefBodyRangePtr != nil {
782+
copy(elemRef.LocalAddr, blockRef.LocalAddr)
783+
elemRef.LocalAddr = append(elemRef.LocalAddr, lang.IndexStep{
784+
Key: cty.NumberIntVal(int64(i)),
785+
})
786+
elemRef.TargetableFromRangePtr = selfRefBodyRangePtr.Ptr()
787+
}
788+
789+
elemRef.NestedTargets = d.collectInferredReferenceTargetsForBody(
790+
elemAddr, bAddrSchema, b.Body, bCollection.Schema.Body, selfRefBodyRangePtr, elemRef.LocalAddr)
791+
745792
sort.Sort(elemRef.NestedTargets)
746793
blockRef.NestedTargets = append(blockRef.NestedTargets, elemRef)
747794

@@ -771,11 +818,17 @@ func (d *PathDecoder) collectInferredReferenceTargetsForBody(addr lang.Address,
771818

772819
blockRef := reference.Target{
773820
Addr: blockAddr,
774-
ScopeId: scopeId,
821+
LocalAddr: make(lang.Address, len(selfRefAddr)),
822+
ScopeId: bAddrSchema.ScopeId,
775823
Type: cty.Set(cty.Object(bodySchemaAsAttrTypes(bCollection.Schema.Body))),
776824
Description: bCollection.Schema.Description,
777825
RangePtr: body.MissingItemRange().Ptr(),
778826
}
827+
if bAddrSchema.DependentBodySelfRef && selfRefBodyRangePtr != nil {
828+
copy(blockRef.LocalAddr, selfRefAddr)
829+
blockRef.LocalAddr = append(blockRef.LocalAddr, lang.AttrStep{Name: bType})
830+
blockRef.TargetableFromRangePtr = selfRefBodyRangePtr.Ptr()
831+
}
779832

780833
for i, b := range bCollection.Blocks {
781834
if i == 0 {
@@ -803,11 +856,17 @@ func (d *PathDecoder) collectInferredReferenceTargetsForBody(addr lang.Address,
803856

804857
blockRef := reference.Target{
805858
Addr: blockAddr,
806-
ScopeId: scopeId,
859+
LocalAddr: make(lang.Address, len(selfRefAddr)),
860+
ScopeId: bAddrSchema.ScopeId,
807861
Type: cty.Map(cty.Object(bodySchemaAsAttrTypes(bCollection.Schema.Body))),
808862
Description: bCollection.Schema.Description,
809863
RangePtr: body.MissingItemRange().Ptr(),
810864
}
865+
if bAddrSchema.DependentBodySelfRef && selfRefBodyRangePtr != nil {
866+
copy(blockRef.LocalAddr, selfRefAddr)
867+
blockRef.LocalAddr = append(blockRef.LocalAddr, lang.AttrStep{Name: bType})
868+
blockRef.TargetableFromRangePtr = selfRefBodyRangePtr.Ptr()
869+
}
811870

812871
for i, b := range bCollection.Blocks {
813872
elemAddr := make(lang.Address, len(blockAddr))
@@ -820,14 +879,23 @@ func (d *PathDecoder) collectInferredReferenceTargetsForBody(addr lang.Address,
820879

821880
elemRef := reference.Target{
822881
Addr: elemAddr,
823-
ScopeId: scopeId,
882+
LocalAddr: make(lang.Address, len(blockRef.LocalAddr)),
883+
ScopeId: bAddrSchema.ScopeId,
824884
Type: refType,
825885
Description: bCollection.Schema.Description,
826886
RangePtr: b.Range.Ptr(),
827887
DefRangePtr: b.DefRange.Ptr(),
828-
NestedTargets: d.collectInferredReferenceTargetsForBody(
829-
elemAddr, scopeId, b.Body, bCollection.Schema.Body),
830888
}
889+
if bAddrSchema.DependentBodySelfRef && selfRefBodyRangePtr != nil {
890+
copy(elemRef.LocalAddr, blockRef.LocalAddr)
891+
elemRef.LocalAddr = append(elemRef.LocalAddr, lang.IndexStep{
892+
Key: cty.StringVal(b.Labels[0]),
893+
})
894+
elemRef.TargetableFromRangePtr = selfRefBodyRangePtr.Ptr()
895+
}
896+
897+
elemRef.NestedTargets = d.collectInferredReferenceTargetsForBody(
898+
elemAddr, bAddrSchema, b.Body, bCollection.Schema.Body, selfRefBodyRangePtr, elemRef.LocalAddr)
831899
sort.Sort(elemRef.NestedTargets)
832900
blockRef.NestedTargets = append(blockRef.NestedTargets, elemRef)
833901

0 commit comments

Comments
 (0)