Skip to content

Commit

Permalink
fix: filtering of self references for cross-file references (#105)
Browse files Browse the repository at this point in the history
* add (failing) test

* fix: filtering of self references for cross-file references
  • Loading branch information
radeksimko authored Mar 14, 2022
1 parent b2b314e commit d770b42
Show file tree
Hide file tree
Showing 2 changed files with 180 additions and 1 deletion.
2 changes: 1 addition & 1 deletion decoder/expression_candidates.go
Original file line number Diff line number Diff line change
Expand Up @@ -331,7 +331,7 @@ func (d *PathDecoder) candidatesForTraversalConstraint(tc schema.TraversalExpr,

d.pathCtx.ReferenceTargets.MatchWalk(tc, string(prefix), func(ref reference.Target) error {
// avoid suggesting references to block's own fields from within (for now)
if ref.RangePtr != nil &&
if ref.RangePtr != nil && outerBodyRng.Filename == ref.RangePtr.Filename &&
(outerBodyRng.ContainsPos(ref.RangePtr.Start) ||
posEqual(outerBodyRng.End, ref.RangePtr.End)) {
return nil
Expand Down
179 changes: 179 additions & 0 deletions decoder/expression_candidates_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"github.com/hashicorp/hcl-lang/schema"
"github.com/hashicorp/hcl/v2"
"github.com/hashicorp/hcl/v2/hclsyntax"
"github.com/zclconf/go-cty-debug/ctydebug"
"github.com/zclconf/go-cty/cty"
)

Expand Down Expand Up @@ -2566,3 +2567,181 @@ another_block "meh" {
})
}
}

func TestDecoder_CandidateAtPos_expressions_crossFileTraversal(t *testing.T) {
f1, _ := hclsyntax.ParseConfig([]byte(`variable "aaa" {}
variable "bbb" {}
variable "ccc" {}
`), "test1.tf", hcl.InitialPos)
f2, _ := hclsyntax.ParseConfig([]byte(`value =
`), "test2.tf", hcl.InitialPos)

bodySchema := &schema.BodySchema{
Attributes: map[string]*schema.AttributeSchema{
"value": {
IsOptional: true,
Expr: schema.ExprConstraints{
schema.TraversalExpr{
OfScopeId: lang.ScopeId("variable"),
OfType: cty.DynamicPseudoType,
},
},
},
},
Blocks: map[string]*schema.BlockSchema{
"variable": {
Labels: []*schema.LabelSchema{
{Name: "name"},
},
Address: &schema.BlockAddrSchema{
Steps: []schema.AddrStep{
schema.StaticStep{Name: "var"},
schema.LabelStep{Index: 0},
},
FriendlyName: "variable",
ScopeId: lang.ScopeId("variable"),
AsTypeOf: &schema.BlockAsTypeOf{
AttributeExpr: "type",
AttributeValue: "default",
},
},
},
},
}

testDir := t.TempDir()
dirReader := &testPathReader{
paths: map[string]*PathContext{
testDir: {
Schema: bodySchema,
Files: map[string]*hcl.File{
"test1.tf": f1,
"test2.tf": f2,
},
ReferenceTargets: reference.Targets{},
},
},
}
decoder := NewDecoder(dirReader)
d, err := decoder.Path(lang.Path{Path: testDir})
if err != nil {
t.Fatal(err)
}
refTargets, err := d.CollectReferenceTargets()
if err != nil {
t.Fatal(err)
}

expectedTargets := reference.Targets{
{
Addr: lang.Address{lang.RootStep{Name: "var"}, lang.AttrStep{Name: "aaa"}},
ScopeId: lang.ScopeId("variable"),
RangePtr: &hcl.Range{
Filename: "test1.tf",
Start: hcl.Pos{Line: 1, Column: 1, Byte: 0},
End: hcl.Pos{Line: 1, Column: 18, Byte: 17},
},
DefRangePtr: &hcl.Range{
Filename: "test1.tf",
Start: hcl.Pos{Line: 1, Column: 1, Byte: 0},
End: hcl.Pos{Line: 1, Column: 15, Byte: 14},
},
Type: cty.DynamicPseudoType,
},
{
Addr: lang.Address{lang.RootStep{Name: "var"}, lang.AttrStep{Name: "bbb"}},
ScopeId: lang.ScopeId("variable"),
RangePtr: &hcl.Range{
Filename: "test1.tf",
Start: hcl.Pos{Line: 2, Column: 1, Byte: 18},
End: hcl.Pos{Line: 2, Column: 18, Byte: 35},
},
DefRangePtr: &hcl.Range{
Filename: "test1.tf",
Start: hcl.Pos{Line: 2, Column: 1, Byte: 18},
End: hcl.Pos{Line: 2, Column: 15, Byte: 32},
},
Type: cty.DynamicPseudoType,
},
{
Addr: lang.Address{lang.RootStep{Name: "var"}, lang.AttrStep{Name: "ccc"}},
ScopeId: lang.ScopeId("variable"),
RangePtr: &hcl.Range{
Filename: "test1.tf",
Start: hcl.Pos{Line: 3, Column: 1, Byte: 36},
End: hcl.Pos{Line: 3, Column: 18, Byte: 53},
},
DefRangePtr: &hcl.Range{
Filename: "test1.tf",
Start: hcl.Pos{Line: 3, Column: 1, Byte: 36},
End: hcl.Pos{Line: 3, Column: 15, Byte: 50},
},
Type: cty.DynamicPseudoType,
},
}
if diff := cmp.Diff(expectedTargets, refTargets, ctydebug.CmpOptions); diff != "" {
t.Fatalf("unexpected targets: %s", diff)
}

dirReader.paths[testDir].ReferenceTargets = refTargets

candidates, err := d.CandidatesAtPos("test2.tf", hcl.Pos{
Line: 1,
Column: 9,
Byte: 8,
})
if err != nil {
t.Fatal(err)
}

expectedCandidates := lang.Candidates{
List: []lang.Candidate{
{
Label: "var.aaa",
Detail: "dynamic",
TextEdit: lang.TextEdit{
Range: hcl.Range{
Filename: "test2.tf",
Start: hcl.Pos{Line: 1, Column: 9, Byte: 8},
End: hcl.Pos{Line: 1, Column: 9, Byte: 8},
},
NewText: "var.aaa",
Snippet: "var.aaa",
},
Kind: lang.TraversalCandidateKind,
},
{
Label: "var.bbb",
Detail: "dynamic",
TextEdit: lang.TextEdit{
Range: hcl.Range{
Filename: "test2.tf",
Start: hcl.Pos{Line: 1, Column: 9, Byte: 8},
End: hcl.Pos{Line: 1, Column: 9, Byte: 8},
},
NewText: "var.bbb",
Snippet: "var.bbb",
},
Kind: lang.TraversalCandidateKind,
},
{
Label: "var.ccc",
Detail: "dynamic",
TextEdit: lang.TextEdit{
Range: hcl.Range{
Filename: "test2.tf",
Start: hcl.Pos{Line: 1, Column: 9, Byte: 8},
End: hcl.Pos{Line: 1, Column: 9, Byte: 8},
},
NewText: "var.ccc",
Snippet: "var.ccc",
},
Kind: lang.TraversalCandidateKind,
},
},
IsComplete: true,
}
if diff := cmp.Diff(expectedCandidates, candidates); diff != "" {
t.Fatalf("unexpected candidates: %s", diff)
}
}

0 comments on commit d770b42

Please sign in to comment.