Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce support for self.* references #163

Merged
merged 10 commits into from
Nov 24, 2022
602 changes: 592 additions & 10 deletions decoder/body_extensions_test.go

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions decoder/candidates.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@ func (d *PathDecoder) candidatesAtPos(ctx context.Context, body *hclsyntax.Body,

for _, attr := range body.Attributes {
if d.isPosInsideAttrExpr(attr, pos) {
if bodySchema.Extensions != nil && bodySchema.Extensions.SelfRefs {
ctx = schema.WithActiveSelfRefs(ctx)
}
if bodySchema.Extensions != nil && bodySchema.Extensions.Count && attr.Name == "count" {
return d.attrValueCandidatesAtPos(ctx, attr, countAttributeSchema(), outerBodyRng, pos)
}
Expand Down
2 changes: 1 addition & 1 deletion decoder/expression_candidates.go
Original file line number Diff line number Diff line change
Expand Up @@ -472,7 +472,7 @@ func (d *PathDecoder) candidatesForTraversalConstraint(ctx context.Context, tc s
prefix, _ := d.bytesFromRange(prefixRng)

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

candidates = append(candidates, lang.Candidate{
Label: address,
Expand Down
10 changes: 7 additions & 3 deletions decoder/hover.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@ func (d *PathDecoder) hoverAtPos(ctx context.Context, body *hclsyntax.Body, body
for name, attr := range body.Attributes {
if attr.Range().ContainsPos(pos) {
var aSchema *schema.AttributeSchema
if bodySchema.Extensions != nil && bodySchema.Extensions.SelfRefs {
ctx = schema.WithActiveSelfRefs(ctx)
}

if bodySchema.Extensions != nil && bodySchema.Extensions.Count && name == "count" {
aSchema = countAttributeSchema()
} else if bodySchema.Extensions != nil && bodySchema.Extensions.ForEach && name == "for_each" {
Expand Down Expand Up @@ -643,14 +647,14 @@ func (d *PathDecoder) hoverContentForTraversalExpr(ctx context.Context, traversa
}

// TODO: Reflect additional found targets here?
return hoverContentForReferenceTarget(ctx, targets[0])
return hoverContentForReferenceTarget(ctx, targets[0], pos)
}

return "", &reference.NoTargetFound{}
}

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

var friendlyName string
if ref.Type != cty.NilType {
Expand Down
30 changes: 19 additions & 11 deletions decoder/reference_origins.go
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,11 @@ func (d *PathDecoder) referenceOriginsInBody(body hcl.Body, bodySchema *schema.B
})
}

origins = append(origins, d.findOriginsInExpression(attr.Expr, aSchema.Expr)...)
allowSelfRefs := false
if bodySchema.Extensions != nil && bodySchema.Extensions.SelfRefs {
allowSelfRefs = true
}
origins = append(origins, d.findOriginsInExpression(attr.Expr, aSchema.Expr, allowSelfRefs)...)
}

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

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

switch eType := expr.(type) {
case *hclsyntax.TupleConsExpr:
tce, ok := ExprConstraints(ec).TupleConsExpr()
if ok {
for _, elemExpr := range eType.ExprList() {
origins = append(origins, d.findOriginsInExpression(elemExpr, tce.AnyElem)...)
origins = append(origins, d.findOriginsInExpression(elemExpr, tce.AnyElem, allowSelfRefs)...)
}
break
}

le, ok := ExprConstraints(ec).ListExpr()
if ok {
for _, elemExpr := range eType.ExprList() {
origins = append(origins, d.findOriginsInExpression(elemExpr, le.Elem)...)
origins = append(origins, d.findOriginsInExpression(elemExpr, le.Elem, allowSelfRefs)...)
}
break
}

se, ok := ExprConstraints(ec).SetExpr()
if ok {
for _, elemExpr := range eType.ExprList() {
origins = append(origins, d.findOriginsInExpression(elemExpr, se.Elem)...)
origins = append(origins, d.findOriginsInExpression(elemExpr, se.Elem, allowSelfRefs)...)
}
break
}
Expand All @@ -215,7 +219,7 @@ func (d *PathDecoder) findOriginsInExpression(expr hcl.Expression, ec schema.Exp
if len(tue.Elems) < i+1 {
break
}
origins = append(origins, d.findOriginsInExpression(elemExpr, tue.Elems[i])...)
origins = append(origins, d.findOriginsInExpression(elemExpr, tue.Elems[i], allowSelfRefs)...)
}
}
case *hclsyntax.ObjectConsExpr:
Expand All @@ -235,14 +239,14 @@ func (d *PathDecoder) findOriginsInExpression(expr hcl.Expression, ec schema.Exp
continue
}

origins = append(origins, d.findOriginsInExpression(item.ValueExpr, attr.Expr)...)
origins = append(origins, d.findOriginsInExpression(item.ValueExpr, attr.Expr, allowSelfRefs)...)
}
}

me, ok := ExprConstraints(ec).MapExpr()
if ok {
for _, item := range eType.Items {
origins = append(origins, d.findOriginsInExpression(item.ValueExpr, me.Elem)...)
origins = append(origins, d.findOriginsInExpression(item.ValueExpr, me.Elem, allowSelfRefs)...)
}
}
case *hclsyntax.AnonSymbolExpr,
Expand All @@ -266,7 +270,7 @@ func (d *PathDecoder) findOriginsInExpression(expr hcl.Expression, ec schema.Exp
// see https://github.com/hashicorp/terraform-ls/issues/496
tes, ok := ExprConstraints(ec).TraversalExprs()
if ok {
origins = append(origins, reference.TraversalsToLocalOrigins(expr.Variables(), tes)...)
origins = append(origins, reference.TraversalsToLocalOrigins(expr.Variables(), tes, allowSelfRefs)...)
}
case *hclsyntax.LiteralValueExpr:
// String constant may also be a traversal in some cases, but currently not recognized
Expand All @@ -279,7 +283,7 @@ func (d *PathDecoder) findOriginsInExpression(expr hcl.Expression, ec schema.Exp
// This may result in less accurate decoding where even origins
// which do not actually conform to the constraints are recognized.
// TODO: https://github.com/hashicorp/terraform-ls/issues/675
origins = append(origins, reference.TraversalsToLocalOrigins(expr.Variables(), schema.TraversalExprs{})...)
origins = append(origins, reference.TraversalsToLocalOrigins(expr.Variables(), schema.TraversalExprs{}, allowSelfRefs)...)
}

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

for _, origin := range d.findOriginsInExpression(attr.Expr, aSchema.Expr) {
allowSelfRefs := false
if bodySchema.Extensions != nil && bodySchema.Extensions.SelfRefs {
allowSelfRefs = true
}
for _, origin := range d.findOriginsInExpression(attr.Expr, aSchema.Expr, allowSelfRefs) {
if origin.OriginRange().ContainsPos(pos) {
return &origin, nil
}
Expand Down
102 changes: 85 additions & 17 deletions decoder/reference_targets.go
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ func (d *PathDecoder) decodeReferenceTargetsForBody(body hcl.Body, parentBlock *

if bSchema.Address.InferBody && bSchema.Body != nil {
bodyRef.NestedTargets = append(bodyRef.NestedTargets,
d.collectInferredReferenceTargetsForBody(addr, bSchema.Address.ScopeId, blk.Body, bSchema.Body)...)
d.collectInferredReferenceTargetsForBody(addr, bSchema.Address, blk.Body, bSchema.Body, nil, lang.Address{})...)
}

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

if bSchema.Address.InferDependentBody && len(bSchema.DependentBody) > 0 {
if bSchema.Address.DependentBodySelfRef {
bodyRef.LocalAddr = lang.Address{
lang.RootStep{Name: "self"},
}
bodyRef.TargetableFromRangePtr = blk.Range.Ptr()
} else {
bodyRef.LocalAddr = lang.Address{}
}

bodyRef.NestedTargets = append(bodyRef.NestedTargets,
d.collectInferredReferenceTargetsForBody(addr, bSchema.Address.ScopeId, blk.Body, fullSchema)...)
d.collectInferredReferenceTargetsForBody(addr, bSchema.Address, blk.Body, fullSchema, nil, bodyRef.LocalAddr)...)
}

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

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

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

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

ref := reference.Target{
Addr: attrAddr,
ScopeId: scopeId,
ScopeId: bAddrSchema.ScopeId,
Type: attrType,
Description: aSchema.Description,
RangePtr: body.MissingItemRange().Ptr(),
}

if bAddrSchema.DependentBodySelfRef && selfRefBodyRangePtr != nil {
localAddr := make(lang.Address, len(selfRefAddr))
copy(localAddr, selfRefAddr)
localAddr = append(localAddr, lang.AttrStep{Name: name})

ref.LocalAddr = localAddr
ref.TargetableFromRangePtr = selfRefBodyRangePtr.Ptr()
}

var attrExpr hcl.Expression
if attr, ok := content.Attributes[name]; ok {
ref.RangePtr = attr.Range.Ptr()
Expand All @@ -683,7 +706,7 @@ func (d *PathDecoder) collectInferredReferenceTargetsForBody(addr lang.Address,

if attrExpr != nil && !attrType.IsPrimitiveType() {
ref.NestedTargets = make(reference.Targets, 0)
ref.NestedTargets = append(ref.NestedTargets, decodeReferenceTargetsForComplexTypeExpr(attrAddr, attrExpr, attrType, scopeId)...)
ref.NestedTargets = append(ref.NestedTargets, decodeReferenceTargetsForComplexTypeExpr(attrAddr, attrExpr, attrType, bAddrSchema.ScopeId)...)
}

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

blockRef := reference.Target{
Addr: blockAddr,
ScopeId: scopeId,
LocalAddr: make(lang.Address, len(selfRefAddr)),
ScopeId: bAddrSchema.ScopeId,
Type: cty.Object(bodySchemaAsAttrTypes(bCollection.Schema.Body)),
Description: bCollection.Schema.Description,
DefRangePtr: blk.DefRange.Ptr(),
RangePtr: blk.Range.Ptr(),
NestedTargets: d.collectInferredReferenceTargetsForBody(
blockAddr, scopeId, blk.Body, bCollection.Schema.Body),
}
if bAddrSchema.DependentBodySelfRef && selfRefBodyRangePtr != nil {
copy(blockRef.LocalAddr, selfRefAddr)
blockRef.LocalAddr = append(blockRef.LocalAddr, lang.AttrStep{Name: bType})
blockRef.TargetableFromRangePtr = selfRefBodyRangePtr.Ptr()
}
blockRef.NestedTargets = d.collectInferredReferenceTargetsForBody(
blockAddr, bAddrSchema, blk.Body, bCollection.Schema.Body, selfRefBodyRangePtr, blockRef.LocalAddr)

sort.Sort(blockRef.NestedTargets)
refs = append(refs, blockRef)
}
Expand All @@ -719,11 +749,17 @@ func (d *PathDecoder) collectInferredReferenceTargetsForBody(addr lang.Address,

blockRef := reference.Target{
Addr: blockAddr,
ScopeId: scopeId,
LocalAddr: make(lang.Address, len(selfRefAddr)),
ScopeId: bAddrSchema.ScopeId,
Type: cty.List(cty.Object(bodySchemaAsAttrTypes(bCollection.Schema.Body))),
Description: bCollection.Schema.Description,
RangePtr: body.MissingItemRange().Ptr(),
}
if bAddrSchema.DependentBodySelfRef && selfRefBodyRangePtr != nil {
copy(blockRef.LocalAddr, selfRefAddr)
blockRef.LocalAddr = append(blockRef.LocalAddr, lang.AttrStep{Name: bType})
blockRef.TargetableFromRangePtr = selfRefBodyRangePtr.Ptr()
}

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

elemRef := reference.Target{
Addr: elemAddr,
ScopeId: scopeId,
LocalAddr: make(lang.Address, len(blockRef.LocalAddr)),
ScopeId: bAddrSchema.ScopeId,
Type: cty.Object(bodySchemaAsAttrTypes(bCollection.Schema.Body)),
Description: bCollection.Schema.Description,
DefRangePtr: b.DefRange.Ptr(),
RangePtr: b.Range.Ptr(),
NestedTargets: d.collectInferredReferenceTargetsForBody(
elemAddr, scopeId, b.Body, bCollection.Schema.Body),
}

if bAddrSchema.DependentBodySelfRef && selfRefBodyRangePtr != nil {
copy(elemRef.LocalAddr, blockRef.LocalAddr)
elemRef.LocalAddr = append(elemRef.LocalAddr, lang.IndexStep{
Key: cty.NumberIntVal(int64(i)),
})
elemRef.TargetableFromRangePtr = selfRefBodyRangePtr.Ptr()
}

elemRef.NestedTargets = d.collectInferredReferenceTargetsForBody(
elemAddr, bAddrSchema, b.Body, bCollection.Schema.Body, selfRefBodyRangePtr, elemRef.LocalAddr)

sort.Sort(elemRef.NestedTargets)
blockRef.NestedTargets = append(blockRef.NestedTargets, elemRef)

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

blockRef := reference.Target{
Addr: blockAddr,
ScopeId: scopeId,
LocalAddr: make(lang.Address, len(selfRefAddr)),
ScopeId: bAddrSchema.ScopeId,
Type: cty.Set(cty.Object(bodySchemaAsAttrTypes(bCollection.Schema.Body))),
Description: bCollection.Schema.Description,
RangePtr: body.MissingItemRange().Ptr(),
}
if bAddrSchema.DependentBodySelfRef && selfRefBodyRangePtr != nil {
copy(blockRef.LocalAddr, selfRefAddr)
blockRef.LocalAddr = append(blockRef.LocalAddr, lang.AttrStep{Name: bType})
blockRef.TargetableFromRangePtr = selfRefBodyRangePtr.Ptr()
}

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

blockRef := reference.Target{
Addr: blockAddr,
ScopeId: scopeId,
LocalAddr: make(lang.Address, len(selfRefAddr)),
ScopeId: bAddrSchema.ScopeId,
Type: cty.Map(cty.Object(bodySchemaAsAttrTypes(bCollection.Schema.Body))),
Description: bCollection.Schema.Description,
RangePtr: body.MissingItemRange().Ptr(),
}
if bAddrSchema.DependentBodySelfRef && selfRefBodyRangePtr != nil {
copy(blockRef.LocalAddr, selfRefAddr)
blockRef.LocalAddr = append(blockRef.LocalAddr, lang.AttrStep{Name: bType})
blockRef.TargetableFromRangePtr = selfRefBodyRangePtr.Ptr()
}

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

elemRef := reference.Target{
Addr: elemAddr,
ScopeId: scopeId,
LocalAddr: make(lang.Address, len(blockRef.LocalAddr)),
ScopeId: bAddrSchema.ScopeId,
Type: refType,
Description: bCollection.Schema.Description,
RangePtr: b.Range.Ptr(),
DefRangePtr: b.DefRange.Ptr(),
NestedTargets: d.collectInferredReferenceTargetsForBody(
elemAddr, scopeId, b.Body, bCollection.Schema.Body),
}
if bAddrSchema.DependentBodySelfRef && selfRefBodyRangePtr != nil {
copy(elemRef.LocalAddr, blockRef.LocalAddr)
elemRef.LocalAddr = append(elemRef.LocalAddr, lang.IndexStep{
Key: cty.StringVal(b.Labels[0]),
})
elemRef.TargetableFromRangePtr = selfRefBodyRangePtr.Ptr()
}

elemRef.NestedTargets = d.collectInferredReferenceTargetsForBody(
elemAddr, bAddrSchema, b.Body, bCollection.Schema.Body, selfRefBodyRangePtr, elemRef.LocalAddr)
sort.Sort(elemRef.NestedTargets)
blockRef.NestedTargets = append(blockRef.NestedTargets, elemRef)

Expand Down
Loading