Skip to content

Commit

Permalink
reference: Ensure Target.Address() renders correct address
Browse files Browse the repository at this point in the history
Since appropriate address now depends on the position of the origin (which is pointing to the target), this change reflects that.
  • Loading branch information
radeksimko committed Nov 24, 2022
1 parent 3b1b516 commit 56802f1
Show file tree
Hide file tree
Showing 6 changed files with 245 additions and 10 deletions.
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
6 changes: 3 additions & 3 deletions decoder/hover.go
Original file line number Diff line number Diff line change
Expand Up @@ -647,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
1 change: 1 addition & 0 deletions decoder/reference_targets.go
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,7 @@ func (d *PathDecoder) decodeReferenceTargetsForBody(body hcl.Body, parentBlock *
bodyRef.LocalAddr = lang.Address{
lang.RootStep{Name: "self"},
}
bodyRef.TargetableFromRangePtr = blk.Range.Ptr()
} else {
bodyRef.LocalAddr = lang.Address{}
}
Expand Down
65 changes: 65 additions & 0 deletions decoder/reference_targets_collect_extensions_hcl_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,19 @@ func TestCollectReferenceTargets_extension_hcl(t *testing.T) {
Byte: 30,
},
},
TargetableFromRangePtr: &hcl.Range{
Filename: "test.tf",
Start: hcl.Pos{
Line: 1,
Column: 1,
Byte: 0,
},
End: hcl.Pos{
Line: 5,
Column: 2,
Byte: 78,
},
},
Type: cty.Object(map[string]cty.Type{
"bar": cty.Number,
"foo": cty.String,
Expand Down Expand Up @@ -313,6 +326,19 @@ func TestCollectReferenceTargets_extension_hcl(t *testing.T) {
Byte: 30,
},
},
TargetableFromRangePtr: &hcl.Range{
Filename: "test.tf",
Start: hcl.Pos{
Line: 1,
Column: 1,
Byte: 0,
},
End: hcl.Pos{
Line: 6,
Column: 2,
Byte: 77,
},
},
Type: cty.Object(map[string]cty.Type{
"foo": cty.Object(map[string]cty.Type{
"bar": cty.Number,
Expand Down Expand Up @@ -525,6 +551,19 @@ func TestCollectReferenceTargets_extension_hcl(t *testing.T) {
Byte: 30,
},
},
TargetableFromRangePtr: &hcl.Range{
Filename: "test.tf",
Start: hcl.Pos{
Line: 1,
Column: 1,
Byte: 0,
},
End: hcl.Pos{
Line: 6,
Column: 2,
Byte: 77,
},
},
Type: cty.Object(map[string]cty.Type{
"foo": cty.List(cty.Object(map[string]cty.Type{
"bar": cty.Number,
Expand Down Expand Up @@ -783,6 +822,19 @@ func TestCollectReferenceTargets_extension_hcl(t *testing.T) {
Byte: 30,
},
},
TargetableFromRangePtr: &hcl.Range{
Filename: "test.tf",
Start: hcl.Pos{
Line: 1,
Column: 1,
Byte: 0,
},
End: hcl.Pos{
Line: 6,
Column: 2,
Byte: 77,
},
},
Type: cty.Object(map[string]cty.Type{
"foo": cty.Set(cty.Object(map[string]cty.Type{
"bar": cty.Number,
Expand Down Expand Up @@ -927,6 +979,19 @@ func TestCollectReferenceTargets_extension_hcl(t *testing.T) {
Byte: 30,
},
},
TargetableFromRangePtr: &hcl.Range{
Filename: "test.tf",
Start: hcl.Pos{
Line: 1,
Column: 1,
Byte: 0,
},
End: hcl.Pos{
Line: 6,
Column: 2,
Byte: 83,
},
},
Type: cty.Object(map[string]cty.Type{
"foo": cty.Map(cty.Object(map[string]cty.Type{
"bar": cty.Number,
Expand Down
21 changes: 15 additions & 6 deletions reference/target.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,14 +98,23 @@ func copyHclRangePtr(rng *hcl.Range) *hcl.Range {

// Address returns any of the two non-empty addresses
// depending on the provided context
func (r Target) Address(ctx context.Context) lang.Address {
addr := r.Addr
if len(r.LocalAddr) > 0 &&
(len(r.Addr) == 0 || schema.ActiveSelfRefsFromContext(ctx)) {
addr = r.LocalAddr
func (r Target) Address(ctx context.Context, pos hcl.Pos) lang.Address {
if len(r.LocalAddr) > 0 {
// If the target has only local address, use it
if len(r.Addr) == 0 {
return r.LocalAddr
}

// If the target has local self address & self is active
if r.LocalAddr[0].String() == "self" && schema.ActiveSelfRefsFromContext(ctx) {
// and we targeting it from the expected range
if r.TargetableFromRangePtr != nil && r.TargetableFromRangePtr.ContainsPos(pos) {
return r.LocalAddr
}
}
}

return addr
return r.Addr
}

func (r Target) FriendlyName() string {
Expand Down
160 changes: 160 additions & 0 deletions reference/target_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
package reference

import (
"context"
"fmt"
"testing"

"github.com/google/go-cmp/cmp"
"github.com/hashicorp/hcl-lang/lang"
"github.com/hashicorp/hcl-lang/schema"
"github.com/hashicorp/hcl/v2"
"github.com/zclconf/go-cty-debug/ctydebug"
)

func TestTarget_Address(t *testing.T) {
testCases := []struct {
name string
pos hcl.Pos
activeSelfRefs bool
target Target
expectedAddress lang.Address
}{
{
"absolute address and no local address",
hcl.InitialPos,
false,
Target{
Addr: lang.Address{
lang.RootStep{Name: "aws_instance"},
lang.AttrStep{Name: "instance_size"},
},
},
lang.Address{
lang.RootStep{Name: "aws_instance"},
lang.AttrStep{Name: "instance_size"},
},
},
{
"local address and no absolute address",
hcl.InitialPos,
false,
Target{
LocalAddr: lang.Address{
lang.RootStep{Name: "count"},
lang.AttrStep{Name: "index"},
},
},
lang.Address{
lang.RootStep{Name: "count"},
lang.AttrStep{Name: "index"},
},
},
{
"self address with active self and matching range",
hcl.Pos{Line: 2, Column: 2, Byte: 2},
true,
Target{
Addr: lang.Address{
lang.RootStep{Name: "aws_instance"},
lang.AttrStep{Name: "instance_size"},
},
LocalAddr: lang.Address{
lang.RootStep{Name: "self"},
lang.AttrStep{Name: "instance_size"},
},
TargetableFromRangePtr: &hcl.Range{
Filename: "test.tf",
Start: hcl.Pos{Line: 1, Column: 1, Byte: 0},
End: hcl.Pos{Line: 3, Column: 1, Byte: 10},
},
},
lang.Address{
lang.RootStep{Name: "self"},
lang.AttrStep{Name: "instance_size"},
},
},
{
"self address without active self but matching range",
hcl.Pos{Line: 2, Column: 2, Byte: 2},
false,
Target{
Addr: lang.Address{
lang.RootStep{Name: "aws_instance"},
lang.AttrStep{Name: "instance_size"},
},
LocalAddr: lang.Address{
lang.RootStep{Name: "self"},
lang.AttrStep{Name: "instance_size"},
},
TargetableFromRangePtr: &hcl.Range{
Filename: "test.tf",
Start: hcl.Pos{Line: 1, Column: 1, Byte: 0},
End: hcl.Pos{Line: 3, Column: 1, Byte: 10},
},
},
lang.Address{
lang.RootStep{Name: "aws_instance"},
lang.AttrStep{Name: "instance_size"},
},
},
{
"self address with active self but no matching range",
hcl.Pos{Line: 5, Column: 2, Byte: 15},
true,
Target{
Addr: lang.Address{
lang.RootStep{Name: "aws_instance"},
lang.AttrStep{Name: "instance_size"},
},
LocalAddr: lang.Address{
lang.RootStep{Name: "self"},
lang.AttrStep{Name: "instance_size"},
},
TargetableFromRangePtr: &hcl.Range{
Filename: "test.tf",
Start: hcl.Pos{Line: 1, Column: 1, Byte: 0},
End: hcl.Pos{Line: 3, Column: 1, Byte: 10},
},
},
lang.Address{
lang.RootStep{Name: "aws_instance"},
lang.AttrStep{Name: "instance_size"},
},
},
{
"self address with active self and missing targetable",
hcl.Pos{Line: 5, Column: 2, Byte: 15},
true,
Target{
Addr: lang.Address{
lang.RootStep{Name: "aws_instance"},
lang.AttrStep{Name: "instance_size"},
},
LocalAddr: lang.Address{
lang.RootStep{Name: "self"},
lang.AttrStep{Name: "instance_size"},
},
},
lang.Address{
lang.RootStep{Name: "aws_instance"},
lang.AttrStep{Name: "instance_size"},
},
},
}

for i, tc := range testCases {
t.Run(fmt.Sprintf("%d-%s", i, tc.name), func(t *testing.T) {
ctx := context.Background()

if tc.activeSelfRefs {
ctx = schema.WithActiveSelfRefs(ctx)
}

address := tc.target.Address(ctx, tc.pos)
if diff := cmp.Diff(tc.expectedAddress, address, ctydebug.CmpOptions); diff != "" {
t.Fatalf("mismatch of address: %s", diff)
}
})
}
}

0 comments on commit 56802f1

Please sign in to comment.