Skip to content

Commit

Permalink
decoder: Check Extension.SelfRefs when collecting origins
Browse files Browse the repository at this point in the history
  • Loading branch information
dbanck authored and radeksimko committed Nov 24, 2022
1 parent 24932c4 commit dfb38e1
Show file tree
Hide file tree
Showing 3 changed files with 105 additions and 13 deletions.
31 changes: 19 additions & 12 deletions decoder/reference_origins.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,6 @@ func (d *Decoder) ReferenceOriginsTargetingPos(path lang.Path, file string, pos
return origins
}

// TODO: Avoid collecting self.* origins unless BodySchema.Extension.SelfRef == true
func (d *PathDecoder) CollectReferenceOrigins() (reference.Origins, error) {
refOrigins := make(reference.Origins, 0)
impliedOrigins := make([]schema.ImpliedOrigin, 0)
Expand Down Expand Up @@ -157,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 @@ -181,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 @@ -216,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 @@ -236,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 @@ -267,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 @@ -280,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 @@ -298,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
9 changes: 8 additions & 1 deletion reference/traversal.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,16 @@ import (
"github.com/hashicorp/hcl/v2"
)

func TraversalsToLocalOrigins(traversals []hcl.Traversal, tes schema.TraversalExprs) Origins {
func TraversalsToLocalOrigins(traversals []hcl.Traversal, tes schema.TraversalExprs, allowSelfRefs bool) Origins {
origins := make(Origins, 0)
for _, traversal := range traversals {
// traversal should not be relative here, since the input to this
// function `expr.Variables()` only returns absolute traversals
if !traversal.IsRelative() && traversal.RootName() == "self" && !allowSelfRefs {
// Only if a block allows the usage of self.* we create a origin,
// else just continue
continue
}
origin, err := TraversalToLocalOrigin(traversal, tes)
if err != nil {
continue
Expand Down
78 changes: 78 additions & 0 deletions reference/traversal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,3 +98,81 @@ func TestTraversalToOrigin(t *testing.T) {
})
}
}

func TestTraversalsToOrigin(t *testing.T) {
testCases := []struct {
testName string
rawTraversals []string
traversalExprs schema.TraversalExprs
allowSelfRefs bool
expectedOrigins Origins
}{
{
"origin collection without self refs",
[]string{"foo.bar", "self.bar"},
schema.TraversalExprs{},
false,
Origins{
LocalOrigin{
Addr: lang.Address{
lang.RootStep{Name: "foo"},
lang.AttrStep{Name: "bar"},
},
Range: hcl.Range{
Filename: "test.tf",
Start: hcl.Pos{Line: 1, Column: 1, Byte: 0},
End: hcl.Pos{Line: 1, Column: 8, Byte: 7},
},
},
},
},
{
"origin collection with self refs",
[]string{"foo.bar", "self.bar"},
schema.TraversalExprs{},
true,
Origins{
LocalOrigin{
Addr: lang.Address{
lang.RootStep{Name: "foo"},
lang.AttrStep{Name: "bar"},
},
Range: hcl.Range{
Filename: "test.tf",
Start: hcl.Pos{Line: 1, Column: 1, Byte: 0},
End: hcl.Pos{Line: 1, Column: 8, Byte: 7},
},
},
LocalOrigin{
Addr: lang.Address{
lang.RootStep{Name: "self"},
lang.AttrStep{Name: "bar"},
},
Range: hcl.Range{
Filename: "test.tf",
Start: hcl.Pos{Line: 1, Column: 1, Byte: 0},
End: hcl.Pos{Line: 1, Column: 9, Byte: 8},
},
},
},
},
}

for i, tc := range testCases {
t.Run(fmt.Sprintf("%d-%s", i, tc.testName), func(t *testing.T) {
traversals := make([]hcl.Traversal, 0)
for _, rawTraversal := range tc.rawTraversals {
traversal, diags := hclsyntax.ParseTraversalAbs([]byte(rawTraversal), "test.tf", hcl.InitialPos)
if len(diags) > 0 {
t.Fatal(diags)
}
traversals = append(traversals, traversal)
}

origins := TraversalsToLocalOrigins(traversals, tc.traversalExprs, tc.allowSelfRefs)
if diff := cmp.Diff(tc.expectedOrigins, origins, ctydebug.CmpOptions); diff != "" {
t.Fatalf("origin mismatch: %s", diff)
}
})
}
}

0 comments on commit dfb38e1

Please sign in to comment.