From 31cd29e209b8029eaa30e036dc57540669c961e4 Mon Sep 17 00:00:00 2001 From: Radek Simko Date: Tue, 26 Oct 2021 09:05:29 +0100 Subject: [PATCH] Extract targets & origins into new reference pkg --- decoder/decoder_test.go | 59 --- decoder/errors.go | 6 - decoder/expression_candidates.go | 5 +- decoder/expression_candidates_test.go | 99 +++-- decoder/hover.go | 17 +- decoder/hover_expressions_test.go | 17 +- decoder/path_context.go | 6 +- decoder/reference_origins.go | 63 +-- decoder/reference_origins_collect_hcl_test.go | 81 ++-- .../reference_origins_collect_json_test.go | 33 +- decoder/reference_origins_test.go | 107 ++--- decoder/reference_targets.go | 306 +++---------- decoder/reference_targets_collect_hcl_test.go | 137 +++--- .../reference_targets_collect_json_test.go | 129 +++--- decoder/reference_targets_test.go | 204 +++------ decoder/semantic_tokens.go | 8 +- decoder/semantic_tokens_expr_test.go | 23 +- lang/address.go | 49 ++ lang/address_steps.go | 26 -- lang/address_test.go | 140 ++++++ lang/reference_origin.go | 62 --- lang/reference_target.go | 103 ----- lang/scope_id.go | 3 + reference/errors.go | 13 + reference/origin.go | 27 ++ reference/origin_constraint.go | 26 ++ reference/origins.go | 46 ++ reference/origins_test.go | 418 ++++++++++++++++++ reference/target.go | 134 ++++++ reference/targets.go | 169 +++++++ reference/targets_test.go | 324 ++++++++++++++ reference/traversal.go | 52 +++ reference/traversal_test.go | 100 +++++ 33 files changed, 1981 insertions(+), 1011 deletions(-) create mode 100644 lang/address.go create mode 100644 lang/address_test.go delete mode 100644 lang/reference_origin.go delete mode 100644 lang/reference_target.go create mode 100644 lang/scope_id.go create mode 100644 reference/errors.go create mode 100644 reference/origin.go create mode 100644 reference/origin_constraint.go create mode 100644 reference/origins.go create mode 100644 reference/origins_test.go create mode 100644 reference/target.go create mode 100644 reference/targets.go create mode 100644 reference/targets_test.go create mode 100644 reference/traversal.go create mode 100644 reference/traversal_test.go diff --git a/decoder/decoder_test.go b/decoder/decoder_test.go index c1f24e42..dd503f00 100644 --- a/decoder/decoder_test.go +++ b/decoder/decoder_test.go @@ -2,15 +2,9 @@ package decoder import ( "context" - "fmt" "testing" - "github.com/google/go-cmp/cmp" "github.com/hashicorp/hcl-lang/lang" - "github.com/hashicorp/hcl/v2" - "github.com/hashicorp/hcl/v2/hclsyntax" - "github.com/zclconf/go-cty-debug/ctydebug" - "github.com/zclconf/go-cty/cty" ) type testPathReader struct { @@ -50,56 +44,3 @@ func testPathDecoder(t *testing.T, pathCtx *PathContext) *PathDecoder { return pathDecoder } - -func TestTraversalToAddress(t *testing.T) { - testCases := []struct { - rawTraversal string - expectedAddr lang.Address - }{ - { - "one", - lang.Address{ - lang.RootStep{Name: "one"}, - }, - }, - { - "first.second", - lang.Address{ - lang.RootStep{Name: "first"}, - lang.AttrStep{Name: "second"}, - }, - }, - { - "foo[2]", - lang.Address{ - lang.RootStep{Name: "foo"}, - lang.IndexStep{Key: cty.NumberIntVal(2)}, - }, - }, - { - `foo["bar"]`, - lang.Address{ - lang.RootStep{Name: "foo"}, - lang.IndexStep{Key: cty.StringVal("bar")}, - }, - }, - } - - for i, tc := range testCases { - t.Run(fmt.Sprintf("%d", i), func(t *testing.T) { - traversal, diags := hclsyntax.ParseTraversalAbs([]byte(tc.rawTraversal), "test.tf", hcl.InitialPos) - if len(diags) > 0 { - t.Fatal(diags) - } - - addr, err := lang.TraversalToAddress(traversal) - if err != nil { - t.Fatal(err) - } - - if diff := cmp.Diff(tc.expectedAddr, addr, ctydebug.CmpOptions); diff != "" { - t.Fatalf("address mismatch: %s", diff) - } - }) - } -} diff --git a/decoder/errors.go b/decoder/errors.go index f0c2b534..b5745df7 100644 --- a/decoder/errors.go +++ b/decoder/errors.go @@ -12,12 +12,6 @@ func (*NoSchemaError) Error() string { return fmt.Sprintf("no schema available") } -type NoRefTargetFound struct{} - -func (*NoRefTargetFound) Error() string { - return fmt.Sprintf("no reference target found") -} - type ConstraintMismatch struct { Expr hcl.Expression } diff --git a/decoder/expression_candidates.go b/decoder/expression_candidates.go index 9dc9d475..11641db5 100644 --- a/decoder/expression_candidates.go +++ b/decoder/expression_candidates.go @@ -7,6 +7,7 @@ import ( "strings" "github.com/hashicorp/hcl-lang/lang" + "github.com/hashicorp/hcl-lang/reference" "github.com/hashicorp/hcl-lang/schema" "github.com/hashicorp/hcl/v2" "github.com/hashicorp/hcl/v2/hclsyntax" @@ -328,9 +329,7 @@ func (d *PathDecoder) candidatesForTraversalConstraint(tc schema.TraversalExpr, prefix, _ := d.bytesFromRange(prefixRng) - refs := ReferenceTargets(d.pathCtx.ReferenceTargets) - - refs.MatchWalk(tc, string(prefix), func(ref lang.ReferenceTarget) error { + 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 && (outerBodyRng.ContainsPos(ref.RangePtr.Start) || diff --git a/decoder/expression_candidates_test.go b/decoder/expression_candidates_test.go index 9591c4c2..c804a7df 100644 --- a/decoder/expression_candidates_test.go +++ b/decoder/expression_candidates_test.go @@ -6,6 +6,7 @@ import ( "github.com/google/go-cmp/cmp" "github.com/hashicorp/hcl-lang/lang" + "github.com/hashicorp/hcl-lang/reference" "github.com/hashicorp/hcl-lang/schema" "github.com/hashicorp/hcl/v2" "github.com/hashicorp/hcl/v2/hclsyntax" @@ -1532,7 +1533,7 @@ func TestDecoder_CandidateAtPos_traversalExpressions(t *testing.T) { testCases := []struct { testName string bodySchema *schema.BodySchema - builtinRefs lang.ReferenceTargets + builtinRefs reference.Targets cfg string pos hcl.Pos expectedCandidates lang.Candidates @@ -1548,7 +1549,7 @@ func TestDecoder_CandidateAtPos_traversalExpressions(t *testing.T) { }, }, }, - lang.ReferenceTargets{}, + reference.Targets{}, `attr = `, hcl.Pos{Line: 1, Column: 8, Byte: 7}, @@ -1566,15 +1567,15 @@ func TestDecoder_CandidateAtPos_traversalExpressions(t *testing.T) { }, }, }, - lang.ReferenceTargets{ - lang.ReferenceTarget{ + reference.Targets{ + reference.Target{ Addr: lang.Address{ lang.RootStep{Name: "var"}, lang.RootStep{Name: "first"}, }, Type: cty.Bool, }, - lang.ReferenceTarget{ + reference.Target{ Addr: lang.Address{ lang.RootStep{Name: "var"}, lang.RootStep{Name: "second"}, @@ -1598,15 +1599,15 @@ func TestDecoder_CandidateAtPos_traversalExpressions(t *testing.T) { }, }, }, - lang.ReferenceTargets{ - lang.ReferenceTarget{ + reference.Targets{ + reference.Target{ Addr: lang.Address{ lang.RootStep{Name: "var"}, lang.AttrStep{Name: "first"}, }, Type: cty.Bool, }, - lang.ReferenceTarget{ + reference.Target{ Addr: lang.Address{ lang.RootStep{Name: "var"}, lang.AttrStep{Name: "second"}, @@ -1653,15 +1654,15 @@ func TestDecoder_CandidateAtPos_traversalExpressions(t *testing.T) { }, }, }, - lang.ReferenceTargets{ - lang.ReferenceTarget{ + reference.Targets{ + reference.Target{ Addr: lang.Address{ lang.RootStep{Name: "var"}, lang.AttrStep{Name: "first"}, }, Type: cty.Bool, }, - lang.ReferenceTarget{ + reference.Target{ Addr: lang.Address{ lang.RootStep{Name: "var"}, lang.AttrStep{Name: "second"}, @@ -1730,15 +1731,15 @@ func TestDecoder_CandidateAtPos_traversalExpressions(t *testing.T) { }, }, }, - lang.ReferenceTargets{ - lang.ReferenceTarget{ + reference.Targets{ + reference.Target{ Addr: lang.Address{ lang.RootStep{Name: "var"}, lang.AttrStep{Name: "first"}, }, Type: cty.DynamicPseudoType, }, - lang.ReferenceTarget{ + reference.Target{ Addr: lang.Address{ lang.RootStep{Name: "var"}, lang.AttrStep{Name: "second"}, @@ -1807,15 +1808,15 @@ func TestDecoder_CandidateAtPos_traversalExpressions(t *testing.T) { }, }, }, - lang.ReferenceTargets{ - lang.ReferenceTarget{ + reference.Targets{ + reference.Target{ Addr: lang.Address{ lang.RootStep{Name: "var"}, lang.AttrStep{Name: "first"}, }, ScopeId: lang.ScopeId("test"), }, - lang.ReferenceTarget{ + reference.Target{ Addr: lang.Address{ lang.RootStep{Name: "var"}, lang.AttrStep{Name: "second"}, @@ -1867,22 +1868,22 @@ func TestDecoder_CandidateAtPos_traversalExpressions(t *testing.T) { }, }, }, - lang.ReferenceTargets{ - lang.ReferenceTarget{ + reference.Targets{ + reference.Target{ Addr: lang.Address{ lang.RootStep{Name: "var"}, lang.AttrStep{Name: "zero"}, }, Type: cty.Number, }, - lang.ReferenceTarget{ + reference.Target{ Addr: lang.Address{ lang.RootStep{Name: "var"}, lang.AttrStep{Name: "first"}, }, ScopeId: lang.ScopeId("blah"), }, - lang.ReferenceTarget{ + reference.Target{ Addr: lang.Address{ lang.RootStep{Name: "var"}, lang.AttrStep{Name: "second"}, @@ -1915,22 +1916,22 @@ func TestDecoder_CandidateAtPos_traversalExpressions(t *testing.T) { }, }, }, - lang.ReferenceTargets{ - lang.ReferenceTarget{ + reference.Targets{ + reference.Target{ Addr: lang.Address{ lang.RootStep{Name: "var"}, lang.AttrStep{Name: "zero"}, }, Type: cty.Number, }, - lang.ReferenceTarget{ + reference.Target{ Addr: lang.Address{ lang.RootStep{Name: "var"}, lang.AttrStep{Name: "first"}, }, ScopeId: lang.ScopeId("blah"), }, - lang.ReferenceTarget{ + reference.Target{ Addr: lang.Address{ lang.RootStep{Name: "var"}, lang.AttrStep{Name: "second"}, @@ -1990,7 +1991,7 @@ func TestDecoder_CandidateAtPos_traversalExpressions(t *testing.T) { }, }, }, - lang.ReferenceTargets{}, + reference.Targets{}, `custom "test" { greeting = "hello" } @@ -2036,15 +2037,15 @@ another_block "meh" { }, }, }, - lang.ReferenceTargets{ - lang.ReferenceTarget{ + reference.Targets{ + reference.Target{ Addr: lang.Address{ lang.RootStep{Name: "var"}, lang.AttrStep{Name: "first"}, }, Type: cty.String, }, - lang.ReferenceTarget{ + reference.Target{ Addr: lang.Address{ lang.RootStep{Name: "var"}, lang.AttrStep{Name: "second"}, @@ -2113,15 +2114,15 @@ another_block "meh" { }, }, }, - lang.ReferenceTargets{ - lang.ReferenceTarget{ + reference.Targets{ + reference.Target{ Addr: lang.Address{ lang.RootStep{Name: "var"}, lang.AttrStep{Name: "first"}, }, Type: cty.String, }, - lang.ReferenceTarget{ + reference.Target{ Addr: lang.Address{ lang.RootStep{Name: "var"}, lang.AttrStep{Name: "second"}, @@ -2190,8 +2191,8 @@ another_block "meh" { }, }, }, - lang.ReferenceTargets{ - lang.ReferenceTarget{ + reference.Targets{ + reference.Target{ Addr: lang.Address{ lang.RootStep{Name: "var"}, lang.AttrStep{Name: "first"}, @@ -2199,7 +2200,7 @@ another_block "meh" { Type: cty.Object(map[string]cty.Type{ "nested": cty.String, }), - NestedTargets: lang.ReferenceTargets{ + NestedTargets: reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "var"}, @@ -2210,7 +2211,7 @@ another_block "meh" { }, }, }, - lang.ReferenceTarget{ + reference.Target{ Addr: lang.Address{ lang.RootStep{Name: "var"}, lang.AttrStep{Name: "second"}, @@ -2279,8 +2280,8 @@ another_block "meh" { }, }, }, - lang.ReferenceTargets{ - lang.ReferenceTarget{ + reference.Targets{ + reference.Target{ Addr: lang.Address{ lang.RootStep{Name: "var"}, lang.AttrStep{Name: "first"}, @@ -2288,7 +2289,7 @@ another_block "meh" { Type: cty.Object(map[string]cty.Type{ "nested": cty.String, }), - NestedTargets: lang.ReferenceTargets{ + NestedTargets: reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "var"}, @@ -2299,7 +2300,7 @@ another_block "meh" { }, }, }, - lang.ReferenceTarget{ + reference.Target{ Addr: lang.Address{ lang.RootStep{Name: "var"}, lang.AttrStep{Name: "second"}, @@ -2346,8 +2347,8 @@ another_block "meh" { }, }, }, - lang.ReferenceTargets{ - lang.ReferenceTarget{ + reference.Targets{ + reference.Target{ Addr: lang.Address{ lang.RootStep{Name: "var"}, lang.AttrStep{Name: "first"}, @@ -2355,7 +2356,7 @@ another_block "meh" { Type: cty.List(cty.Object(map[string]cty.Type{ "nested": cty.String, })), - NestedTargets: lang.ReferenceTargets{ + NestedTargets: reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "var"}, @@ -2365,7 +2366,7 @@ another_block "meh" { Type: cty.Object(map[string]cty.Type{ "nested": cty.String, }), - NestedTargets: lang.ReferenceTargets{ + NestedTargets: reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "var"}, @@ -2379,7 +2380,7 @@ another_block "meh" { }, }, }, - lang.ReferenceTarget{ + reference.Target{ Addr: lang.Address{ lang.RootStep{Name: "var"}, lang.AttrStep{Name: "second"}, @@ -2448,14 +2449,14 @@ another_block "meh" { }, }, }, - lang.ReferenceTargets{ - lang.ReferenceTarget{ + reference.Targets{ + reference.Target{ Addr: lang.Address{ lang.RootStep{Name: "var"}, lang.AttrStep{Name: "first"}, }, Type: cty.Map(cty.String), - NestedTargets: lang.ReferenceTargets{ + NestedTargets: reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "var"}, @@ -2466,7 +2467,7 @@ another_block "meh" { }, }, }, - lang.ReferenceTarget{ + reference.Target{ Addr: lang.Address{ lang.RootStep{Name: "var"}, lang.AttrStep{Name: "second"}, diff --git a/decoder/hover.go b/decoder/hover.go index 29613d6b..c0059bfa 100644 --- a/decoder/hover.go +++ b/decoder/hover.go @@ -6,6 +6,7 @@ import ( "strings" "github.com/hashicorp/hcl-lang/lang" + "github.com/hashicorp/hcl-lang/reference" "github.com/hashicorp/hcl-lang/schema" "github.com/hashicorp/hcl/v2" "github.com/hashicorp/hcl/v2/hclsyntax" @@ -593,26 +594,20 @@ func stringValFromTemplateExpr(tplExpr *hclsyntax.TemplateExpr) (cty.Value, bool } func (d *PathDecoder) hoverContentForTraversalExpr(traversal hcl.Traversal, tes []schema.TraversalExpr) (string, error) { - if d.pathCtx.ReferenceTargets == nil { - return "", &NoRefTargetFound{} - } - - allTargets := ReferenceTargets(d.pathCtx.ReferenceTargets) - - origin, err := TraversalToReferenceOrigin(traversal, tes) + origin, err := reference.TraversalToOrigin(traversal, tes) if err != nil { return "", nil } - ref, err := allTargets.FirstTargetableBy(origin) - if err != nil { - return "", err + ref, ok := d.pathCtx.ReferenceTargets.FirstTargetableBy(origin) + if !ok { + return "", &reference.NoTargetFound{} } return hoverContentForReferenceTarget(ref) } -func hoverContentForReferenceTarget(ref lang.ReferenceTarget) (string, error) { +func hoverContentForReferenceTarget(ref *reference.Target) (string, error) { content := fmt.Sprintf("`%s`", ref.Addr.String()) var friendlyName string diff --git a/decoder/hover_expressions_test.go b/decoder/hover_expressions_test.go index cc0ec115..ee5752a9 100644 --- a/decoder/hover_expressions_test.go +++ b/decoder/hover_expressions_test.go @@ -7,6 +7,7 @@ import ( "github.com/google/go-cmp/cmp" "github.com/hashicorp/hcl-lang/lang" + "github.com/hashicorp/hcl-lang/reference" "github.com/hashicorp/hcl-lang/schema" "github.com/hashicorp/hcl/v2" "github.com/hashicorp/hcl/v2/hclsyntax" @@ -1305,7 +1306,7 @@ func TestDecoder_HoverAtPos_traversalExpressions(t *testing.T) { testCases := []struct { name string attrSchema map[string]*schema.AttributeSchema - refs lang.ReferenceTargets + refs reference.Targets cfg string pos hcl.Pos expectedData *lang.HoverData @@ -1320,11 +1321,11 @@ func TestDecoder_HoverAtPos_traversalExpressions(t *testing.T) { }, }, }, - lang.ReferenceTargets{}, + reference.Targets{}, `attr = var.blah`, hcl.Pos{Line: 1, Column: 10, Byte: 9}, nil, - &NoRefTargetFound{}, + &reference.NoTargetFound{}, }, { "known mismatching traversal", @@ -1335,7 +1336,7 @@ func TestDecoder_HoverAtPos_traversalExpressions(t *testing.T) { }, }, }, - lang.ReferenceTargets{ + reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "var"}, @@ -1347,7 +1348,7 @@ func TestDecoder_HoverAtPos_traversalExpressions(t *testing.T) { `attr = var.blah`, hcl.Pos{Line: 1, Column: 10, Byte: 9}, nil, - &NoRefTargetFound{}, + &reference.NoTargetFound{}, }, { "known type matching traversal", @@ -1358,7 +1359,7 @@ func TestDecoder_HoverAtPos_traversalExpressions(t *testing.T) { }, }, }, - lang.ReferenceTargets{ + reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "var"}, @@ -1396,7 +1397,7 @@ func TestDecoder_HoverAtPos_traversalExpressions(t *testing.T) { }, }, }, - lang.ReferenceTargets{ + reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "var"}, @@ -1434,7 +1435,7 @@ func TestDecoder_HoverAtPos_traversalExpressions(t *testing.T) { }, }, }, - lang.ReferenceTargets{ + reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "var"}, diff --git a/decoder/path_context.go b/decoder/path_context.go index 1712284d..b6545dd8 100644 --- a/decoder/path_context.go +++ b/decoder/path_context.go @@ -1,14 +1,14 @@ package decoder import ( - "github.com/hashicorp/hcl-lang/lang" + "github.com/hashicorp/hcl-lang/reference" "github.com/hashicorp/hcl-lang/schema" "github.com/hashicorp/hcl/v2" ) type PathContext struct { Schema *schema.BodySchema - ReferenceOrigins lang.ReferenceOrigins - ReferenceTargets lang.ReferenceTargets + ReferenceOrigins reference.Origins + ReferenceTargets reference.Targets Files map[string]*hcl.File } diff --git a/decoder/reference_origins.go b/decoder/reference_origins.go index 57eddee5..1b268af3 100644 --- a/decoder/reference_origins.go +++ b/decoder/reference_origins.go @@ -4,6 +4,7 @@ import ( "sort" "github.com/hashicorp/hcl-lang/lang" + "github.com/hashicorp/hcl-lang/reference" "github.com/hashicorp/hcl-lang/schema" "github.com/hashicorp/hcl/v2" "github.com/hashicorp/hcl/v2/hclsyntax" @@ -12,7 +13,7 @@ import ( // ReferenceOriginAtPos returns the ReferenceOrigin // enclosing the position in a file, if one exists, else nil -func (d *PathDecoder) ReferenceOriginAtPos(filename string, pos hcl.Pos) (*lang.ReferenceOrigin, error) { +func (d *PathDecoder) ReferenceOriginAtPos(filename string, pos hcl.Pos) (*reference.Origin, error) { // TODO: Filter d.refOriginReader instead here f, err := d.fileByName(filename) @@ -32,18 +33,12 @@ func (d *PathDecoder) ReferenceOriginAtPos(filename string, pos hcl.Pos) (*lang. return d.referenceOriginAtPos(rootBody, d.pathCtx.Schema, pos) } -func (d *PathDecoder) ReferenceOriginsTargeting(refTarget lang.ReferenceTarget) (lang.ReferenceOrigins, error) { - if d.pathCtx.ReferenceOrigins == nil { - return nil, nil - } - - allOrigins := ReferenceOrigins(d.pathCtx.ReferenceOrigins) - - return allOrigins.Targeting(refTarget), nil +func (d *PathDecoder) ReferenceOriginsTargeting(refTarget reference.Target) (reference.Origins, error) { + return d.pathCtx.ReferenceOrigins.Targeting(refTarget), nil } -func (d *PathDecoder) CollectReferenceOrigins() (lang.ReferenceOrigins, error) { - refOrigins := make(lang.ReferenceOrigins, 0) +func (d *PathDecoder) CollectReferenceOrigins() (reference.Origins, error) { + refOrigins := make(reference.Origins, 0) if d.pathCtx.Schema == nil { // unable to collect reference origins without schema @@ -69,8 +64,8 @@ func (d *PathDecoder) CollectReferenceOrigins() (lang.ReferenceOrigins, error) { return refOrigins, nil } -func (d *PathDecoder) referenceOriginsInBody(body hcl.Body, bodySchema *schema.BodySchema) lang.ReferenceOrigins { - origins := make(lang.ReferenceOrigins, 0) +func (d *PathDecoder) referenceOriginsInBody(body hcl.Body, bodySchema *schema.BodySchema) reference.Origins { + origins := make(reference.Origins, 0) if bodySchema == nil { return origins @@ -109,8 +104,8 @@ func (d *PathDecoder) referenceOriginsInBody(body hcl.Body, bodySchema *schema.B return origins } -func (d *PathDecoder) findOriginsInExpression(expr hcl.Expression, ec schema.ExprConstraints) lang.ReferenceOrigins { - origins := make(lang.ReferenceOrigins, 0) +func (d *PathDecoder) findOriginsInExpression(expr hcl.Expression, ec schema.ExprConstraints) reference.Origins { + origins := make(reference.Origins, 0) switch eType := expr.(type) { case *hclsyntax.TupleConsExpr: @@ -214,8 +209,8 @@ func (d *PathDecoder) findOriginsInExpression(expr hcl.Expression, ec schema.Exp return origins } -func traversalsToReferenceOrigins(traversals []hcl.Traversal, tes schema.TraversalExprs) lang.ReferenceOrigins { - origins := make(lang.ReferenceOrigins, 0) +func traversalsToReferenceOrigins(traversals []hcl.Traversal, tes schema.TraversalExprs) reference.Origins { + origins := make(reference.Origins, 0) for _, traversal := range traversals { origin, err := TraversalToReferenceOrigin(traversal, tes) if err != nil { @@ -227,7 +222,7 @@ func traversalsToReferenceOrigins(traversals []hcl.Traversal, tes schema.Travers return origins } -func (d *PathDecoder) referenceOriginAtPos(body *hclsyntax.Body, bodySchema *schema.BodySchema, pos hcl.Pos) (*lang.ReferenceOrigin, error) { +func (d *PathDecoder) referenceOriginAtPos(body *hclsyntax.Body, bodySchema *schema.BodySchema, pos hcl.Pos) (*reference.Origin, error) { for _, attr := range body.Attributes { if d.isPosInsideAttrExpr(attr, pos) { aSchema, ok := bodySchema.Attributes[attr.Name] @@ -281,51 +276,31 @@ func (d *PathDecoder) traversalAtPos(expr hclsyntax.Expression, pos hcl.Pos) (hc return nil, false } -type ReferenceOrigins lang.ReferenceOrigins - -func (ro ReferenceOrigins) Targeting(refTarget lang.ReferenceTarget) lang.ReferenceOrigins { - origins := make(lang.ReferenceOrigins, 0) - - target := ReferenceTarget(refTarget) - - for _, refOrigin := range ro { - if target.IsTargetableBy(refOrigin) { - origins = append(origins, refOrigin) - } - } - - for _, iTarget := range refTarget.NestedTargets { - origins = append(origins, ro.Targeting(iTarget)...) - } - - return origins -} - -func TraversalToReferenceOrigin(traversal hcl.Traversal, tes []schema.TraversalExpr) (lang.ReferenceOrigin, error) { +func TraversalToReferenceOrigin(traversal hcl.Traversal, tes []schema.TraversalExpr) (reference.Origin, error) { addr, err := lang.TraversalToAddress(traversal) if err != nil { - return lang.ReferenceOrigin{}, err + return reference.Origin{}, err } - return lang.ReferenceOrigin{ + return reference.Origin{ Addr: addr, Range: traversal.SourceRange(), Constraints: traversalExpressionsToOriginConstraints(tes), }, nil } -func traversalExpressionsToOriginConstraints(tes []schema.TraversalExpr) lang.ReferenceOriginConstraints { +func traversalExpressionsToOriginConstraints(tes []schema.TraversalExpr) reference.OriginConstraints { if len(tes) == 0 { return nil } - roc := make(lang.ReferenceOriginConstraints, 0) + roc := make(reference.OriginConstraints, 0) for _, te := range tes { if te.Address != nil { // skip traversals which are targets by themselves (not origins) continue } - roc = append(roc, lang.ReferenceOriginConstraint{ + roc = append(roc, reference.OriginConstraint{ OfType: te.OfType, OfScopeId: te.OfScopeId, }) diff --git a/decoder/reference_origins_collect_hcl_test.go b/decoder/reference_origins_collect_hcl_test.go index 99ca5c77..1ed2facc 100644 --- a/decoder/reference_origins_collect_hcl_test.go +++ b/decoder/reference_origins_collect_hcl_test.go @@ -6,6 +6,7 @@ import ( "github.com/google/go-cmp/cmp" "github.com/hashicorp/hcl-lang/lang" + "github.com/hashicorp/hcl-lang/reference" "github.com/hashicorp/hcl-lang/schema" "github.com/hashicorp/hcl/v2" "github.com/hashicorp/hcl/v2/hclsyntax" @@ -18,7 +19,7 @@ func TestCollectReferenceOrigins_hcl(t *testing.T) { name string schema *schema.BodySchema cfg string - expectedOrigins lang.ReferenceOrigins + expectedOrigins reference.Origins }{ { "no origins", @@ -30,7 +31,7 @@ func TestCollectReferenceOrigins_hcl(t *testing.T) { }, }, `attribute = "foo-bar"`, - lang.ReferenceOrigins{}, + reference.Origins{}, }, { "root attribute single step", @@ -44,12 +45,12 @@ func TestCollectReferenceOrigins_hcl(t *testing.T) { }, }, `attr = onestep`, - lang.ReferenceOrigins{ + reference.Origins{ { Addr: lang.Address{ lang.RootStep{Name: "onestep"}, }, - Constraints: lang.ReferenceOriginConstraints{{}}, + Constraints: reference.OriginConstraints{{}}, Range: hcl.Range{ Filename: "test.tf", Start: hcl.Pos{ @@ -90,12 +91,12 @@ func TestCollectReferenceOrigins_hcl(t *testing.T) { `attr1 = onestep attr2 = anotherstep attr3 = onestep`, - lang.ReferenceOrigins{ + reference.Origins{ { Addr: lang.Address{ lang.RootStep{Name: "onestep"}, }, - Constraints: lang.ReferenceOriginConstraints{{}}, + Constraints: reference.OriginConstraints{{}}, Range: hcl.Range{ Filename: "test.tf", Start: hcl.Pos{ @@ -114,7 +115,7 @@ attr3 = onestep`, Addr: lang.Address{ lang.RootStep{Name: "anotherstep"}, }, - Constraints: lang.ReferenceOriginConstraints{{}}, + Constraints: reference.OriginConstraints{{}}, Range: hcl.Range{ Filename: "test.tf", Start: hcl.Pos{ @@ -133,7 +134,7 @@ attr3 = onestep`, Addr: lang.Address{ lang.RootStep{Name: "onestep"}, }, - Constraints: lang.ReferenceOriginConstraints{{}}, + Constraints: reference.OriginConstraints{{}}, Range: hcl.Range{ Filename: "test.tf", Start: hcl.Pos{ @@ -162,12 +163,12 @@ attr3 = onestep`, }, }, `attr1 = "${onestep}-${onestep}-${another.foo.bar}"`, - lang.ReferenceOrigins{ + reference.Origins{ { Addr: lang.Address{ lang.RootStep{Name: "onestep"}, }, - Constraints: lang.ReferenceOriginConstraints{{}}, + Constraints: reference.OriginConstraints{{}}, Range: hcl.Range{ Filename: "test.tf", Start: hcl.Pos{ @@ -186,7 +187,7 @@ attr3 = onestep`, Addr: lang.Address{ lang.RootStep{Name: "onestep"}, }, - Constraints: lang.ReferenceOriginConstraints{{}}, + Constraints: reference.OriginConstraints{{}}, Range: hcl.Range{ Filename: "test.tf", Start: hcl.Pos{ @@ -207,7 +208,7 @@ attr3 = onestep`, lang.AttrStep{Name: "foo"}, lang.AttrStep{Name: "bar"}, }, - Constraints: lang.ReferenceOriginConstraints{{}}, + Constraints: reference.OriginConstraints{{}}, Range: hcl.Range{ Filename: "test.tf", Start: hcl.Pos{ @@ -236,7 +237,7 @@ attr3 = onestep`, }, }, `attr = one.two["key"].attr[0]`, - lang.ReferenceOrigins{ + reference.Origins{ { Addr: lang.Address{ lang.RootStep{Name: "one"}, @@ -245,7 +246,7 @@ attr3 = onestep`, lang.AttrStep{Name: "attr"}, lang.IndexStep{Key: cty.NumberIntVal(0)}, }, - Constraints: lang.ReferenceOriginConstraints{{}}, + Constraints: reference.OriginConstraints{{}}, Range: hcl.Range{ Filename: "test.tf", Start: hcl.Pos{ @@ -283,12 +284,12 @@ attr3 = onestep`, attr = onestep } `, - lang.ReferenceOrigins{ + reference.Origins{ { Addr: lang.Address{ lang.RootStep{Name: "onestep"}, }, - Constraints: lang.ReferenceOriginConstraints{{}}, + Constraints: reference.OriginConstraints{{}}, Range: hcl.Range{ Filename: "test.tf", Start: hcl.Pos{ @@ -324,12 +325,12 @@ attr3 = onestep`, attr = onestep } `, - lang.ReferenceOrigins{ + reference.Origins{ { Addr: lang.Address{ lang.RootStep{Name: "onestep"}, }, - Constraints: lang.ReferenceOriginConstraints{{}}, + Constraints: reference.OriginConstraints{{}}, Range: hcl.Range{ Filename: "test.tf", Start: hcl.Pos{ @@ -386,13 +387,13 @@ attr3 = onestep`, dep_attr = var.second } `, - lang.ReferenceOrigins{ + reference.Origins{ { Addr: lang.Address{ lang.RootStep{Name: "var"}, lang.AttrStep{Name: "first"}, }, - Constraints: lang.ReferenceOriginConstraints{{}}, + Constraints: reference.OriginConstraints{{}}, Range: hcl.Range{ Filename: "test.tf", Start: hcl.Pos{ @@ -412,7 +413,7 @@ attr3 = onestep`, lang.RootStep{Name: "var"}, lang.AttrStep{Name: "second"}, }, - Constraints: lang.ReferenceOriginConstraints{{}}, + Constraints: reference.OriginConstraints{{}}, Range: hcl.Range{ Filename: "test.tf", Start: hcl.Pos{ @@ -469,13 +470,13 @@ attr3 = onestep`, dep_attr = var.second } `, - lang.ReferenceOrigins{ + reference.Origins{ { Addr: lang.Address{ lang.RootStep{Name: "var"}, lang.AttrStep{Name: "first"}, }, - Constraints: lang.ReferenceOriginConstraints{{}}, + Constraints: reference.OriginConstraints{{}}, Range: hcl.Range{ Filename: "test.tf", Start: hcl.Pos{ @@ -537,7 +538,7 @@ attr3 = onestep`, set = [ var.second ] tuple = [ var.third ] `, - lang.ReferenceOrigins{ + reference.Origins{ { Addr: lang.Address{ lang.RootStep{Name: "var"}, @@ -556,7 +557,7 @@ tuple = [ var.third ] Byte: 18, }, }, - Constraints: lang.ReferenceOriginConstraints{ + Constraints: reference.OriginConstraints{ {OfScopeId: lang.ScopeId("test")}, }, }, @@ -578,7 +579,7 @@ tuple = [ var.third ] Byte: 39, }, }, - Constraints: lang.ReferenceOriginConstraints{ + Constraints: reference.OriginConstraints{ {OfScopeId: lang.ScopeId("test")}, }, }, @@ -600,7 +601,7 @@ tuple = [ var.third ] Byte: 61, }, }, - Constraints: lang.ReferenceOriginConstraints{ + Constraints: reference.OriginConstraints{ {OfScopeId: lang.ScopeId("test")}, }, }, @@ -630,7 +631,7 @@ tuple = [ var.third ] `obj = { attr = var.first }`, - lang.ReferenceOrigins{ + reference.Origins{ { Addr: lang.Address{ lang.RootStep{Name: "var"}, @@ -649,7 +650,7 @@ tuple = [ var.third ] Byte: 26, }, }, - Constraints: lang.ReferenceOriginConstraints{ + Constraints: reference.OriginConstraints{ {OfScopeId: lang.ScopeId("test")}, }, }, @@ -675,7 +676,7 @@ tuple = [ var.third ] `map = { key = var.first }`, - lang.ReferenceOrigins{ + reference.Origins{ { Addr: lang.Address{ lang.RootStep{Name: "var"}, @@ -694,7 +695,7 @@ tuple = [ var.third ] Byte: 25, }, }, - Constraints: lang.ReferenceOriginConstraints{ + Constraints: reference.OriginConstraints{ {OfScopeId: lang.ScopeId("test")}, }, }, @@ -718,7 +719,7 @@ tuple = [ var.third ] }, }, `tuple_cons = [ var.one ]`, - lang.ReferenceOrigins{ + reference.Origins{ { Addr: lang.Address{ lang.RootStep{Name: "var"}, @@ -737,7 +738,7 @@ tuple = [ var.third ] Byte: 22, }, }, - Constraints: lang.ReferenceOriginConstraints{ + Constraints: reference.OriginConstraints{ {OfScopeId: lang.ScopeId("test")}, }, }, @@ -782,7 +783,7 @@ obj = { foo = var.two } `, - lang.ReferenceOrigins{ + reference.Origins{ { Addr: lang.Address{ lang.RootStep{Name: "var"}, @@ -801,7 +802,7 @@ obj = { Byte: 23, }, }, - Constraints: lang.ReferenceOriginConstraints{ + Constraints: reference.OriginConstraints{ {OfType: cty.String}, }, }, @@ -823,7 +824,7 @@ obj = { Byte: 49, }, }, - Constraints: lang.ReferenceOriginConstraints{ + Constraints: reference.OriginConstraints{ {OfType: cty.String}, }, }, @@ -871,7 +872,7 @@ obj = { set = [ var.two ] tup = [ var.three ] `, - lang.ReferenceOrigins{ + reference.Origins{ { Addr: lang.Address{ lang.RootStep{Name: "var"}, @@ -890,7 +891,7 @@ tup = [ var.three ] Byte: 16, }, }, - Constraints: lang.ReferenceOriginConstraints{ + Constraints: reference.OriginConstraints{ {OfType: cty.String}, }, }, @@ -912,7 +913,7 @@ tup = [ var.three ] Byte: 34, }, }, - Constraints: lang.ReferenceOriginConstraints{ + Constraints: reference.OriginConstraints{ {OfType: cty.String}, }, }, @@ -934,7 +935,7 @@ tup = [ var.three ] Byte: 54, }, }, - Constraints: lang.ReferenceOriginConstraints{ + Constraints: reference.OriginConstraints{ {OfType: cty.String}, }, }, diff --git a/decoder/reference_origins_collect_json_test.go b/decoder/reference_origins_collect_json_test.go index af96e72c..824a1692 100644 --- a/decoder/reference_origins_collect_json_test.go +++ b/decoder/reference_origins_collect_json_test.go @@ -6,6 +6,7 @@ import ( "github.com/google/go-cmp/cmp" "github.com/hashicorp/hcl-lang/lang" + "github.com/hashicorp/hcl-lang/reference" "github.com/hashicorp/hcl-lang/schema" "github.com/hashicorp/hcl/v2" "github.com/hashicorp/hcl/v2/json" @@ -18,7 +19,7 @@ func TestCollectReferenceOrigins_json(t *testing.T) { name string schema *schema.BodySchema cfg string - expectedOrigins lang.ReferenceOrigins + expectedOrigins reference.Origins }{ { "no origins", @@ -30,7 +31,7 @@ func TestCollectReferenceOrigins_json(t *testing.T) { }, }, `{"attribute": "foo-bar"}`, - lang.ReferenceOrigins{}, + reference.Origins{}, }, { "root attribute single step", @@ -44,7 +45,7 @@ func TestCollectReferenceOrigins_json(t *testing.T) { }, }, `{"attr": "${onestep}"}`, - lang.ReferenceOrigins{ + reference.Origins{ { Addr: lang.Address{ lang.RootStep{Name: "onestep"}, @@ -91,7 +92,7 @@ func TestCollectReferenceOrigins_json(t *testing.T) { "attr2": "${anotherstep}", "attr3": "${onestep}" }`, - lang.ReferenceOrigins{ + reference.Origins{ { Addr: lang.Address{ lang.RootStep{Name: "onestep"}, @@ -160,7 +161,7 @@ func TestCollectReferenceOrigins_json(t *testing.T) { }, }, `{"attr1": "${onestep}-${onestep}-${another.foo.bar}"}`, - lang.ReferenceOrigins{ + reference.Origins{ { Addr: lang.Address{ lang.RootStep{Name: "onestep"}, @@ -231,7 +232,7 @@ func TestCollectReferenceOrigins_json(t *testing.T) { }, }, `{"attr": "${one.two[\"key\"].attr[0]}"}`, - lang.ReferenceOrigins{ + reference.Origins{ { Addr: lang.Address{ lang.RootStep{Name: "one"}, @@ -279,7 +280,7 @@ func TestCollectReferenceOrigins_json(t *testing.T) { } } `, - lang.ReferenceOrigins{ + reference.Origins{ { Addr: lang.Address{ lang.RootStep{Name: "onestep"}, @@ -321,7 +322,7 @@ func TestCollectReferenceOrigins_json(t *testing.T) { } } `, - lang.ReferenceOrigins{ + reference.Origins{ { Addr: lang.Address{ lang.RootStep{Name: "onestep"}, @@ -386,7 +387,7 @@ func TestCollectReferenceOrigins_json(t *testing.T) { } } `, - lang.ReferenceOrigins{ + reference.Origins{ { Addr: lang.Address{ lang.RootStep{Name: "var"}, @@ -471,7 +472,7 @@ func TestCollectReferenceOrigins_json(t *testing.T) { } } `, - lang.ReferenceOrigins{ + reference.Origins{ { Addr: lang.Address{ lang.RootStep{Name: "var"}, @@ -539,7 +540,7 @@ func TestCollectReferenceOrigins_json(t *testing.T) { "set": [ "${var.second}" ], "tuple": [ "${var.third}" ] }`, - lang.ReferenceOrigins{ + reference.Origins{ { Addr: lang.Address{ lang.RootStep{Name: "var"}, @@ -625,7 +626,7 @@ func TestCollectReferenceOrigins_json(t *testing.T) { "attr": "${var.first}" } }`, - lang.ReferenceOrigins{ + reference.Origins{ { Addr: lang.Address{ lang.RootStep{Name: "var"}, @@ -669,7 +670,7 @@ func TestCollectReferenceOrigins_json(t *testing.T) { "key": "${var.first}" } }`, - lang.ReferenceOrigins{ + reference.Origins{ { Addr: lang.Address{ lang.RootStep{Name: "var"}, @@ -709,7 +710,7 @@ func TestCollectReferenceOrigins_json(t *testing.T) { }, }, `{"tuple_cons": [ "${var.one}" ]}`, - lang.ReferenceOrigins{ + reference.Origins{ { Addr: lang.Address{ lang.RootStep{Name: "var"}, @@ -772,7 +773,7 @@ func TestCollectReferenceOrigins_json(t *testing.T) { } } `, - lang.ReferenceOrigins{ + reference.Origins{ { Addr: lang.Address{ lang.RootStep{Name: "var"}, @@ -856,7 +857,7 @@ func TestCollectReferenceOrigins_json(t *testing.T) { "set": [ "${var.two}" ], "tup": [ "${var.three}" ] }`, - lang.ReferenceOrigins{ + reference.Origins{ { Addr: lang.Address{ lang.RootStep{Name: "var"}, diff --git a/decoder/reference_origins_test.go b/decoder/reference_origins_test.go index 1e098fb4..9f3fdf48 100644 --- a/decoder/reference_origins_test.go +++ b/decoder/reference_origins_test.go @@ -7,6 +7,7 @@ import ( "github.com/google/go-cmp/cmp" "github.com/hashicorp/hcl-lang/lang" + "github.com/hashicorp/hcl-lang/reference" "github.com/hashicorp/hcl-lang/schema" "github.com/hashicorp/hcl/v2" "github.com/hashicorp/hcl/v2/hclsyntax" @@ -21,7 +22,7 @@ func TestReferenceOriginAtPos(t *testing.T) { cfg string bodySchema *schema.BodySchema pos hcl.Pos - expectedOrigin *lang.ReferenceOrigin + expectedOrigin *reference.Origin }{ { "empty config", @@ -48,7 +49,7 @@ func TestReferenceOriginAtPos(t *testing.T) { Column: 9, Byte: 8, }, - &lang.ReferenceOrigin{ + &reference.Origin{ Addr: lang.Address{ lang.RootStep{Name: "blah"}, }, @@ -65,7 +66,7 @@ func TestReferenceOriginAtPos(t *testing.T) { Byte: 11, }, }, - Constraints: lang.ReferenceOriginConstraints{{}}, + Constraints: reference.OriginConstraints{{}}, }, }, { @@ -106,7 +107,7 @@ func TestReferenceOriginAtPos(t *testing.T) { Column: 9, Byte: 8, }, - &lang.ReferenceOrigin{ + &reference.Origin{ Addr: lang.Address{ lang.RootStep{Name: "var"}, lang.AttrStep{Name: "myobj"}, @@ -127,7 +128,7 @@ func TestReferenceOriginAtPos(t *testing.T) { Byte: 29, }, }, - Constraints: lang.ReferenceOriginConstraints{{}}, + Constraints: reference.OriginConstraints{{}}, }, }, { @@ -148,7 +149,7 @@ func TestReferenceOriginAtPos(t *testing.T) { Column: 9, Byte: 8, }, - &lang.ReferenceOrigin{ + &reference.Origin{ Addr: lang.Address{ lang.RootStep{Name: "var"}, lang.AttrStep{Name: "myobj"}, @@ -168,7 +169,7 @@ func TestReferenceOriginAtPos(t *testing.T) { Byte: 31, }, }, - Constraints: lang.ReferenceOriginConstraints{{}}, + Constraints: reference.OriginConstraints{{}}, }, }, { @@ -189,7 +190,7 @@ func TestReferenceOriginAtPos(t *testing.T) { Column: 9, Byte: 8, }, - &lang.ReferenceOrigin{ + &reference.Origin{ Addr: lang.Address{ lang.RootStep{Name: "var"}, lang.AttrStep{Name: "myobj"}, @@ -209,7 +210,7 @@ func TestReferenceOriginAtPos(t *testing.T) { Byte: 28, }, }, - Constraints: lang.ReferenceOriginConstraints{{}}, + Constraints: reference.OriginConstraints{{}}, }, }, { @@ -242,7 +243,7 @@ func TestReferenceOriginAtPos(t *testing.T) { Column: 11, Byte: 30, }, - &lang.ReferenceOrigin{ + &reference.Origin{ Addr: lang.Address{ lang.RootStep{Name: "var"}, lang.AttrStep{Name: "myobj"}, @@ -262,7 +263,7 @@ func TestReferenceOriginAtPos(t *testing.T) { Byte: 50, }, }, - Constraints: lang.ReferenceOriginConstraints{{}}, + Constraints: reference.OriginConstraints{{}}, }, }, { @@ -287,7 +288,7 @@ func TestReferenceOriginAtPos(t *testing.T) { Column: 11, Byte: 12, }, - &lang.ReferenceOrigin{ + &reference.Origin{ Addr: lang.Address{ lang.RootStep{Name: "var"}, lang.AttrStep{Name: "test"}, @@ -305,7 +306,7 @@ func TestReferenceOriginAtPos(t *testing.T) { Byte: 17, }, }, - Constraints: lang.ReferenceOriginConstraints{ + Constraints: reference.OriginConstraints{ {OfScopeId: lang.ScopeId("test")}, }, }, @@ -357,24 +358,24 @@ func TestReferenceOriginAtPos_json(t *testing.T) { func TestReferenceOriginsTargeting(t *testing.T) { testCases := []struct { name string - allOrigins lang.ReferenceOrigins - refTarget lang.ReferenceTarget - expectedOrigins lang.ReferenceOrigins + allOrigins reference.Origins + refTarget reference.Target + expectedOrigins reference.Origins }{ { "no origins", - lang.ReferenceOrigins{}, - lang.ReferenceTarget{ + reference.Origins{}, + reference.Target{ Addr: lang.Address{ lang.RootStep{Name: "test"}, }, Type: cty.String, }, - lang.ReferenceOrigins{}, + reference.Origins{}, }, { "exact address match", - lang.ReferenceOrigins{ + reference.Origins{ { Addr: lang.Address{ lang.RootStep{Name: "test"}, @@ -385,25 +386,25 @@ func TestReferenceOriginsTargeting(t *testing.T) { lang.RootStep{Name: "test"}, lang.AttrStep{Name: "secondstep"}, }, - Constraints: lang.ReferenceOriginConstraints{ + Constraints: reference.OriginConstraints{ {OfType: cty.String}, }, }, }, - lang.ReferenceTarget{ + reference.Target{ Addr: lang.Address{ lang.RootStep{Name: "test"}, lang.AttrStep{Name: "secondstep"}, }, Type: cty.String, }, - lang.ReferenceOrigins{ + reference.Origins{ { Addr: lang.Address{ lang.RootStep{Name: "test"}, lang.AttrStep{Name: "secondstep"}, }, - Constraints: lang.ReferenceOriginConstraints{ + Constraints: reference.OriginConstraints{ {OfType: cty.String}, }, }, @@ -411,7 +412,7 @@ func TestReferenceOriginsTargeting(t *testing.T) { }, { "no match", - lang.ReferenceOrigins{ + reference.Origins{ { Addr: lang.Address{ lang.RootStep{Name: "test"}, @@ -424,18 +425,18 @@ func TestReferenceOriginsTargeting(t *testing.T) { }, }, }, - lang.ReferenceTarget{ + reference.Target{ Addr: lang.Address{ lang.RootStep{Name: "test"}, lang.AttrStep{Name: "different"}, }, Type: cty.String, }, - lang.ReferenceOrigins{}, + reference.Origins{}, }, { "match of nested target - two matches", - lang.ReferenceOrigins{ + reference.Origins{ { Addr: lang.Address{ lang.RootStep{Name: "foo"}, @@ -445,7 +446,7 @@ func TestReferenceOriginsTargeting(t *testing.T) { Addr: lang.Address{ lang.RootStep{Name: "test"}, }, - Constraints: lang.ReferenceOriginConstraints{ + Constraints: reference.OriginConstraints{ {OfType: cty.DynamicPseudoType}, }, }, @@ -454,19 +455,19 @@ func TestReferenceOriginsTargeting(t *testing.T) { lang.RootStep{Name: "test"}, lang.AttrStep{Name: "second"}, }, - Constraints: lang.ReferenceOriginConstraints{ + Constraints: reference.OriginConstraints{ {OfType: cty.String}, }, }, }, - lang.ReferenceTarget{ + reference.Target{ Addr: lang.Address{ lang.RootStep{Name: "test"}, }, Type: cty.Object(map[string]cty.Type{ "second": cty.String, }), - NestedTargets: lang.ReferenceTargets{ + NestedTargets: reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "test"}, @@ -476,12 +477,12 @@ func TestReferenceOriginsTargeting(t *testing.T) { }, }, }, - lang.ReferenceOrigins{ + reference.Origins{ { Addr: lang.Address{ lang.RootStep{Name: "test"}, }, - Constraints: lang.ReferenceOriginConstraints{ + Constraints: reference.OriginConstraints{ {OfType: cty.DynamicPseudoType}, }, }, @@ -490,7 +491,7 @@ func TestReferenceOriginsTargeting(t *testing.T) { lang.RootStep{Name: "test"}, lang.AttrStep{Name: "second"}, }, - Constraints: lang.ReferenceOriginConstraints{ + Constraints: reference.OriginConstraints{ {OfType: cty.String}, }, }, @@ -498,74 +499,74 @@ func TestReferenceOriginsTargeting(t *testing.T) { }, { "loose match of target of unknown type", - lang.ReferenceOrigins{ + reference.Origins{ { Addr: lang.Address{ lang.RootStep{Name: "foo"}, }, - Constraints: lang.ReferenceOriginConstraints{{}}, + Constraints: reference.OriginConstraints{{}}, }, { Addr: lang.Address{ lang.RootStep{Name: "test"}, }, - Constraints: lang.ReferenceOriginConstraints{{}}, + Constraints: reference.OriginConstraints{{}}, }, { Addr: lang.Address{ lang.RootStep{Name: "test"}, lang.AttrStep{Name: "second"}, }, - Constraints: lang.ReferenceOriginConstraints{{}}, + Constraints: reference.OriginConstraints{{}}, }, }, - lang.ReferenceTarget{ + reference.Target{ Addr: lang.Address{ lang.RootStep{Name: "test"}, }, Type: cty.DynamicPseudoType, }, - lang.ReferenceOrigins{ + reference.Origins{ { Addr: lang.Address{ lang.RootStep{Name: "test"}, }, - Constraints: lang.ReferenceOriginConstraints{{}}, + Constraints: reference.OriginConstraints{{}}, }, { Addr: lang.Address{ lang.RootStep{Name: "test"}, lang.AttrStep{Name: "second"}, }, - Constraints: lang.ReferenceOriginConstraints{{}}, + Constraints: reference.OriginConstraints{{}}, }, }, }, { "mismatch of target nil type", - lang.ReferenceOrigins{ + reference.Origins{ { Addr: lang.Address{ lang.RootStep{Name: "test"}, }, - Constraints: lang.ReferenceOriginConstraints{ + Constraints: reference.OriginConstraints{ {OfScopeId: lang.ScopeId("test")}, }, }, }, - lang.ReferenceTarget{ + reference.Target{ Addr: lang.Address{ lang.RootStep{Name: "test"}, }, ScopeId: lang.ScopeId("test"), Type: cty.String, }, - lang.ReferenceOrigins{}, + reference.Origins{}, }, // JSON edge cases { "constraint-less origin mismatching scope-only target", - lang.ReferenceOrigins{ + reference.Origins{ { Addr: lang.Address{ lang.RootStep{Name: "var"}, @@ -574,7 +575,7 @@ func TestReferenceOriginsTargeting(t *testing.T) { Constraints: nil, }, }, - lang.ReferenceTarget{ + reference.Target{ Addr: lang.Address{ lang.RootStep{Name: "var"}, lang.AttrStep{Name: "alpha"}, @@ -582,11 +583,11 @@ func TestReferenceOriginsTargeting(t *testing.T) { ScopeId: "variable", Type: cty.NilType, }, - lang.ReferenceOrigins{}, + reference.Origins{}, }, { "constraint-less origin matching type-aware target", - lang.ReferenceOrigins{ + reference.Origins{ { Addr: lang.Address{ lang.RootStep{Name: "var"}, @@ -595,7 +596,7 @@ func TestReferenceOriginsTargeting(t *testing.T) { Constraints: nil, }, }, - lang.ReferenceTarget{ + reference.Target{ Addr: lang.Address{ lang.RootStep{Name: "var"}, lang.AttrStep{Name: "beta"}, @@ -603,7 +604,7 @@ func TestReferenceOriginsTargeting(t *testing.T) { ScopeId: "variable", Type: cty.DynamicPseudoType, }, - lang.ReferenceOrigins{ + reference.Origins{ { Addr: lang.Address{ lang.RootStep{Name: "var"}, diff --git a/decoder/reference_targets.go b/decoder/reference_targets.go index f3fa9534..e0dc3b9f 100644 --- a/decoder/reference_targets.go +++ b/decoder/reference_targets.go @@ -2,11 +2,10 @@ package decoder import ( "bytes" - "errors" "sort" - "strings" "github.com/hashicorp/hcl-lang/lang" + "github.com/hashicorp/hcl-lang/reference" "github.com/hashicorp/hcl-lang/schema" "github.com/hashicorp/hcl/v2" "github.com/hashicorp/hcl/v2/ext/typeexpr" @@ -17,16 +16,14 @@ import ( // ReferenceTargetForOrigin returns the first ReferenceTarget // with matching ReferenceOrigin Address, if one exists, else nil -func (d *PathDecoder) ReferenceTargetForOrigin(refOrigin lang.ReferenceOrigin) (*lang.ReferenceTarget, error) { +func (d *PathDecoder) ReferenceTargetForOrigin(refOrigin reference.Origin) (*reference.Target, error) { if d.pathCtx.ReferenceTargets == nil { return nil, nil } - allTargets := ReferenceTargets(d.pathCtx.ReferenceTargets) - - ref, err := allTargets.FirstTargetableBy(refOrigin) + ref, err := d.pathCtx.ReferenceTargets.FirstTargetableBy(refOrigin) if err != nil { - if _, ok := err.(*NoRefTargetFound); ok { + if _, ok := err.(*reference.NoTargetFound); ok { return nil, nil } return nil, err @@ -35,21 +32,15 @@ func (d *PathDecoder) ReferenceTargetForOrigin(refOrigin lang.ReferenceOrigin) ( return &ref, nil } -func (d *PathDecoder) ReferenceTargetsInFile(file string) (lang.ReferenceTargets, error) { - if d.pathCtx.ReferenceTargets == nil { - return nil, nil - } - - allTargets := ReferenceTargets(d.pathCtx.ReferenceTargets) - - targets := make(lang.ReferenceTargets, 0) +func (d *PathDecoder) ReferenceTargetsInFile(file string) (reference.Targets, error) { + targets := make(reference.Targets, 0) // It is practically impossible for nested targets to be placed // in a separate file from their parent target, so we save // some cycles here by limiting walk just to the top level. depth := 0 - allTargets.DeepWalk(func(target lang.ReferenceTarget) error { + d.pathCtx.ReferenceTargets.DeepWalk(func(target reference.Target) error { if target.RangePtr == nil { return nil } @@ -62,15 +53,13 @@ func (d *PathDecoder) ReferenceTargetsInFile(file string) (lang.ReferenceTargets return targets, nil } -func (d *PathDecoder) OutermostReferenceTargetsAtPos(file string, pos hcl.Pos) (lang.ReferenceTargets, error) { +func (d *PathDecoder) OutermostReferenceTargetsAtPos(file string, pos hcl.Pos) (reference.Targets, error) { if d.pathCtx.ReferenceTargets == nil { return nil, nil } - allTargets := ReferenceTargets(d.pathCtx.ReferenceTargets) - - matchingTargets := make(lang.ReferenceTargets, 0) - for _, target := range allTargets { + matchingTargets := make(reference.Targets, 0) + for _, target := range d.pathCtx.ReferenceTargets { if target.RangePtr == nil { continue } @@ -85,7 +74,7 @@ func (d *PathDecoder) OutermostReferenceTargetsAtPos(file string, pos hcl.Pos) ( return matchingTargets, nil } -func (d *PathDecoder) InnermostReferenceTargetsAtPos(file string, pos hcl.Pos) (lang.ReferenceTargets, error) { +func (d *PathDecoder) InnermostReferenceTargetsAtPos(file string, pos hcl.Pos) (reference.Targets, error) { if d.pathCtx.ReferenceTargets == nil { return nil, nil } @@ -95,12 +84,10 @@ func (d *PathDecoder) InnermostReferenceTargetsAtPos(file string, pos hcl.Pos) ( return targets, nil } -func (d *PathDecoder) innermostReferenceTargetsAtPos(targets lang.ReferenceTargets, file string, pos hcl.Pos) (lang.ReferenceTargets, bool) { - allTargets := ReferenceTargets(targets) - - matchingTargets := make(lang.ReferenceTargets, 0) +func (d *PathDecoder) innermostReferenceTargetsAtPos(targets reference.Targets, file string, pos hcl.Pos) (reference.Targets, bool) { + matchingTargets := make(reference.Targets, 0) - for _, target := range allTargets { + for _, target := range targets { if target.RangePtr == nil { continue } @@ -112,7 +99,7 @@ func (d *PathDecoder) innermostReferenceTargetsAtPos(targets lang.ReferenceTarge } } - var innermostTargets lang.ReferenceTargets + var innermostTargets reference.Targets for _, target := range matchingTargets { if target.DefRangePtr != nil { @@ -135,178 +122,13 @@ func (d *PathDecoder) innermostReferenceTargetsAtPos(targets lang.ReferenceTarge return innermostTargets, len(innermostTargets) > 0 } -type ReferenceTarget lang.ReferenceTarget - -func (ref ReferenceTarget) MatchesConstraint(te schema.TraversalExpr) bool { - return ref.MatchesScopeId(te.OfScopeId) && ref.ConformsToType(te.OfType) -} - -func (ref ReferenceTarget) MatchesScopeId(scopeId lang.ScopeId) bool { - return scopeId == "" || ref.ScopeId == scopeId -} - -func (ref ReferenceTarget) ConformsToType(typ cty.Type) bool { - conformsToType := false - if typ != cty.NilType && ref.Type != cty.NilType { - if ref.Type == cty.DynamicPseudoType { - // anything conforms with dynamic - conformsToType = true - } - if errs := ref.Type.TestConformance(typ); len(errs) == 0 { - conformsToType = true - } - } - - return conformsToType || (typ == cty.NilType && ref.Type == cty.NilType) -} - -func (target ReferenceTarget) IsTargetableBy(origin lang.ReferenceOrigin) bool { - if len(target.Addr) > len(origin.Addr) { - return false - } - - originAddr := Address(origin.Addr) - - matchesCons := false - - if len(origin.Constraints) == 0 && target.Type != cty.NilType { - matchesCons = true - } - - for _, cons := range origin.Constraints { - if !target.MatchesScopeId(cons.OfScopeId) { - continue - } - - if target.Type == cty.DynamicPseudoType { - originAddr = Address(origin.Addr).FirstSteps(uint(len(target.Addr))) - matchesCons = true - continue - } - if cons.OfType != cty.NilType && target.ConformsToType(cons.OfType) { - matchesCons = true - } - if cons.OfType == cty.NilType && target.Type == cty.NilType { - // This just simplifies testing - matchesCons = true - } - } - - return Address(target.Addr).Equals(originAddr) && matchesCons -} - -type ReferenceTargets lang.ReferenceTargets - -type RefTargetWalkFunc func(lang.ReferenceTarget) error - -var StopWalking error = errors.New("stop walking") - -const InfiniteDepth = -1 - -func (refs ReferenceTargets) DeepWalk(f RefTargetWalkFunc, depth int) { - w := refTargetDeepWalker{ - WalkFunc: f, - Depth: depth, - } - w.Walk(refs) -} - -type refTargetDeepWalker struct { - WalkFunc RefTargetWalkFunc - Depth int - - currentDepth int -} - -func (w refTargetDeepWalker) Walk(refTargets ReferenceTargets) { - for _, ref := range refTargets { - err := w.WalkFunc(ref) - if err == StopWalking { - return - } - - if len(ref.NestedTargets) > 0 && (w.Depth == InfiniteDepth || w.Depth > w.currentDepth) { - irefs := ReferenceTargets(ref.NestedTargets) - w.currentDepth++ - w.Walk(irefs) - w.currentDepth-- - } - } -} - -func (refs ReferenceTargets) MatchWalk(te schema.TraversalExpr, prefix string, f RefTargetWalkFunc) { - for _, ref := range refs { - if strings.HasPrefix(ref.Addr.String(), string(prefix)) { - nestedMatches := ReferenceTargets(ref.NestedTargets).ContainsMatch(te, prefix) - if ReferenceTarget(ref).MatchesConstraint(te) || nestedMatches { - f(ref) - continue - } - } - - ReferenceTargets(ref.NestedTargets).MatchWalk(te, prefix, f) - } -} - -func (refs ReferenceTargets) ContainsMatch(te schema.TraversalExpr, prefix string) bool { - for _, ref := range refs { - if strings.HasPrefix(ref.Addr.String(), string(prefix)) && - ReferenceTarget(ref).MatchesConstraint(te) { - return true - } - if len(ref.NestedTargets) > 0 { - if match := ReferenceTargets(ref.NestedTargets).ContainsMatch(te, prefix); match { - return true - } - } - } - return false -} - -func (refs ReferenceTargets) FirstTargetableBy(origin lang.ReferenceOrigin) (lang.ReferenceTarget, error) { - var matchingReference *lang.ReferenceTarget - - refs.DeepWalk(func(ref lang.ReferenceTarget) error { - if ReferenceTarget(ref).IsTargetableBy(origin) { - matchingReference = &ref - return StopWalking - } - return nil - }, InfiniteDepth) - - if matchingReference == nil { - return lang.ReferenceTarget{}, &NoRefTargetFound{} - } - - return *matchingReference, nil -} - -type Address lang.Address - -func (a Address) Equals(addr Address) bool { - if len(a) != len(addr) { - return false - } - for i, step := range a { - if step.String() != addr[i].String() { - return false - } - } - - return true -} - -func (a Address) FirstSteps(steps uint) Address { - return a[0:steps] -} - -func (d *PathDecoder) CollectReferenceTargets() (lang.ReferenceTargets, error) { +func (d *PathDecoder) CollectReferenceTargets() (reference.Targets, error) { if d.pathCtx.Schema == nil { // unable to collect reference targets without schema return nil, &NoSchemaError{} } - refs := make(lang.ReferenceTargets, 0) + refs := make(reference.Targets, 0) files := d.filenames() for _, filename := range files { f, err := d.fileByName(filename) @@ -320,11 +142,11 @@ func (d *PathDecoder) CollectReferenceTargets() (lang.ReferenceTargets, error) { return refs, nil } -func (d *PathDecoder) decodeReferenceTargetsForBody(body hcl.Body, parentBlock *blockContent, bodySchema *schema.BodySchema) lang.ReferenceTargets { - refs := make(lang.ReferenceTargets, 0) +func (d *PathDecoder) decodeReferenceTargetsForBody(body hcl.Body, parentBlock *blockContent, bodySchema *schema.BodySchema) reference.Targets { + refs := make(reference.Targets, 0) if bodySchema == nil { - return lang.ReferenceTargets{} + return reference.Targets{} } content := decodeBody(body, bodySchema) @@ -364,7 +186,7 @@ func (d *PathDecoder) decodeReferenceTargetsForBody(body hcl.Body, parentBlock * } if bSchema.Address.AsReference { - ref := lang.ReferenceTarget{ + ref := reference.Target{ Addr: addr, ScopeId: bSchema.Address.ScopeId, DefRangePtr: blk.DefRange.Ptr(), @@ -378,10 +200,10 @@ func (d *PathDecoder) decodeReferenceTargetsForBody(body hcl.Body, parentBlock * refs = append(refs, referenceAsTypeOf(blk.Block, blk.Range.Ptr(), bSchema, addr)...) } - var bodyRef lang.ReferenceTarget + var bodyRef reference.Target if bSchema.Address.BodyAsData { - bodyRef = lang.ReferenceTarget{ + bodyRef = reference.Target{ Addr: addr, ScopeId: bSchema.Address.ScopeId, DefRangePtr: blk.DefRange.Ptr(), @@ -404,7 +226,7 @@ func (d *PathDecoder) decodeReferenceTargetsForBody(body hcl.Body, parentBlock * if bSchema.Address.DependentBodyAsData { if !bSchema.Address.BodyAsData { - bodyRef = lang.ReferenceTarget{ + bodyRef = reference.Target{ Addr: addr, ScopeId: bSchema.Address.ScopeId, DefRangePtr: blk.DefRange.Ptr(), @@ -420,7 +242,7 @@ func (d *PathDecoder) decodeReferenceTargetsForBody(body hcl.Body, parentBlock * if err != nil { continue } - bodyRef.NestedTargets = make(lang.ReferenceTargets, 0) + bodyRef.NestedTargets = make(reference.Targets, 0) fullSchema = mergedSchema } @@ -449,8 +271,8 @@ func (d *PathDecoder) decodeReferenceTargetsForBody(body hcl.Body, parentBlock * return refs } -func decodeTargetableBody(body hcl.Body, parentBlock *blockContent, tt *schema.Targetable) lang.ReferenceTarget { - target := lang.ReferenceTarget{ +func decodeTargetableBody(body hcl.Body, parentBlock *blockContent, tt *schema.Targetable) reference.Target { + target := reference.Target{ Addr: tt.Address.Copy(), ScopeId: tt.ScopeId, RangePtr: parentBlock.Range.Ptr(), @@ -460,7 +282,7 @@ func decodeTargetableBody(body hcl.Body, parentBlock *blockContent, tt *schema.T } if tt.NestedTargetables != nil { - target.NestedTargets = make(lang.ReferenceTargets, len(tt.NestedTargetables)) + target.NestedTargets = make(reference.Targets, len(tt.NestedTargetables)) for i, ntt := range tt.NestedTargetables { target.NestedTargets[i] = decodeTargetableBody(body, parentBlock, ntt) } @@ -469,13 +291,13 @@ func decodeTargetableBody(body hcl.Body, parentBlock *blockContent, tt *schema.T return target } -func decodeReferenceTargetsForAttribute(attr *hcl.Attribute, attrSchema *schema.AttributeSchema) lang.ReferenceTargets { - refs := make(lang.ReferenceTargets, 0) +func decodeReferenceTargetsForAttribute(attr *hcl.Attribute, attrSchema *schema.AttributeSchema) reference.Targets { + refs := make(reference.Targets, 0) attrAddr, ok := resolveAttributeAddress(attr, attrSchema.Address) if ok { if attrSchema.Address.AsReference { - ref := lang.ReferenceTarget{ + ref := reference.Target{ Addr: attrAddr, ScopeId: attrSchema.Address.ScopeId, DefRangePtr: &attr.NameRange, @@ -498,7 +320,7 @@ func decodeReferenceTargetsForAttribute(attr *hcl.Attribute, attrSchema *schema. scopeId := attrSchema.Address.ScopeId - ref := lang.ReferenceTarget{ + ref := reference.Target{ Addr: attrAddr, Type: t, ScopeId: scopeId, @@ -508,7 +330,7 @@ func decodeReferenceTargetsForAttribute(attr *hcl.Attribute, attrSchema *schema. } if attr.Expr != nil && !t.IsPrimitiveType() { - ref.NestedTargets = make(lang.ReferenceTargets, 0) + ref.NestedTargets = make(reference.Targets, 0) ref.NestedTargets = append(ref.NestedTargets, decodeReferenceTargetsForComplexTypeExpr(attrAddr, attr.Expr, t, scopeId)...) } @@ -522,8 +344,8 @@ func decodeReferenceTargetsForAttribute(attr *hcl.Attribute, attrSchema *schema. return refs } -func decodeReferenceTargetsForComplexTypeExpr(addr lang.Address, expr hcl.Expression, t cty.Type, scopeId lang.ScopeId) lang.ReferenceTargets { - refs := make(lang.ReferenceTargets, 0) +func decodeReferenceTargetsForComplexTypeExpr(addr lang.Address, expr hcl.Expression, t cty.Type, scopeId lang.ScopeId) reference.Targets { + refs := make(reference.Targets, 0) if expr == nil { return refs @@ -540,14 +362,14 @@ func decodeReferenceTargetsForComplexTypeExpr(addr lang.Address, expr hcl.Expres elemAddr := append(addr.Copy(), lang.IndexStep{Key: cty.NumberIntVal(int64(i))}) elemType := t.ElementType() - ref := lang.ReferenceTarget{ + ref := reference.Target{ Addr: elemAddr, Type: elemType, ScopeId: scopeId, RangePtr: item.Range().Ptr(), } if !elemType.IsPrimitiveType() { - ref.NestedTargets = make(lang.ReferenceTargets, 0) + ref.NestedTargets = make(reference.Targets, 0) ref.NestedTargets = append(ref.NestedTargets, decodeReferenceTargetsForComplexTypeExpr(elemAddr, item, elemType, scopeId)...) } @@ -570,7 +392,7 @@ func decodeReferenceTargetsForComplexTypeExpr(addr lang.Address, expr hcl.Expres attrAddr := append(addr.Copy(), lang.AttrStep{Name: key.AsString()}) rng := hcl.RangeBetween(item.KeyExpr.Range(), item.ValueExpr.Range()) - ref := lang.ReferenceTarget{ + ref := reference.Target{ Addr: attrAddr, Type: attrType, ScopeId: scopeId, @@ -578,7 +400,7 @@ func decodeReferenceTargetsForComplexTypeExpr(addr lang.Address, expr hcl.Expres RangePtr: rng.Ptr(), } if !attrType.IsPrimitiveType() { - ref.NestedTargets = make(lang.ReferenceTargets, 0) + ref.NestedTargets = make(reference.Targets, 0) ref.NestedTargets = append(ref.NestedTargets, decodeReferenceTargetsForComplexTypeExpr(attrAddr, item.ValueExpr, attrType, scopeId)...) } @@ -602,7 +424,7 @@ func decodeReferenceTargetsForComplexTypeExpr(addr lang.Address, expr hcl.Expres elemAddr := append(addr.Copy(), lang.IndexStep{Key: key}) rng := hcl.RangeBetween(item.KeyExpr.Range(), item.ValueExpr.Range()) - ref := lang.ReferenceTarget{ + ref := reference.Target{ Addr: elemAddr, Type: elemType, ScopeId: scopeId, @@ -610,7 +432,7 @@ func decodeReferenceTargetsForComplexTypeExpr(addr lang.Address, expr hcl.Expres RangePtr: rng.Ptr(), } if !elemType.IsPrimitiveType() { - ref.NestedTargets = make(lang.ReferenceTargets, 0) + ref.NestedTargets = make(reference.Targets, 0) ref.NestedTargets = append(ref.NestedTargets, decodeReferenceTargetsForComplexTypeExpr(elemAddr, item.ValueExpr, elemType, scopeId)...) } @@ -622,8 +444,8 @@ func decodeReferenceTargetsForComplexTypeExpr(addr lang.Address, expr hcl.Expres return refs } -func referenceAsTypeOf(block *hcl.Block, rngPtr *hcl.Range, bSchema *schema.BlockSchema, addr lang.Address) lang.ReferenceTargets { - ref := lang.ReferenceTarget{ +func referenceAsTypeOf(block *hcl.Block, rngPtr *hcl.Range, bSchema *schema.BlockSchema, addr lang.Address) reference.Targets { + ref := reference.Target{ Addr: addr, ScopeId: bSchema.Address.ScopeId, DefRangePtr: block.DefRange.Ptr(), @@ -637,14 +459,14 @@ func referenceAsTypeOf(block *hcl.Block, rngPtr *hcl.Range, bSchema *schema.Bloc attrs, diags := block.Body.JustAttributes() if diags.HasErrors() { - return lang.ReferenceTargets{ref} + return reference.Targets{ref} } if bSchema.Address.AsTypeOf.AttributeExpr != "" { typeDecl, ok := asTypeOfAttrExpr(attrs, bSchema) if !ok && bSchema.Address.AsTypeOf.AttributeValue == "" { // nothing to fall back to, exit early - return lang.ReferenceTargets{ref} + return reference.Targets{ref} } ref.Type = typeDecl } @@ -652,21 +474,21 @@ func referenceAsTypeOf(block *hcl.Block, rngPtr *hcl.Range, bSchema *schema.Bloc if bSchema.Address.AsTypeOf.AttributeValue != "" { attr, ok := attrs[bSchema.Address.AsTypeOf.AttributeValue] if !ok { - return lang.ReferenceTargets{ref} + return reference.Targets{ref} } value, diags := attr.Expr.Value(nil) if diags.HasErrors() { - return lang.ReferenceTargets{ref} + return reference.Targets{ref} } val, err := convert.Convert(value, ref.Type) if err != nil { // type does not comply with type constraint - return lang.ReferenceTargets{ref} + return reference.Targets{ref} } ref.Type = val.Type() } - return lang.ReferenceTargets{ref} + return reference.Targets{ref} } func asTypeOfAttrExpr(attrs hcl.Attributes, bSchema *schema.BlockSchema) (cty.Type, bool) { @@ -755,8 +577,8 @@ func exprConstraintToDataType(expr schema.ExprConstraints) (cty.Type, bool) { return cty.NilType, false } -func referenceTargetsForExpr(expr hcl.Expression, ec ExprConstraints) lang.ReferenceTargets { - refs := make(lang.ReferenceTargets, 0) +func referenceTargetsForExpr(expr hcl.Expression, ec ExprConstraints) reference.Targets { + refs := make(reference.Targets, 0) switch e := expr.(type) { // TODO: Support all expression types (list/set/map literals) @@ -764,12 +586,12 @@ func referenceTargetsForExpr(expr hcl.Expression, ec ExprConstraints) lang.Refer tes, ok := ec.TraversalExprs() if !ok { // unknown traversal - return lang.ReferenceTargets{} + return reference.Targets{} } addr, err := lang.TraversalToAddress(e.AsTraversal()) if err != nil { - return lang.ReferenceTargets{} + return reference.Targets{} } for _, te := range tes { @@ -778,7 +600,7 @@ func referenceTargetsForExpr(expr hcl.Expression, ec ExprConstraints) lang.Refer continue } - refs = append(refs, lang.ReferenceTarget{ + refs = append(refs, reference.Target{ Addr: addr, ScopeId: te.Address.ScopeId, RangePtr: e.SrcRange.Ptr(), @@ -863,8 +685,8 @@ 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) lang.ReferenceTargets { - refs := make(lang.ReferenceTargets, 0) +func (d *PathDecoder) collectInferredReferenceTargetsForBody(addr lang.Address, scopeId lang.ScopeId, body hcl.Body, bodySchema *schema.BodySchema) reference.Targets { + refs := make(reference.Targets, 0) content := decodeBody(body, bodySchema) @@ -877,7 +699,7 @@ func (d *PathDecoder) collectInferredReferenceTargetsForBody(addr lang.Address, attrAddr := append(addr.Copy(), lang.AttrStep{Name: name}) - ref := lang.ReferenceTarget{ + ref := reference.Target{ Addr: attrAddr, ScopeId: scopeId, Type: attrType, @@ -893,7 +715,7 @@ func (d *PathDecoder) collectInferredReferenceTargetsForBody(addr lang.Address, } if attrExpr != nil && !attrType.IsPrimitiveType() { - ref.NestedTargets = make(lang.ReferenceTargets, 0) + ref.NestedTargets = make(reference.Targets, 0) ref.NestedTargets = append(ref.NestedTargets, decodeReferenceTargetsForComplexTypeExpr(attrAddr, attrExpr, attrType, scopeId)...) } @@ -909,7 +731,7 @@ func (d *PathDecoder) collectInferredReferenceTargetsForBody(addr lang.Address, blk := bCollection.Blocks[0] - blockRef := lang.ReferenceTarget{ + blockRef := reference.Target{ Addr: blockAddr, ScopeId: scopeId, Type: cty.Object(bodySchemaAsAttrTypes(bCollection.Schema.Body)), @@ -928,7 +750,7 @@ func (d *PathDecoder) collectInferredReferenceTargetsForBody(addr lang.Address, copy(blockAddr, addr) blockAddr = append(blockAddr, lang.AttrStep{Name: bType}) - blockRef := lang.ReferenceTarget{ + blockRef := reference.Target{ Addr: blockAddr, ScopeId: scopeId, Type: cty.List(cty.Object(bodySchemaAsAttrTypes(bCollection.Schema.Body))), @@ -943,7 +765,7 @@ func (d *PathDecoder) collectInferredReferenceTargetsForBody(addr lang.Address, Key: cty.NumberIntVal(int64(i)), }) - elemRef := lang.ReferenceTarget{ + elemRef := reference.Target{ Addr: elemAddr, ScopeId: scopeId, Type: cty.Object(bodySchemaAsAttrTypes(bCollection.Schema.Body)), @@ -980,7 +802,7 @@ func (d *PathDecoder) collectInferredReferenceTargetsForBody(addr lang.Address, copy(blockAddr, addr) blockAddr = append(blockAddr, lang.AttrStep{Name: bType}) - blockRef := lang.ReferenceTarget{ + blockRef := reference.Target{ Addr: blockAddr, ScopeId: scopeId, Type: cty.Set(cty.Object(bodySchemaAsAttrTypes(bCollection.Schema.Body))), @@ -1012,7 +834,7 @@ func (d *PathDecoder) collectInferredReferenceTargetsForBody(addr lang.Address, copy(blockAddr, addr) blockAddr = append(blockAddr, lang.AttrStep{Name: bType}) - blockRef := lang.ReferenceTarget{ + blockRef := reference.Target{ Addr: blockAddr, ScopeId: scopeId, Type: cty.Map(cty.Object(bodySchemaAsAttrTypes(bCollection.Schema.Body))), @@ -1029,7 +851,7 @@ func (d *PathDecoder) collectInferredReferenceTargetsForBody(addr lang.Address, refType := cty.Object(bodySchemaAsAttrTypes(bCollection.Schema.Body)) - elemRef := lang.ReferenceTarget{ + elemRef := reference.Target{ Addr: elemAddr, ScopeId: scopeId, Type: refType, diff --git a/decoder/reference_targets_collect_hcl_test.go b/decoder/reference_targets_collect_hcl_test.go index 4eba3461..e6ba125d 100644 --- a/decoder/reference_targets_collect_hcl_test.go +++ b/decoder/reference_targets_collect_hcl_test.go @@ -6,6 +6,7 @@ import ( "github.com/google/go-cmp/cmp" "github.com/hashicorp/hcl-lang/lang" + "github.com/hashicorp/hcl-lang/reference" "github.com/hashicorp/hcl-lang/schema" "github.com/hashicorp/hcl/v2" "github.com/hashicorp/hcl/v2/hclsyntax" @@ -18,7 +19,7 @@ func TestCollectReferenceTargets_hcl(t *testing.T) { name string schema *schema.BodySchema cfg string - expectedRefs lang.ReferenceTargets + expectedRefs reference.Targets }{ { "root attribute as reference", @@ -40,7 +41,7 @@ func TestCollectReferenceTargets_hcl(t *testing.T) { }, `testattr = "example" `, - lang.ReferenceTargets{ + reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "special"}, @@ -95,7 +96,7 @@ func TestCollectReferenceTargets_hcl(t *testing.T) { }, `testattr = "example" `, - lang.ReferenceTargets{ + reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "special"}, @@ -150,7 +151,7 @@ func TestCollectReferenceTargets_hcl(t *testing.T) { }, `testattr = "example" `, - lang.ReferenceTargets{ + reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "special"}, @@ -209,7 +210,7 @@ func TestCollectReferenceTargets_hcl(t *testing.T) { nestedattr = "test" } `, - lang.ReferenceTargets{ + reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "special"}, @@ -244,7 +245,7 @@ func TestCollectReferenceTargets_hcl(t *testing.T) { Byte: 8, }, }, - NestedTargets: lang.ReferenceTargets{ + NestedTargets: reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "special"}, @@ -304,7 +305,7 @@ func TestCollectReferenceTargets_hcl(t *testing.T) { nestedattr = "test" } `, - lang.ReferenceTargets{ + reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "special"}, @@ -337,7 +338,7 @@ func TestCollectReferenceTargets_hcl(t *testing.T) { Byte: 8, }, }, - NestedTargets: lang.ReferenceTargets{ + NestedTargets: reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "special"}, @@ -395,7 +396,7 @@ func TestCollectReferenceTargets_hcl(t *testing.T) { }, `testattr = [ "example" ] `, - lang.ReferenceTargets{ + reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "special"}, @@ -428,7 +429,7 @@ func TestCollectReferenceTargets_hcl(t *testing.T) { Byte: 24, }, }, - NestedTargets: lang.ReferenceTargets{ + NestedTargets: reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "special"}, @@ -478,7 +479,7 @@ func TestCollectReferenceTargets_hcl(t *testing.T) { }, `testattr = [ "example" ] `, - lang.ReferenceTargets{ + reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "special"}, @@ -511,7 +512,7 @@ func TestCollectReferenceTargets_hcl(t *testing.T) { Byte: 24, }, }, - NestedTargets: lang.ReferenceTargets{ + NestedTargets: reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "special"}, @@ -556,7 +557,7 @@ func TestCollectReferenceTargets_hcl(t *testing.T) { }, `testattr = "example" `, - lang.ReferenceTargets{}, + reference.Targets{}, }, { "block with mismatching steps", @@ -589,7 +590,7 @@ func TestCollectReferenceTargets_hcl(t *testing.T) { attr = 3 } `, - lang.ReferenceTargets{}, + reference.Targets{}, }, { "block as ref only", @@ -627,7 +628,7 @@ func TestCollectReferenceTargets_hcl(t *testing.T) { name = "lorem ipsum" } `, - lang.ReferenceTargets{ + reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "blah"}, @@ -748,7 +749,7 @@ func TestCollectReferenceTargets_hcl(t *testing.T) { } } `, - lang.ReferenceTargets{ + reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "blah"}, @@ -831,7 +832,7 @@ func TestCollectReferenceTargets_hcl(t *testing.T) { name = "lorem ipsum" } `, - lang.ReferenceTargets{ + reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "blah"}, @@ -907,7 +908,7 @@ func TestCollectReferenceTargets_hcl(t *testing.T) { name = "lorem ipsum" } `, - lang.ReferenceTargets{ + reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "blah"}, @@ -985,7 +986,7 @@ listener "https" { protocol = "tcp" } `, - lang.ReferenceTargets{ + reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "http"}, @@ -1119,7 +1120,7 @@ listener "https" { } } `, - lang.ReferenceTargets{ + reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "aws"}, @@ -1159,7 +1160,7 @@ listener "https" { Byte: 14, }, }, - NestedTargets: lang.ReferenceTargets{ + NestedTargets: reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "aws"}, @@ -1225,7 +1226,7 @@ listener "https" { Byte: 118, }, }, - NestedTargets: lang.ReferenceTargets{ + NestedTargets: reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "aws"}, @@ -1302,7 +1303,7 @@ listener "https" { Byte: 62, }, }, - NestedTargets: lang.ReferenceTargets{ + NestedTargets: reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "aws"}, @@ -1440,7 +1441,7 @@ listener "https" { Byte: 148, }, }, - NestedTargets: lang.ReferenceTargets{ + NestedTargets: reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "aws"}, @@ -1554,7 +1555,7 @@ provider "test" { } } `, - lang.ReferenceTargets{ + reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "aws"}, @@ -1671,7 +1672,7 @@ provider "test" { } } `, - lang.ReferenceTargets{ + reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "aws"}, @@ -1711,7 +1712,7 @@ provider "test" { Byte: 14, }, }, - NestedTargets: lang.ReferenceTargets{ + NestedTargets: reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "aws"}, @@ -1777,7 +1778,7 @@ provider "test" { Byte: 63, }, }, - NestedTargets: lang.ReferenceTargets{ + NestedTargets: reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "aws"}, @@ -1854,7 +1855,7 @@ provider "test" { Byte: 91, }, }, - NestedTargets: lang.ReferenceTargets{ + NestedTargets: reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "aws"}, @@ -1958,7 +1959,7 @@ provider "test" { Byte: 121, }, }, - NestedTargets: lang.ReferenceTargets{ + NestedTargets: reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "aws"}, @@ -2052,7 +2053,7 @@ provider "test" { } } `, - lang.ReferenceTargets{ + reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "root"}, @@ -2091,7 +2092,7 @@ provider "test" { "protocol": cty.String, }), }), - NestedTargets: lang.ReferenceTargets{ + NestedTargets: reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "root"}, @@ -2162,7 +2163,7 @@ provider "test" { "port": cty.Number, "protocol": cty.String, }), - NestedTargets: lang.ReferenceTargets{ + NestedTargets: reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "root"}, @@ -2296,7 +2297,7 @@ provider "test" { } } `, - lang.ReferenceTargets{ + reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "root"}, @@ -2335,7 +2336,7 @@ provider "test" { "protocol": cty.String, })), }), - NestedTargets: lang.ReferenceTargets{ + NestedTargets: reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "root"}, @@ -2394,7 +2395,7 @@ provider "test" { "port": cty.Number, "protocol": cty.String, })), - NestedTargets: lang.ReferenceTargets{ + NestedTargets: reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "root"}, @@ -2432,7 +2433,7 @@ provider "test" { "port": cty.Number, "protocol": cty.String, }), - NestedTargets: lang.ReferenceTargets{ + NestedTargets: reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "root"}, @@ -2544,7 +2545,7 @@ provider "test" { "port": cty.Number, "protocol": cty.String, }), - NestedTargets: lang.ReferenceTargets{ + NestedTargets: reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "root"}, @@ -2682,7 +2683,7 @@ provider "test" { } } `, - lang.ReferenceTargets{ + reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "root"}, @@ -2721,7 +2722,7 @@ provider "test" { "protocol": cty.String, })), }), - NestedTargets: lang.ReferenceTargets{ + NestedTargets: reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "root"}, @@ -2843,7 +2844,7 @@ provider "test" { } } `, - lang.ReferenceTargets{ + reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "root"}, @@ -2882,7 +2883,7 @@ provider "test" { "protocol": cty.String, })), }), - NestedTargets: lang.ReferenceTargets{ + NestedTargets: reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "root"}, @@ -2941,7 +2942,7 @@ provider "test" { "port": cty.Number, "protocol": cty.String, })), - NestedTargets: lang.ReferenceTargets{ + NestedTargets: reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "root"}, @@ -2979,7 +2980,7 @@ provider "test" { "port": cty.Number, "protocol": cty.String, }), - NestedTargets: lang.ReferenceTargets{ + NestedTargets: reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "root"}, @@ -3091,7 +3092,7 @@ provider "test" { "port": cty.Number, "protocol": cty.String, }), - NestedTargets: lang.ReferenceTargets{ + NestedTargets: reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "root"}, @@ -3231,7 +3232,7 @@ provider "test" { } } `, - lang.ReferenceTargets{ + reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "lb"}, @@ -3270,7 +3271,7 @@ provider "test" { "protocol": cty.String, })), }), - NestedTargets: lang.ReferenceTargets{ + NestedTargets: reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "lb"}, @@ -3329,7 +3330,7 @@ provider "test" { "port": cty.Number, "protocol": cty.String, })), - NestedTargets: lang.ReferenceTargets{ + NestedTargets: reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "lb"}, @@ -3367,7 +3368,7 @@ provider "test" { Byte: 51, }, }, - NestedTargets: lang.ReferenceTargets{ + NestedTargets: reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "lb"}, @@ -3479,7 +3480,7 @@ provider "test" { Byte: 111, }, }, - NestedTargets: lang.ReferenceTargets{ + NestedTargets: reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "lb"}, @@ -3565,7 +3566,7 @@ provider "test" { }, `testattr = special.test `, - lang.ReferenceTargets{ + reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "special"}, @@ -3620,7 +3621,7 @@ provider "test" { alias = "euwest" } `, - lang.ReferenceTargets{ + reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "aws"}, @@ -3680,7 +3681,7 @@ provider "test" { `variable "test" { } `, - lang.ReferenceTargets{ + reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "test"}, @@ -3749,7 +3750,7 @@ provider "test" { type = map(string) } `, - lang.ReferenceTargets{ + reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "test"}, @@ -3822,7 +3823,7 @@ provider "test" { default = "something" } `, - lang.ReferenceTargets{ + reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "test"}, @@ -3896,7 +3897,7 @@ provider "test" { default = ["something"] } `, - lang.ReferenceTargets{ + reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "test"}, @@ -3973,7 +3974,7 @@ provider "test" { ] } `, - lang.ReferenceTargets{ + reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "test"}, @@ -4047,7 +4048,7 @@ provider "test" { default = "something" } `, - lang.ReferenceTargets{ + reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "test"}, @@ -4116,7 +4117,7 @@ provider "test" { `module "test" { } `, - lang.ReferenceTargets{ + reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "module"}, @@ -4227,7 +4228,7 @@ module "different" { attr = "foo" } `, - lang.ReferenceTargets{ + reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "module"}, @@ -4262,7 +4263,7 @@ module "different" { Byte: 13, }, }, - NestedTargets: lang.ReferenceTargets{ + NestedTargets: reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "module"}, @@ -4343,7 +4344,7 @@ module "different" { } } `, - lang.ReferenceTargets{ + reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "local"}, @@ -4390,7 +4391,7 @@ module "different" { Byte: 18, }, }, - NestedTargets: lang.ReferenceTargets{ + NestedTargets: reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "local"}, @@ -4427,7 +4428,7 @@ module "different" { Byte: 32, }, }, - NestedTargets: lang.ReferenceTargets{ + NestedTargets: reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "local"}, @@ -4502,7 +4503,7 @@ module "different" { Byte: 72, }, }, - NestedTargets: lang.ReferenceTargets{ + NestedTargets: reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "local"}, @@ -4577,7 +4578,7 @@ module "different" { Byte: 111, }, }, - NestedTargets: lang.ReferenceTargets{ + NestedTargets: reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "local"}, @@ -4652,7 +4653,7 @@ module "different" { Byte: 151, }, }, - NestedTargets: lang.ReferenceTargets{ + NestedTargets: reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "local"}, @@ -4716,7 +4717,7 @@ module "different" { `output { } `, - lang.ReferenceTargets{}, + reference.Targets{}, }, } diff --git a/decoder/reference_targets_collect_json_test.go b/decoder/reference_targets_collect_json_test.go index 229caa38..06c61341 100644 --- a/decoder/reference_targets_collect_json_test.go +++ b/decoder/reference_targets_collect_json_test.go @@ -6,6 +6,7 @@ import ( "github.com/google/go-cmp/cmp" "github.com/hashicorp/hcl-lang/lang" + "github.com/hashicorp/hcl-lang/reference" "github.com/hashicorp/hcl-lang/schema" "github.com/hashicorp/hcl/v2" "github.com/hashicorp/hcl/v2/json" @@ -18,7 +19,7 @@ func TestCollectReferenceTargets_json(t *testing.T) { name string schema *schema.BodySchema cfg string - expectedRefs lang.ReferenceTargets + expectedRefs reference.Targets }{ { "root attribute as reference", @@ -39,7 +40,7 @@ func TestCollectReferenceTargets_json(t *testing.T) { }, }, `{"testattr": "${example}"}`, - lang.ReferenceTargets{ + reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "special"}, @@ -93,7 +94,7 @@ func TestCollectReferenceTargets_json(t *testing.T) { }, }, `{"testattr": "example"}`, - lang.ReferenceTargets{ + reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "special"}, @@ -147,7 +148,7 @@ func TestCollectReferenceTargets_json(t *testing.T) { }, }, `{"testattr": "example"}`, - lang.ReferenceTargets{ + reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "special"}, @@ -208,7 +209,7 @@ func TestCollectReferenceTargets_json(t *testing.T) { } } `, - lang.ReferenceTargets{ + reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "special"}, @@ -243,7 +244,7 @@ func TestCollectReferenceTargets_json(t *testing.T) { Byte: 14, }, }, - NestedTargets: lang.ReferenceTargets{ + NestedTargets: reference.Targets{ // TODO: See https: //github.com/hashicorp/terraform-ls/issues/675 }, }, @@ -272,7 +273,7 @@ func TestCollectReferenceTargets_json(t *testing.T) { } } `, - lang.ReferenceTargets{ + reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "special"}, @@ -305,7 +306,7 @@ func TestCollectReferenceTargets_json(t *testing.T) { Byte: 14, }, }, - NestedTargets: lang.ReferenceTargets{ + NestedTargets: reference.Targets{ // TODO: See https: //github.com/hashicorp/terraform-ls/issues/675 }, }, @@ -331,7 +332,7 @@ func TestCollectReferenceTargets_json(t *testing.T) { `{ "testattr": [ "example" ] }`, - lang.ReferenceTargets{ + reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "special"}, @@ -364,7 +365,7 @@ func TestCollectReferenceTargets_json(t *testing.T) { Byte: 14, }, }, - NestedTargets: lang.ReferenceTargets{ + NestedTargets: reference.Targets{ // TODO: See https: //github.com/hashicorp/terraform-ls/issues/675 }, }, @@ -394,7 +395,7 @@ func TestCollectReferenceTargets_json(t *testing.T) { `{ "testattr": [ "example" ] }`, - lang.ReferenceTargets{ + reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "special"}, @@ -427,7 +428,7 @@ func TestCollectReferenceTargets_json(t *testing.T) { Byte: 14, }, }, - NestedTargets: lang.ReferenceTargets{ + NestedTargets: reference.Targets{ // TODO: See https: //github.com/hashicorp/terraform-ls/issues/675 }, }, @@ -450,7 +451,7 @@ func TestCollectReferenceTargets_json(t *testing.T) { }, }, `{"testattr": "example"}`, - lang.ReferenceTargets{}, + reference.Targets{}, }, { "block with mismatching steps", @@ -487,7 +488,7 @@ func TestCollectReferenceTargets_json(t *testing.T) { } } `, - lang.ReferenceTargets{}, + reference.Targets{}, }, { "block as ref only", @@ -531,7 +532,7 @@ func TestCollectReferenceTargets_json(t *testing.T) { } } `, - lang.ReferenceTargets{ + reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "blah"}, @@ -658,7 +659,7 @@ func TestCollectReferenceTargets_json(t *testing.T) { } } `, - lang.ReferenceTargets{ + reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "blah"}, @@ -746,7 +747,7 @@ func TestCollectReferenceTargets_json(t *testing.T) { } } }`, - lang.ReferenceTargets{ + reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "blah"}, @@ -828,7 +829,7 @@ func TestCollectReferenceTargets_json(t *testing.T) { } } `, - lang.ReferenceTargets{ + reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "blah"}, @@ -910,7 +911,7 @@ func TestCollectReferenceTargets_json(t *testing.T) { } } `, - lang.ReferenceTargets{ + reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "http"}, @@ -1048,7 +1049,7 @@ func TestCollectReferenceTargets_json(t *testing.T) { } } `, - lang.ReferenceTargets{ + reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "aws"}, @@ -1088,7 +1089,7 @@ func TestCollectReferenceTargets_json(t *testing.T) { Byte: 30, }, }, - NestedTargets: lang.ReferenceTargets{ + NestedTargets: reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "aws"}, @@ -1154,7 +1155,7 @@ func TestCollectReferenceTargets_json(t *testing.T) { Byte: 171, }, }, - NestedTargets: lang.ReferenceTargets{ + NestedTargets: reference.Targets{ // TODO: See https: //github.com/hashicorp/terraform-ls/issues/675 }, }, @@ -1190,7 +1191,7 @@ func TestCollectReferenceTargets_json(t *testing.T) { Byte: 94, }, }, - NestedTargets: lang.ReferenceTargets{ + NestedTargets: reference.Targets{ // TODO: See https: //github.com/hashicorp/terraform-ls/issues/675 }, }, @@ -1261,7 +1262,7 @@ func TestCollectReferenceTargets_json(t *testing.T) { Byte: 207, }, }, - NestedTargets: lang.ReferenceTargets{ + NestedTargets: reference.Targets{ // TODO: See https: //github.com/hashicorp/terraform-ls/issues/675 }, }, @@ -1346,7 +1347,7 @@ func TestCollectReferenceTargets_json(t *testing.T) { } } `, - lang.ReferenceTargets{ + reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "aws"}, @@ -1467,7 +1468,7 @@ func TestCollectReferenceTargets_json(t *testing.T) { } } `, - lang.ReferenceTargets{ + reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "aws"}, @@ -1507,7 +1508,7 @@ func TestCollectReferenceTargets_json(t *testing.T) { Byte: 30, }, }, - NestedTargets: lang.ReferenceTargets{ + NestedTargets: reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "aws"}, @@ -1573,7 +1574,7 @@ func TestCollectReferenceTargets_json(t *testing.T) { Byte: 95, }, }, - NestedTargets: lang.ReferenceTargets{ + NestedTargets: reference.Targets{ // TODO: See https: //github.com/hashicorp/terraform-ls/issues/675 }, }, @@ -1609,7 +1610,7 @@ func TestCollectReferenceTargets_json(t *testing.T) { Byte: 129, }, }, - NestedTargets: lang.ReferenceTargets{ + NestedTargets: reference.Targets{ // TODO: See https: //github.com/hashicorp/terraform-ls/issues/675 }, }, @@ -1680,7 +1681,7 @@ func TestCollectReferenceTargets_json(t *testing.T) { Byte: 174, }, }, - NestedTargets: lang.ReferenceTargets{ + NestedTargets: reference.Targets{ // TODO: See https: //github.com/hashicorp/terraform-ls/issues/675 }, }, @@ -1745,7 +1746,7 @@ func TestCollectReferenceTargets_json(t *testing.T) { } } `, - lang.ReferenceTargets{ + reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "root"}, @@ -1784,7 +1785,7 @@ func TestCollectReferenceTargets_json(t *testing.T) { "protocol": cty.String, }), }), - NestedTargets: lang.ReferenceTargets{ + NestedTargets: reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "root"}, @@ -1855,7 +1856,7 @@ func TestCollectReferenceTargets_json(t *testing.T) { "port": cty.Number, "protocol": cty.String, }), - NestedTargets: lang.ReferenceTargets{ + NestedTargets: reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "root"}, @@ -1993,7 +1994,7 @@ func TestCollectReferenceTargets_json(t *testing.T) { } } `, - lang.ReferenceTargets{ + reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "root"}, @@ -2032,7 +2033,7 @@ func TestCollectReferenceTargets_json(t *testing.T) { "protocol": cty.String, })), }), - NestedTargets: lang.ReferenceTargets{ + NestedTargets: reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "root"}, @@ -2091,7 +2092,7 @@ func TestCollectReferenceTargets_json(t *testing.T) { "port": cty.Number, "protocol": cty.String, })), - NestedTargets: lang.ReferenceTargets{ + NestedTargets: reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "root"}, @@ -2129,7 +2130,7 @@ func TestCollectReferenceTargets_json(t *testing.T) { "port": cty.Number, "protocol": cty.String, }), - NestedTargets: lang.ReferenceTargets{ + NestedTargets: reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "root"}, @@ -2241,7 +2242,7 @@ func TestCollectReferenceTargets_json(t *testing.T) { "port": cty.Number, "protocol": cty.String, }), - NestedTargets: lang.ReferenceTargets{ + NestedTargets: reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "root"}, @@ -2383,7 +2384,7 @@ func TestCollectReferenceTargets_json(t *testing.T) { } } `, - lang.ReferenceTargets{ + reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "root"}, @@ -2422,7 +2423,7 @@ func TestCollectReferenceTargets_json(t *testing.T) { "protocol": cty.String, })), }), - NestedTargets: lang.ReferenceTargets{ + NestedTargets: reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "root"}, @@ -2548,7 +2549,7 @@ func TestCollectReferenceTargets_json(t *testing.T) { } } `, - lang.ReferenceTargets{ + reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "root"}, @@ -2587,7 +2588,7 @@ func TestCollectReferenceTargets_json(t *testing.T) { "protocol": cty.String, })), }), - NestedTargets: lang.ReferenceTargets{ + NestedTargets: reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "root"}, @@ -2646,7 +2647,7 @@ func TestCollectReferenceTargets_json(t *testing.T) { "port": cty.Number, "protocol": cty.String, })), - NestedTargets: lang.ReferenceTargets{ + NestedTargets: reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "root"}, @@ -2684,7 +2685,7 @@ func TestCollectReferenceTargets_json(t *testing.T) { "port": cty.Number, "protocol": cty.String, }), - NestedTargets: lang.ReferenceTargets{ + NestedTargets: reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "root"}, @@ -2796,7 +2797,7 @@ func TestCollectReferenceTargets_json(t *testing.T) { "port": cty.Number, "protocol": cty.String, }), - NestedTargets: lang.ReferenceTargets{ + NestedTargets: reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "root"}, @@ -2942,7 +2943,7 @@ func TestCollectReferenceTargets_json(t *testing.T) { } } `, - lang.ReferenceTargets{ + reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "lb"}, @@ -2981,7 +2982,7 @@ func TestCollectReferenceTargets_json(t *testing.T) { "protocol": cty.String, })), }), - NestedTargets: lang.ReferenceTargets{ + NestedTargets: reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "lb"}, @@ -3040,7 +3041,7 @@ func TestCollectReferenceTargets_json(t *testing.T) { "port": cty.Number, "protocol": cty.String, })), - NestedTargets: lang.ReferenceTargets{ + NestedTargets: reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "lb"}, @@ -3078,7 +3079,7 @@ func TestCollectReferenceTargets_json(t *testing.T) { Byte: 91, }, }, - NestedTargets: lang.ReferenceTargets{ + NestedTargets: reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "lb"}, @@ -3190,7 +3191,7 @@ func TestCollectReferenceTargets_json(t *testing.T) { Byte: 171, }, }, - NestedTargets: lang.ReferenceTargets{ + NestedTargets: reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "lb"}, @@ -3275,7 +3276,7 @@ func TestCollectReferenceTargets_json(t *testing.T) { }, }, `{"testattr": "${special.test}"}`, - lang.ReferenceTargets{ + reference.Targets{ // TODO: See https: //github.com/hashicorp/terraform-ls/issues/675 }, }, @@ -3314,7 +3315,7 @@ func TestCollectReferenceTargets_json(t *testing.T) { } } `, - lang.ReferenceTargets{ + reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "aws"}, @@ -3376,7 +3377,7 @@ func TestCollectReferenceTargets_json(t *testing.T) { "test": {} } }`, - lang.ReferenceTargets{ + reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "test"}, @@ -3449,7 +3450,7 @@ func TestCollectReferenceTargets_json(t *testing.T) { } } `, - lang.ReferenceTargets{ + reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "test"}, @@ -3526,7 +3527,7 @@ func TestCollectReferenceTargets_json(t *testing.T) { } } `, - lang.ReferenceTargets{ + reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "test"}, @@ -3604,7 +3605,7 @@ func TestCollectReferenceTargets_json(t *testing.T) { } } `, - lang.ReferenceTargets{ + reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "test"}, @@ -3683,7 +3684,7 @@ func TestCollectReferenceTargets_json(t *testing.T) { } } `, - lang.ReferenceTargets{ + reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "test"}, @@ -3760,7 +3761,7 @@ func TestCollectReferenceTargets_json(t *testing.T) { } } }`, - lang.ReferenceTargets{ + reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "test"}, @@ -3832,7 +3833,7 @@ func TestCollectReferenceTargets_json(t *testing.T) { } } `, - lang.ReferenceTargets{ + reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "module"}, @@ -3947,7 +3948,7 @@ func TestCollectReferenceTargets_json(t *testing.T) { } } `, - lang.ReferenceTargets{ + reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "module"}, @@ -3982,7 +3983,7 @@ func TestCollectReferenceTargets_json(t *testing.T) { Byte: 29, }, }, - NestedTargets: lang.ReferenceTargets{ + NestedTargets: reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "module"}, @@ -4065,7 +4066,7 @@ func TestCollectReferenceTargets_json(t *testing.T) { } } `, - lang.ReferenceTargets{ + reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "local"}, @@ -4112,7 +4113,7 @@ func TestCollectReferenceTargets_json(t *testing.T) { Byte: 29, }, }, - NestedTargets: lang.ReferenceTargets{ + NestedTargets: reference.Targets{ // TODO: See https: //github.com/hashicorp/terraform-ls/issues/675 }, }, @@ -4140,7 +4141,7 @@ func TestCollectReferenceTargets_json(t *testing.T) { "output": {} } `, - lang.ReferenceTargets{}, + reference.Targets{}, }, } diff --git a/decoder/reference_targets_test.go b/decoder/reference_targets_test.go index bfff2567..8f272efb 100644 --- a/decoder/reference_targets_test.go +++ b/decoder/reference_targets_test.go @@ -7,86 +7,12 @@ import ( "github.com/google/go-cmp/cmp" "github.com/hashicorp/hcl-lang/lang" + "github.com/hashicorp/hcl-lang/reference" "github.com/hashicorp/hcl/v2" "github.com/zclconf/go-cty-debug/ctydebug" "github.com/zclconf/go-cty/cty" ) -func TestAddress_Equals_basic(t *testing.T) { - originalAddr := Address(lang.Address{ - lang.RootStep{Name: "provider"}, - lang.AttrStep{Name: "aws"}, - }) - - matchingAddr := lang.Address{ - lang.RootStep{Name: "provider"}, - lang.AttrStep{Name: "aws"}, - } - if !originalAddr.Equals(Address(matchingAddr)) { - t.Fatalf("expected %q to match %q", originalAddr, matchingAddr) - } - - mismatchingAddr := lang.Address{ - lang.RootStep{Name: "provider"}, - lang.AttrStep{Name: "aaa"}, - } - if originalAddr.Equals(Address(mismatchingAddr)) { - t.Fatalf("expected %q not to match %q", originalAddr, mismatchingAddr) - } -} - -func TestAddress_Equals_numericIndexStep(t *testing.T) { - originalAddr := Address(lang.Address{ - lang.RootStep{Name: "aws_alb"}, - lang.AttrStep{Name: "example"}, - lang.IndexStep{Key: cty.NumberIntVal(0)}, - }) - - matchingAddr := lang.Address{ - lang.RootStep{Name: "aws_alb"}, - lang.AttrStep{Name: "example"}, - lang.IndexStep{Key: cty.NumberIntVal(0)}, - } - if !originalAddr.Equals(Address(matchingAddr)) { - t.Fatalf("expected %q to match %q", originalAddr, matchingAddr) - } - - mismatchingAddr := lang.Address{ - lang.RootStep{Name: "aws_alb"}, - lang.AttrStep{Name: "example"}, - lang.IndexStep{Key: cty.NumberIntVal(4)}, - } - if originalAddr.Equals(Address(mismatchingAddr)) { - t.Fatalf("expected %q not to match %q", originalAddr, mismatchingAddr) - } -} - -func TestAddress_Equals_stringIndexStep(t *testing.T) { - originalAddr := Address(lang.Address{ - lang.RootStep{Name: "aws_alb"}, - lang.AttrStep{Name: "example"}, - lang.IndexStep{Key: cty.StringVal("first")}, - }) - - matchingAddr := lang.Address{ - lang.RootStep{Name: "aws_alb"}, - lang.AttrStep{Name: "example"}, - lang.IndexStep{Key: cty.StringVal("first")}, - } - if !originalAddr.Equals(Address(matchingAddr)) { - t.Fatalf("expected %q to match %q", originalAddr, matchingAddr) - } - - mismatchingAddr := lang.Address{ - lang.RootStep{Name: "aws_alb"}, - lang.AttrStep{Name: "example"}, - lang.IndexStep{Key: cty.StringVal("second")}, - } - if originalAddr.Equals(Address(mismatchingAddr)) { - t.Fatalf("expected %q not to match %q", originalAddr, mismatchingAddr) - } -} - func TestCollectReferenceTargets_noSchema(t *testing.T) { d := testPathDecoder(t, &PathContext{}) _, err := d.CollectReferenceTargets() @@ -103,14 +29,14 @@ func TestCollectReferenceTargets_noSchema(t *testing.T) { func TestReferenceTargetForOrigin(t *testing.T) { testCases := []struct { name string - refTargets lang.ReferenceTargets - refOrigin lang.ReferenceOrigin - expectedRefTarget *lang.ReferenceTarget + refTargets reference.Targets + refOrigin reference.Origin + expectedRefTarget *reference.Target }{ { "no targets", - lang.ReferenceTargets{}, - lang.ReferenceOrigin{ + reference.Targets{}, + reference.Origin{ Addr: lang.Address{ lang.RootStep{Name: "var"}, lang.AttrStep{Name: "test"}, @@ -120,7 +46,7 @@ func TestReferenceTargetForOrigin(t *testing.T) { }, { "single match", - lang.ReferenceTargets{ + reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "var"}, @@ -128,14 +54,14 @@ func TestReferenceTargetForOrigin(t *testing.T) { }, }, }, - lang.ReferenceOrigin{ + reference.Origin{ Addr: lang.Address{ lang.RootStep{Name: "var"}, lang.AttrStep{Name: "test"}, }, - Constraints: lang.ReferenceOriginConstraints{{}}, + Constraints: reference.OriginConstraints{{}}, }, - &lang.ReferenceTarget{ + &reference.Target{ Addr: lang.Address{ lang.RootStep{Name: "var"}, lang.AttrStep{Name: "test"}, @@ -144,7 +70,7 @@ func TestReferenceTargetForOrigin(t *testing.T) { }, { "first of two matches", - lang.ReferenceTargets{ + reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "data"}, @@ -167,16 +93,16 @@ func TestReferenceTargetForOrigin(t *testing.T) { ScopeId: lang.ScopeId("variable"), }, }, - lang.ReferenceOrigin{ + reference.Origin{ Addr: lang.Address{ lang.RootStep{Name: "var"}, lang.AttrStep{Name: "test"}, }, - Constraints: lang.ReferenceOriginConstraints{ + Constraints: reference.OriginConstraints{ {OfType: cty.Bool}, }, }, - &lang.ReferenceTarget{ + &reference.Target{ Addr: lang.Address{ lang.RootStep{Name: "var"}, lang.AttrStep{Name: "test"}, @@ -186,7 +112,7 @@ func TestReferenceTargetForOrigin(t *testing.T) { }, { "match of unknown type", - lang.ReferenceTargets{ + reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "data"}, @@ -202,15 +128,15 @@ func TestReferenceTargetForOrigin(t *testing.T) { Type: cty.DynamicPseudoType, }, }, - lang.ReferenceOrigin{ + reference.Origin{ Addr: lang.Address{ lang.RootStep{Name: "var"}, lang.AttrStep{Name: "foo"}, lang.AttrStep{Name: "bar"}, }, - Constraints: lang.ReferenceOriginConstraints{{}}, + Constraints: reference.OriginConstraints{{}}, }, - &lang.ReferenceTarget{ + &reference.Target{ Addr: lang.Address{ lang.RootStep{Name: "var"}, lang.AttrStep{Name: "foo"}, @@ -220,7 +146,7 @@ func TestReferenceTargetForOrigin(t *testing.T) { }, { "match of nested target", - lang.ReferenceTargets{ + reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "data"}, @@ -236,7 +162,7 @@ func TestReferenceTargetForOrigin(t *testing.T) { Type: cty.Object(map[string]cty.Type{ "bar": cty.String, }), - NestedTargets: lang.ReferenceTargets{ + NestedTargets: reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "var"}, @@ -248,17 +174,17 @@ func TestReferenceTargetForOrigin(t *testing.T) { }, }, }, - lang.ReferenceOrigin{ + reference.Origin{ Addr: lang.Address{ lang.RootStep{Name: "var"}, lang.AttrStep{Name: "foo"}, lang.AttrStep{Name: "bar"}, }, - Constraints: lang.ReferenceOriginConstraints{ + Constraints: reference.OriginConstraints{ {OfType: cty.String}, }, }, - &lang.ReferenceTarget{ + &reference.Target{ Addr: lang.Address{ lang.RootStep{Name: "var"}, lang.AttrStep{Name: "foo"}, @@ -289,21 +215,21 @@ func TestReferenceTargetForOrigin(t *testing.T) { func TestOutermostReferenceTargetsAtPos(t *testing.T) { testCases := []struct { name string - refTargets lang.ReferenceTargets + refTargets reference.Targets filename string pos hcl.Pos - expectedTargets lang.ReferenceTargets + expectedTargets reference.Targets }{ { "no targets", - lang.ReferenceTargets{}, + reference.Targets{}, "test.tf", hcl.InitialPos, - lang.ReferenceTargets{}, + reference.Targets{}, }, { "file mismatch", - lang.ReferenceTargets{ + reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "var"}, @@ -327,11 +253,11 @@ func TestOutermostReferenceTargetsAtPos(t *testing.T) { Column: 3, Byte: 2, }, - lang.ReferenceTargets{}, + reference.Targets{}, }, { "position mismatch", - lang.ReferenceTargets{ + reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "var"}, @@ -355,11 +281,11 @@ func TestOutermostReferenceTargetsAtPos(t *testing.T) { Column: 1, Byte: 50, }, - lang.ReferenceTargets{}, + reference.Targets{}, }, { "single matching target", - lang.ReferenceTargets{ + reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "var"}, @@ -383,7 +309,7 @@ func TestOutermostReferenceTargetsAtPos(t *testing.T) { Column: 3, Byte: 2, }, - lang.ReferenceTargets{ + reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "var"}, @@ -404,7 +330,7 @@ func TestOutermostReferenceTargetsAtPos(t *testing.T) { }, { "two matching targets for the same position", - lang.ReferenceTargets{ + reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "var"}, @@ -444,7 +370,7 @@ func TestOutermostReferenceTargetsAtPos(t *testing.T) { Column: 3, Byte: 2, }, - lang.ReferenceTargets{ + reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "var"}, @@ -481,7 +407,7 @@ func TestOutermostReferenceTargetsAtPos(t *testing.T) { }, { "nested target matches outermost", - lang.ReferenceTargets{ + reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "aws_instance"}, @@ -499,7 +425,7 @@ func TestOutermostReferenceTargetsAtPos(t *testing.T) { Byte: 63, }, }, - NestedTargets: lang.ReferenceTargets{ + NestedTargets: reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "aws_instance"}, @@ -530,7 +456,7 @@ func TestOutermostReferenceTargetsAtPos(t *testing.T) { Column: 4, Byte: 36, }, - lang.ReferenceTargets{ + reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "aws_instance"}, @@ -548,7 +474,7 @@ func TestOutermostReferenceTargetsAtPos(t *testing.T) { Byte: 63, }, }, - NestedTargets: lang.ReferenceTargets{ + NestedTargets: reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "aws_instance"}, @@ -596,21 +522,21 @@ func TestOutermostReferenceTargetsAtPos(t *testing.T) { func TestInnermostReferenceTargetsAtPos(t *testing.T) { testCases := []struct { name string - refTargets lang.ReferenceTargets + refTargets reference.Targets filename string pos hcl.Pos - expectedTargets lang.ReferenceTargets + expectedTargets reference.Targets }{ { "no targets", - lang.ReferenceTargets{}, + reference.Targets{}, "test.tf", hcl.InitialPos, nil, }, { "file mismatch", - lang.ReferenceTargets{ + reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "var"}, @@ -638,7 +564,7 @@ func TestInnermostReferenceTargetsAtPos(t *testing.T) { }, { "position mismatch", - lang.ReferenceTargets{ + reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "var"}, @@ -666,7 +592,7 @@ func TestInnermostReferenceTargetsAtPos(t *testing.T) { }, { "single target match", - lang.ReferenceTargets{ + reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "var"}, @@ -690,7 +616,7 @@ func TestInnermostReferenceTargetsAtPos(t *testing.T) { Column: 3, Byte: 2, }, - lang.ReferenceTargets{ + reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "var"}, @@ -711,7 +637,7 @@ func TestInnermostReferenceTargetsAtPos(t *testing.T) { }, { "multiple targets matching at the same position", - lang.ReferenceTargets{ + reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "var"}, @@ -751,7 +677,7 @@ func TestInnermostReferenceTargetsAtPos(t *testing.T) { Column: 3, Byte: 2, }, - lang.ReferenceTargets{ + reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "var"}, @@ -788,7 +714,7 @@ func TestInnermostReferenceTargetsAtPos(t *testing.T) { }, { "nested target matches innermost", - lang.ReferenceTargets{ + reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "aws_instance"}, @@ -806,7 +732,7 @@ func TestInnermostReferenceTargetsAtPos(t *testing.T) { Byte: 63, }, }, - NestedTargets: lang.ReferenceTargets{ + NestedTargets: reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "aws_instance"}, @@ -837,7 +763,7 @@ func TestInnermostReferenceTargetsAtPos(t *testing.T) { Column: 4, Byte: 36, }, - lang.ReferenceTargets{ + reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "aws_instance"}, @@ -863,7 +789,7 @@ func TestInnermostReferenceTargetsAtPos(t *testing.T) { }, { "matching nested targets with position at block definition", - lang.ReferenceTargets{ + reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "module"}, @@ -890,7 +816,7 @@ func TestInnermostReferenceTargetsAtPos(t *testing.T) { Byte: 63, }, }, - NestedTargets: lang.ReferenceTargets{ + NestedTargets: reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "module"}, @@ -917,7 +843,7 @@ func TestInnermostReferenceTargetsAtPos(t *testing.T) { Column: 15, Byte: 16, }, - lang.ReferenceTargets{ + reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "module"}, @@ -944,7 +870,7 @@ func TestInnermostReferenceTargetsAtPos(t *testing.T) { Byte: 63, }, }, - NestedTargets: lang.ReferenceTargets{ + NestedTargets: reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "module"}, @@ -988,19 +914,19 @@ func TestInnermostReferenceTargetsAtPos(t *testing.T) { func TestReferenceTargetsInFile(t *testing.T) { testCases := []struct { name string - refTargets lang.ReferenceTargets + refTargets reference.Targets filename string - expectedTargets lang.ReferenceTargets + expectedTargets reference.Targets }{ { "no targets", - lang.ReferenceTargets{}, + reference.Targets{}, "test.tf", - lang.ReferenceTargets{}, + reference.Targets{}, }, { "mismatching filename", - lang.ReferenceTargets{ + reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "foo"}, @@ -1017,11 +943,11 @@ func TestReferenceTargetsInFile(t *testing.T) { }, }, "test.tf", - lang.ReferenceTargets{}, + reference.Targets{}, }, { "matching file", - lang.ReferenceTargets{ + reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "foo"}, @@ -1035,7 +961,7 @@ func TestReferenceTargetsInFile(t *testing.T) { Byte: 10, }, }, - NestedTargets: lang.ReferenceTargets{ + NestedTargets: reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "foo"}, @@ -1055,7 +981,7 @@ func TestReferenceTargetsInFile(t *testing.T) { }, }, "test.tf", - lang.ReferenceTargets{ + reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "foo"}, @@ -1069,7 +995,7 @@ func TestReferenceTargetsInFile(t *testing.T) { Byte: 10, }, }, - NestedTargets: lang.ReferenceTargets{ + NestedTargets: reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "foo"}, diff --git a/decoder/semantic_tokens.go b/decoder/semantic_tokens.go index afd4a5c3..fc2babb3 100644 --- a/decoder/semantic_tokens.go +++ b/decoder/semantic_tokens.go @@ -4,6 +4,7 @@ import ( "sort" "github.com/hashicorp/hcl-lang/lang" + "github.com/hashicorp/hcl-lang/reference" "github.com/hashicorp/hcl-lang/schema" "github.com/hashicorp/hcl/v2" "github.com/hashicorp/hcl/v2/hclsyntax" @@ -144,16 +145,15 @@ func (d *PathDecoder) tokensForExpression(expr hclsyntax.Expression, constraints tes, ok := constraints.TraversalExprs() if ok && d.pathCtx.ReferenceTargets != nil { - refs := ReferenceTargets(d.pathCtx.ReferenceTargets) traversal := eType.AsTraversal() - origin, err := TraversalToReferenceOrigin(traversal, tes) + origin, err := reference.TraversalToOrigin(traversal, tes) if err != nil { return tokens } - _, err = refs.FirstTargetableBy(origin) - if err != nil { + _, targetFound := d.pathCtx.ReferenceTargets.FirstTargetableBy(origin) + if !targetFound { return tokens } diff --git a/decoder/semantic_tokens_expr_test.go b/decoder/semantic_tokens_expr_test.go index a9cd7711..75906c7f 100644 --- a/decoder/semantic_tokens_expr_test.go +++ b/decoder/semantic_tokens_expr_test.go @@ -6,6 +6,7 @@ import ( "github.com/google/go-cmp/cmp" "github.com/hashicorp/hcl-lang/lang" + "github.com/hashicorp/hcl-lang/reference" "github.com/hashicorp/hcl-lang/schema" "github.com/hashicorp/hcl/v2" "github.com/hashicorp/hcl/v2/hclsyntax" @@ -1467,7 +1468,7 @@ func TestDecoder_SemanticTokensInFile_traversalExpression(t *testing.T) { testCases := []struct { name string attrSchema map[string]*schema.AttributeSchema - refs lang.ReferenceTargets + refs reference.Targets cfg string expectedTokens []lang.SemanticToken }{ @@ -1480,7 +1481,7 @@ func TestDecoder_SemanticTokensInFile_traversalExpression(t *testing.T) { }, }, }, - lang.ReferenceTargets{}, + reference.Targets{}, `attr = var.blah `, []lang.SemanticToken{ @@ -1512,8 +1513,8 @@ func TestDecoder_SemanticTokensInFile_traversalExpression(t *testing.T) { }, }, }, - lang.ReferenceTargets{ - lang.ReferenceTarget{ + reference.Targets{ + reference.Target{ Addr: lang.Address{ lang.RootStep{Name: "var"}, lang.AttrStep{Name: "blah"}, @@ -1552,8 +1553,8 @@ func TestDecoder_SemanticTokensInFile_traversalExpression(t *testing.T) { }, }, }, - lang.ReferenceTargets{ - lang.ReferenceTarget{ + reference.Targets{ + reference.Target{ Addr: lang.Address{ lang.RootStep{Name: "var"}, lang.AttrStep{Name: "blah"}, @@ -1626,8 +1627,8 @@ func TestDecoder_SemanticTokensInFile_traversalExpression(t *testing.T) { }, }, }, - lang.ReferenceTargets{ - lang.ReferenceTarget{ + reference.Targets{ + reference.Target{ Addr: lang.Address{ lang.RootStep{Name: "var"}, lang.AttrStep{Name: "blah"}, @@ -1700,8 +1701,8 @@ func TestDecoder_SemanticTokensInFile_traversalExpression(t *testing.T) { }, }, }, - lang.ReferenceTargets{ - lang.ReferenceTarget{ + reference.Targets{ + reference.Target{ Addr: lang.Address{ lang.RootStep{Name: "var"}, lang.AttrStep{Name: "foo"}, @@ -1827,7 +1828,7 @@ func TestDecoder_SemanticTokensInFile_traversalExpression(t *testing.T) { }, }, }, - lang.ReferenceTargets{ + reference.Targets{ { Addr: lang.Address{ lang.RootStep{Name: "var"}, diff --git a/lang/address.go b/lang/address.go new file mode 100644 index 00000000..bfa07b95 --- /dev/null +++ b/lang/address.go @@ -0,0 +1,49 @@ +package lang + +import ( + "fmt" + + "github.com/hashicorp/hcl/v2" +) + +type Address []AddressStep + +func (a Address) Equals(addr Address) bool { + if len(a) != len(addr) { + return false + } + for i, step := range a { + if step.String() != addr[i].String() { + return false + } + } + + return true +} + +func (a Address) FirstSteps(steps uint) Address { + return a[0:steps] +} + +func TraversalToAddress(traversal hcl.Traversal) (Address, error) { + addr := Address{} + for _, tr := range traversal { + switch t := tr.(type) { + case hcl.TraverseRoot: + addr = append(addr, RootStep{ + Name: t.Name, + }) + case hcl.TraverseAttr: + addr = append(addr, AttrStep{ + Name: t.Name, + }) + case hcl.TraverseIndex: + addr = append(addr, IndexStep{ + Key: t.Key, + }) + default: + return addr, fmt.Errorf("invalid traversal: %#v", tr) + } + } + return addr, nil +} diff --git a/lang/address_steps.go b/lang/address_steps.go index 430b9866..f3778556 100644 --- a/lang/address_steps.go +++ b/lang/address_steps.go @@ -3,12 +3,9 @@ package lang import ( "fmt" - "github.com/hashicorp/hcl/v2" "github.com/zclconf/go-cty/cty" ) -type Address []AddressStep - type addrStepSigil struct{} type AddressStep interface { @@ -78,26 +75,3 @@ func (s IndexStep) String() string { func (IndexStep) isRefStepImpl() addrStepSigil { return addrStepSigil{} } - -func TraversalToAddress(traversal hcl.Traversal) (Address, error) { - addr := Address{} - for _, tr := range traversal { - switch t := tr.(type) { - case hcl.TraverseRoot: - addr = append(addr, RootStep{ - Name: t.Name, - }) - case hcl.TraverseAttr: - addr = append(addr, AttrStep{ - Name: t.Name, - }) - case hcl.TraverseIndex: - addr = append(addr, IndexStep{ - Key: t.Key, - }) - default: - return addr, fmt.Errorf("invalid traversal: %#v", tr) - } - } - return addr, nil -} diff --git a/lang/address_test.go b/lang/address_test.go new file mode 100644 index 00000000..330f78f2 --- /dev/null +++ b/lang/address_test.go @@ -0,0 +1,140 @@ +package lang + +import ( + "fmt" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/hashicorp/hcl/v2" + "github.com/hashicorp/hcl/v2/hclsyntax" + "github.com/zclconf/go-cty-debug/ctydebug" + "github.com/zclconf/go-cty/cty" +) + +func TestAddress_Equals_basic(t *testing.T) { + originalAddr := Address{ + RootStep{Name: "provider"}, + AttrStep{Name: "aws"}, + } + + matchingAddr := Address{ + RootStep{Name: "provider"}, + AttrStep{Name: "aws"}, + } + if !originalAddr.Equals(Address(matchingAddr)) { + t.Fatalf("expected %q to match %q", originalAddr, matchingAddr) + } + + mismatchingAddr := Address{ + RootStep{Name: "provider"}, + AttrStep{Name: "aaa"}, + } + if originalAddr.Equals(Address(mismatchingAddr)) { + t.Fatalf("expected %q not to match %q", originalAddr, mismatchingAddr) + } +} + +func TestAddress_Equals_numericIndexStep(t *testing.T) { + originalAddr := Address{ + RootStep{Name: "aws_alb"}, + AttrStep{Name: "example"}, + IndexStep{Key: cty.NumberIntVal(0)}, + } + + matchingAddr := Address{ + RootStep{Name: "aws_alb"}, + AttrStep{Name: "example"}, + IndexStep{Key: cty.NumberIntVal(0)}, + } + if !originalAddr.Equals(Address(matchingAddr)) { + t.Fatalf("expected %q to match %q", originalAddr, matchingAddr) + } + + mismatchingAddr := Address{ + RootStep{Name: "aws_alb"}, + AttrStep{Name: "example"}, + IndexStep{Key: cty.NumberIntVal(4)}, + } + if originalAddr.Equals(Address(mismatchingAddr)) { + t.Fatalf("expected %q not to match %q", originalAddr, mismatchingAddr) + } +} + +func TestAddress_Equals_stringIndexStep(t *testing.T) { + originalAddr := Address{ + RootStep{Name: "aws_alb"}, + AttrStep{Name: "example"}, + IndexStep{Key: cty.StringVal("first")}, + } + + matchingAddr := Address{ + RootStep{Name: "aws_alb"}, + AttrStep{Name: "example"}, + IndexStep{Key: cty.StringVal("first")}, + } + if !originalAddr.Equals(Address(matchingAddr)) { + t.Fatalf("expected %q to match %q", originalAddr, matchingAddr) + } + + mismatchingAddr := Address{ + RootStep{Name: "aws_alb"}, + AttrStep{Name: "example"}, + IndexStep{Key: cty.StringVal("second")}, + } + if originalAddr.Equals(Address(mismatchingAddr)) { + t.Fatalf("expected %q not to match %q", originalAddr, mismatchingAddr) + } +} + +func TestTraversalToAddress(t *testing.T) { + testCases := []struct { + rawTraversal string + expectedAddr Address + }{ + { + "one", + Address{ + RootStep{Name: "one"}, + }, + }, + { + "first.second", + Address{ + RootStep{Name: "first"}, + AttrStep{Name: "second"}, + }, + }, + { + "foo[2]", + Address{ + RootStep{Name: "foo"}, + IndexStep{Key: cty.NumberIntVal(2)}, + }, + }, + { + `foo["bar"]`, + Address{ + RootStep{Name: "foo"}, + IndexStep{Key: cty.StringVal("bar")}, + }, + }, + } + + for i, tc := range testCases { + t.Run(fmt.Sprintf("%d", i), func(t *testing.T) { + traversal, diags := hclsyntax.ParseTraversalAbs([]byte(tc.rawTraversal), "test.tf", hcl.InitialPos) + if len(diags) > 0 { + t.Fatal(diags) + } + + addr, err := TraversalToAddress(traversal) + if err != nil { + t.Fatal(err) + } + + if diff := cmp.Diff(tc.expectedAddr, addr, ctydebug.CmpOptions); diff != "" { + t.Fatalf("address mismatch: %s", diff) + } + }) + } +} diff --git a/lang/reference_origin.go b/lang/reference_origin.go deleted file mode 100644 index 1fa1ccad..00000000 --- a/lang/reference_origin.go +++ /dev/null @@ -1,62 +0,0 @@ -package lang - -import ( - "github.com/hashicorp/hcl/v2" - "github.com/zclconf/go-cty/cty" -) - -type ReferenceOrigin struct { - Addr Address - Range hcl.Range - - // Constraints represents any traversal expression constraints - // for the attribute where the origin was found. - // - // Further matching against decoded reference targets is needed - // for >1 constraints, which is done later at runtime as - // targets and origins can be decoded at different times. - Constraints ReferenceOriginConstraints -} - -type ReferenceOrigins []ReferenceOrigin - -func (ro ReferenceOrigins) Copy() ReferenceOrigins { - if ro == nil { - return nil - } - - newOrigins := make(ReferenceOrigins, len(ro)) - for i, origin := range ro { - newOrigins[i] = origin.Copy() - } - - return newOrigins -} - -func (ro ReferenceOrigin) Copy() ReferenceOrigin { - return ReferenceOrigin{ - Addr: ro.Addr, - Range: ro.Range, - Constraints: ro.Constraints.Copy(), - } -} - -type ReferenceOriginConstraint struct { - OfScopeId ScopeId - OfType cty.Type -} - -type ReferenceOriginConstraints []ReferenceOriginConstraint - -func (roc ReferenceOriginConstraints) Copy() ReferenceOriginConstraints { - if roc == nil { - return nil - } - - cons := make(ReferenceOriginConstraints, 0) - for _, oc := range roc { - cons = append(cons, oc) - } - - return cons -} diff --git a/lang/reference_target.go b/lang/reference_target.go deleted file mode 100644 index 4638f6b5..00000000 --- a/lang/reference_target.go +++ /dev/null @@ -1,103 +0,0 @@ -package lang - -import ( - "github.com/hashicorp/hcl/v2" - "github.com/zclconf/go-cty/cty" -) - -type ScopeId string - -type ReferenceTarget struct { - Addr Address - ScopeId ScopeId - - // RangePtr represents range of the whole attribute or block - // or nil if the target is not addressable. - RangePtr *hcl.Range - - // DefRangePtr represents a definition range, i.e. block header, - // or an attribute name or nil if the target is not addressable - // or when it represents multiple list, set or map blocks. - // - // This is useful in situation where a representative single-line - // range is needed - e.g. to render a contextual UI element in - // the editor near the middle of this range. - DefRangePtr *hcl.Range - - Type cty.Type - Name string - Description MarkupContent - - NestedTargets ReferenceTargets -} - -type ReferenceTargets []ReferenceTarget - -func (refs ReferenceTargets) Copy() ReferenceTargets { - if refs == nil { - return nil - } - - newRefs := make(ReferenceTargets, len(refs)) - for i, ref := range refs { - newRefs[i] = ref.Copy() - } - - return newRefs -} - -func (ref ReferenceTarget) Copy() ReferenceTarget { - return ReferenceTarget{ - Addr: ref.Addr, - ScopeId: ref.ScopeId, - RangePtr: copyHclRangePtr(ref.RangePtr), - DefRangePtr: copyHclRangePtr(ref.DefRangePtr), - Type: ref.Type, // cty.Type is immutable by design - Name: ref.Name, - Description: ref.Description, - NestedTargets: ref.NestedTargets.Copy(), - } -} - -func copyHclRangePtr(rng *hcl.Range) *hcl.Range { - if rng == nil { - return nil - } - return rng.Ptr() -} - -func (r ReferenceTargets) Len() int { - return len(r) -} - -func (r ReferenceTargets) Less(i, j int) bool { - return r[i].Addr.String() < r[j].Addr.String() -} - -func (r ReferenceTargets) Swap(i, j int) { - r[i], r[j] = r[j], r[i] -} - -func (r ReferenceTarget) Address() Address { - return r.Addr -} - -func (r ReferenceTarget) FriendlyName() string { - if r.Name != "" { - return r.Name - } - - if r.Type != cty.NilType { - return r.Type.FriendlyName() - } - - return "reference" -} - -func (r ReferenceTarget) TargetRange() (hcl.Range, bool) { - if r.RangePtr == nil { - return hcl.Range{}, false - } - - return *r.RangePtr, true -} diff --git a/lang/scope_id.go b/lang/scope_id.go new file mode 100644 index 00000000..d4e5f961 --- /dev/null +++ b/lang/scope_id.go @@ -0,0 +1,3 @@ +package lang + +type ScopeId string diff --git a/reference/errors.go b/reference/errors.go new file mode 100644 index 00000000..e14b1b18 --- /dev/null +++ b/reference/errors.go @@ -0,0 +1,13 @@ +package reference + +type NoTargetFound struct{} + +func (*NoTargetFound) Error() string { + return "no reference target found" +} + +type NoOriginFound struct{} + +func (*NoOriginFound) Error() string { + return "no reference origin found" +} diff --git a/reference/origin.go b/reference/origin.go new file mode 100644 index 00000000..a9f2562c --- /dev/null +++ b/reference/origin.go @@ -0,0 +1,27 @@ +package reference + +import ( + "github.com/hashicorp/hcl-lang/lang" + "github.com/hashicorp/hcl/v2" +) + +type Origin struct { + Addr lang.Address + Range hcl.Range + + // Constraints represents any traversal expression constraints + // for the attribute where the origin was found. + // + // Further matching against decoded reference targets is needed + // for >1 constraints, which is done later at runtime as + // targets and origins can be decoded at different times. + Constraints OriginConstraints +} + +func (ro Origin) Copy() Origin { + return Origin{ + Addr: ro.Addr, + Range: ro.Range, + Constraints: ro.Constraints.Copy(), + } +} diff --git a/reference/origin_constraint.go b/reference/origin_constraint.go new file mode 100644 index 00000000..81f8d340 --- /dev/null +++ b/reference/origin_constraint.go @@ -0,0 +1,26 @@ +package reference + +import ( + "github.com/hashicorp/hcl-lang/lang" + "github.com/zclconf/go-cty/cty" +) + +type OriginConstraint struct { + OfScopeId lang.ScopeId + OfType cty.Type +} + +type OriginConstraints []OriginConstraint + +func (roc OriginConstraints) Copy() OriginConstraints { + if roc == nil { + return nil + } + + cons := make(OriginConstraints, 0) + for _, oc := range roc { + cons = append(cons, oc) + } + + return cons +} diff --git a/reference/origins.go b/reference/origins.go new file mode 100644 index 00000000..37f17404 --- /dev/null +++ b/reference/origins.go @@ -0,0 +1,46 @@ +package reference + +import ( + "github.com/hashicorp/hcl/v2" +) + +type Origins []Origin + +func (ro Origins) Copy() Origins { + if ro == nil { + return nil + } + + newOrigins := make(Origins, len(ro)) + for i, origin := range ro { + newOrigins[i] = origin.Copy() + } + + return newOrigins +} + +func (ro Origins) AtPos(file string, pos hcl.Pos) (*Origin, bool) { + for _, origin := range ro { + if origin.Range.Filename == file && origin.Range.ContainsPos(pos) { + return &origin, true + } + } + + return nil, false +} + +func (ro Origins) Targeting(refTarget Target) Origins { + origins := make(Origins, 0) + + for _, refOrigin := range ro { + if refTarget.IsTargetableBy(refOrigin) { + origins = append(origins, refOrigin) + } + } + + for _, iTarget := range refTarget.NestedTargets { + origins = append(origins, ro.Targeting(iTarget)...) + } + + return origins +} diff --git a/reference/origins_test.go b/reference/origins_test.go new file mode 100644 index 00000000..20b0648d --- /dev/null +++ b/reference/origins_test.go @@ -0,0 +1,418 @@ +package reference + +import ( + "fmt" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/hashicorp/hcl-lang/lang" + "github.com/hashicorp/hcl/v2" + "github.com/zclconf/go-cty-debug/ctydebug" + "github.com/zclconf/go-cty/cty" +) + +func TestOrigins_AtPos(t *testing.T) { + testCases := []struct { + name string + origins Origins + pos hcl.Pos + expectedOrigin *Origin + expectedFound bool + }{ + { + "no origins", + Origins{}, + hcl.InitialPos, + nil, + false, + }, + { + "single mismatching origin", + Origins{ + Origin{ + Addr: lang.Address{ + lang.RootStep{Name: "blah"}, + }, + Range: hcl.Range{ + Filename: "test.tf", + Start: hcl.Pos{Line: 1, Column: 8, Byte: 7}, + End: hcl.Pos{Line: 1, Column: 12, Byte: 11}, + }, + }, + }, + hcl.Pos{ + Line: 1, + Column: 3, + Byte: 2, + }, + nil, + false, + }, + { + "single matching origin", + Origins{ + Origin{ + Addr: lang.Address{ + lang.RootStep{Name: "blah"}, + }, + Range: hcl.Range{ + Filename: "test.tf", + Start: hcl.Pos{Line: 1, Column: 8, Byte: 7}, + End: hcl.Pos{Line: 1, Column: 12, Byte: 11}, + }, + }, + }, + hcl.Pos{ + Line: 1, + Column: 9, + Byte: 8, + }, + &Origin{ + Addr: lang.Address{ + lang.RootStep{Name: "blah"}, + }, + Range: hcl.Range{ + Filename: "test.tf", + Start: hcl.Pos{Line: 1, Column: 8, Byte: 7}, + End: hcl.Pos{Line: 1, Column: 12, Byte: 11}, + }, + }, + true, + }, + { + "multiple origins - single match", + Origins{ + Origin{ + Addr: lang.Address{ + lang.RootStep{Name: "foo"}, + }, + Range: hcl.Range{ + Filename: "test.tf", + Start: hcl.Pos{Line: 1, Column: 8, Byte: 7}, + End: hcl.Pos{Line: 1, Column: 12, Byte: 11}, + }, + }, + Origin{ + Addr: lang.Address{ + lang.RootStep{Name: "var"}, + }, + Range: hcl.Range{ + Filename: "differentfile.tf", + Start: hcl.Pos{Line: 2, Column: 8, Byte: 14}, + End: hcl.Pos{Line: 2, Column: 12, Byte: 18}, + }, + }, + Origin{ + Addr: lang.Address{ + lang.RootStep{Name: "bar"}, + }, + Range: hcl.Range{ + Filename: "test.tf", + Start: hcl.Pos{Line: 2, Column: 8, Byte: 14}, + End: hcl.Pos{Line: 2, Column: 12, Byte: 18}, + }, + }, + }, + hcl.Pos{ + Line: 2, + Column: 9, + Byte: 15, + }, + &Origin{ + Addr: lang.Address{ + lang.RootStep{Name: "bar"}, + }, + Range: hcl.Range{ + Filename: "test.tf", + Start: hcl.Pos{Line: 2, Column: 8, Byte: 14}, + End: hcl.Pos{Line: 2, Column: 12, Byte: 18}, + }, + }, + true, + }, + } + + for i, tc := range testCases { + t.Run(fmt.Sprintf("%d-%s", i, tc.name), func(t *testing.T) { + refOrigin, ok := tc.origins.AtPos("test.tf", tc.pos) + if !ok && tc.expectedFound { + t.Fatal("expected origin to be found") + } + + if diff := cmp.Diff(tc.expectedOrigin, refOrigin, ctydebug.CmpOptions); diff != "" { + t.Fatalf("mismatched origin: %s", diff) + } + }) + } +} + +func TestOrigins_Targeting(t *testing.T) { + testCases := []struct { + name string + origins Origins + refTarget Target + expectedOrigins Origins + }{ + { + "no origins", + Origins{}, + Target{ + Addr: lang.Address{ + lang.RootStep{Name: "test"}, + }, + Type: cty.String, + }, + Origins{}, + }, + { + "exact address match", + Origins{ + { + Addr: lang.Address{ + lang.RootStep{Name: "test"}, + }, + }, + { + Addr: lang.Address{ + lang.RootStep{Name: "test"}, + lang.AttrStep{Name: "secondstep"}, + }, + Constraints: OriginConstraints{ + {OfType: cty.String}, + }, + }, + }, + Target{ + Addr: lang.Address{ + lang.RootStep{Name: "test"}, + lang.AttrStep{Name: "secondstep"}, + }, + Type: cty.String, + }, + Origins{ + { + Addr: lang.Address{ + lang.RootStep{Name: "test"}, + lang.AttrStep{Name: "secondstep"}, + }, + Constraints: OriginConstraints{ + {OfType: cty.String}, + }, + }, + }, + }, + { + "no match", + Origins{ + { + Addr: lang.Address{ + lang.RootStep{Name: "test"}, + }, + }, + { + Addr: lang.Address{ + lang.RootStep{Name: "test"}, + lang.AttrStep{Name: "secondstep"}, + }, + }, + }, + Target{ + Addr: lang.Address{ + lang.RootStep{Name: "test"}, + lang.AttrStep{Name: "different"}, + }, + Type: cty.String, + }, + Origins{}, + }, + { + "match of nested target - two matches", + Origins{ + { + Addr: lang.Address{ + lang.RootStep{Name: "foo"}, + }, + }, + { + Addr: lang.Address{ + lang.RootStep{Name: "test"}, + }, + Constraints: OriginConstraints{ + {OfType: cty.DynamicPseudoType}, + }, + }, + { + Addr: lang.Address{ + lang.RootStep{Name: "test"}, + lang.AttrStep{Name: "second"}, + }, + Constraints: OriginConstraints{ + {OfType: cty.String}, + }, + }, + }, + Target{ + Addr: lang.Address{ + lang.RootStep{Name: "test"}, + }, + Type: cty.Object(map[string]cty.Type{ + "second": cty.String, + }), + NestedTargets: Targets{ + { + Addr: lang.Address{ + lang.RootStep{Name: "test"}, + lang.AttrStep{Name: "second"}, + }, + Type: cty.String, + }, + }, + }, + Origins{ + { + Addr: lang.Address{ + lang.RootStep{Name: "test"}, + }, + Constraints: OriginConstraints{ + {OfType: cty.DynamicPseudoType}, + }, + }, + { + Addr: lang.Address{ + lang.RootStep{Name: "test"}, + lang.AttrStep{Name: "second"}, + }, + Constraints: OriginConstraints{ + {OfType: cty.String}, + }, + }, + }, + }, + { + "loose match of target of unknown type", + Origins{ + { + Addr: lang.Address{ + lang.RootStep{Name: "foo"}, + }, + Constraints: OriginConstraints{{}}, + }, + { + Addr: lang.Address{ + lang.RootStep{Name: "test"}, + }, + Constraints: OriginConstraints{{}}, + }, + { + Addr: lang.Address{ + lang.RootStep{Name: "test"}, + lang.AttrStep{Name: "second"}, + }, + Constraints: OriginConstraints{{}}, + }, + }, + Target{ + Addr: lang.Address{ + lang.RootStep{Name: "test"}, + }, + Type: cty.DynamicPseudoType, + }, + Origins{ + { + Addr: lang.Address{ + lang.RootStep{Name: "test"}, + }, + Constraints: OriginConstraints{{}}, + }, + { + Addr: lang.Address{ + lang.RootStep{Name: "test"}, + lang.AttrStep{Name: "second"}, + }, + Constraints: OriginConstraints{{}}, + }, + }, + }, + { + "mismatch of target nil type", + Origins{ + { + Addr: lang.Address{ + lang.RootStep{Name: "test"}, + }, + Constraints: OriginConstraints{ + {OfScopeId: lang.ScopeId("test")}, + }, + }, + }, + Target{ + Addr: lang.Address{ + lang.RootStep{Name: "test"}, + }, + ScopeId: lang.ScopeId("test"), + Type: cty.String, + }, + Origins{}, + }, + // JSON edge cases + { + "constraint-less origin mismatching scope-only target", + Origins{ + { + Addr: lang.Address{ + lang.RootStep{Name: "var"}, + lang.AttrStep{Name: "alpha"}, + }, + Constraints: nil, + }, + }, + Target{ + Addr: lang.Address{ + lang.RootStep{Name: "var"}, + lang.AttrStep{Name: "alpha"}, + }, + ScopeId: "variable", + Type: cty.NilType, + }, + Origins{}, + }, + { + "constraint-less origin matching type-aware target", + Origins{ + { + Addr: lang.Address{ + lang.RootStep{Name: "var"}, + lang.AttrStep{Name: "beta"}, + }, + Constraints: nil, + }, + }, + Target{ + Addr: lang.Address{ + lang.RootStep{Name: "var"}, + lang.AttrStep{Name: "beta"}, + }, + ScopeId: "variable", + Type: cty.DynamicPseudoType, + }, + Origins{ + { + Addr: lang.Address{ + lang.RootStep{Name: "var"}, + lang.AttrStep{Name: "beta"}, + }, + Constraints: nil, + }, + }, + }, + } + for i, tc := range testCases { + t.Run(fmt.Sprintf("%d-%s", i, tc.name), func(t *testing.T) { + origins := tc.origins.Targeting(tc.refTarget) + + if diff := cmp.Diff(tc.expectedOrigins, origins, ctydebug.CmpOptions); diff != "" { + t.Fatalf("mismatched reference origins: %s", diff) + } + }) + } +} diff --git a/reference/target.go b/reference/target.go new file mode 100644 index 00000000..8cac4da2 --- /dev/null +++ b/reference/target.go @@ -0,0 +1,134 @@ +package reference + +import ( + "github.com/hashicorp/hcl-lang/lang" + "github.com/hashicorp/hcl-lang/schema" + "github.com/hashicorp/hcl/v2" + "github.com/zclconf/go-cty/cty" +) + +type Target struct { + Addr lang.Address + ScopeId lang.ScopeId + + // RangePtr represents range of the whole attribute or block + // or nil if the target is not addressable. + RangePtr *hcl.Range + + // DefRangePtr represents a definition range, i.e. block header, + // or an attribute name or nil if the target is not addressable + // or when it represents multiple list, set or map blocks. + // + // This is useful in situation where a representative single-line + // range is needed - e.g. to render a contextual UI element in + // the editor near the middle of this range. + DefRangePtr *hcl.Range + + Type cty.Type + Name string + Description lang.MarkupContent + + NestedTargets Targets +} + +func (ref Target) Copy() Target { + return Target{ + Addr: ref.Addr, + ScopeId: ref.ScopeId, + RangePtr: copyHclRangePtr(ref.RangePtr), + DefRangePtr: copyHclRangePtr(ref.DefRangePtr), + Type: ref.Type, // cty.Type is immutable by design + Name: ref.Name, + Description: ref.Description, + NestedTargets: ref.NestedTargets.Copy(), + } +} + +func copyHclRangePtr(rng *hcl.Range) *hcl.Range { + if rng == nil { + return nil + } + return rng.Ptr() +} + +func (r Target) Address() lang.Address { + return r.Addr +} + +func (r Target) FriendlyName() string { + if r.Name != "" { + return r.Name + } + + if r.Type != cty.NilType { + return r.Type.FriendlyName() + } + + return "reference" +} + +func (r Target) TargetRange() (hcl.Range, bool) { + if r.RangePtr == nil { + return hcl.Range{}, false + } + + return *r.RangePtr, true +} + +func (ref Target) MatchesConstraint(te schema.TraversalExpr) bool { + return ref.MatchesScopeId(te.OfScopeId) && ref.ConformsToType(te.OfType) +} + +func (ref Target) MatchesScopeId(scopeId lang.ScopeId) bool { + return scopeId == "" || ref.ScopeId == scopeId +} + +func (ref Target) ConformsToType(typ cty.Type) bool { + conformsToType := false + if typ != cty.NilType && ref.Type != cty.NilType { + if ref.Type == cty.DynamicPseudoType { + // anything conforms with dynamic + conformsToType = true + } + if errs := ref.Type.TestConformance(typ); len(errs) == 0 { + conformsToType = true + } + } + + return conformsToType || (typ == cty.NilType && ref.Type == cty.NilType) +} + +func (target Target) IsTargetableBy(origin Origin) bool { + if len(target.Addr) > len(origin.Addr) { + return false + } + + originAddr := origin.Addr + + matchesCons := false + + if len(origin.Constraints) == 0 && target.Type != cty.NilType { + matchesCons = true + } + + for _, cons := range origin.Constraints { + if !target.MatchesScopeId(cons.OfScopeId) { + continue + } + + if target.Type == cty.DynamicPseudoType { + originAddr = origin.Addr.FirstSteps(uint(len(target.Addr))) + matchesCons = true + continue + } + if cons.OfType != cty.NilType && target.ConformsToType(cons.OfType) { + matchesCons = true + } + if cons.OfType == cty.NilType && target.Type == cty.NilType { + // This just simplifies testing + matchesCons = true + } + } + + return target.Addr.Equals(originAddr) && matchesCons +} diff --git a/reference/targets.go b/reference/targets.go new file mode 100644 index 00000000..9f76aff9 --- /dev/null +++ b/reference/targets.go @@ -0,0 +1,169 @@ +package reference + +import ( + "errors" + "strings" + + "github.com/hashicorp/hcl-lang/schema" + "github.com/hashicorp/hcl/v2" +) + +type Targets []Target + +func (refs Targets) Copy() Targets { + if refs == nil { + return nil + } + + newRefs := make(Targets, len(refs)) + for i, ref := range refs { + newRefs[i] = ref.Copy() + } + + return newRefs +} + +func (r Targets) Len() int { + return len(r) +} + +func (r Targets) Less(i, j int) bool { + return r[i].Addr.String() < r[j].Addr.String() +} + +func (r Targets) Swap(i, j int) { + r[i], r[j] = r[j], r[i] +} + +type TargetWalkFunc func(Target) error + +var stopWalking error = errors.New("stop walking") + +const InfiniteDepth = -1 + +func (refs Targets) deepWalk(f TargetWalkFunc, depth int) { + w := refTargetDeepWalker{ + WalkFunc: f, + Depth: depth, + } + w.walk(refs) +} + +type refTargetDeepWalker struct { + WalkFunc TargetWalkFunc + Depth int + + currentDepth int +} + +func (w refTargetDeepWalker) walk(refTargets Targets) { + for _, ref := range refTargets { + err := w.WalkFunc(ref) + if err == stopWalking { + return + } + + if len(ref.NestedTargets) > 0 && (w.Depth == InfiniteDepth || w.Depth > w.currentDepth) { + w.currentDepth++ + w.walk(ref.NestedTargets) + w.currentDepth-- + } + } +} + +func (refs Targets) MatchWalk(te schema.TraversalExpr, prefix string, f TargetWalkFunc) { + for _, ref := range refs { + if strings.HasPrefix(ref.Addr.String(), string(prefix)) { + nestedMatches := ref.NestedTargets.containsMatch(te, prefix) + if ref.MatchesConstraint(te) || nestedMatches { + f(ref) + continue + } + } + + ref.NestedTargets.MatchWalk(te, prefix, f) + } +} + +func (refs Targets) containsMatch(te schema.TraversalExpr, prefix string) bool { + for _, ref := range refs { + if strings.HasPrefix(ref.Addr.String(), string(prefix)) && + ref.MatchesConstraint(te) { + return true + } + if len(ref.NestedTargets) > 0 { + if match := ref.NestedTargets.containsMatch(te, prefix); match { + return true + } + } + } + return false +} + +func (refs Targets) FirstTargetableBy(origin Origin) (*Target, bool) { + var matchingReference *Target + + refs.deepWalk(func(ref Target) error { + if ref.IsTargetableBy(origin) { + matchingReference = &ref + return stopWalking + } + return nil + }, InfiniteDepth) + + if matchingReference == nil { + return nil, false + } + + return matchingReference, true +} + +func (refs Targets) OutermostInFile(file string) Targets { + targets := make(Targets, 0) + + for _, target := range refs { + if target.RangePtr == nil { + continue + } + if target.RangePtr.Filename == file { + targets = append(targets, target) + } + } + + return targets +} + +func (refs Targets) InnermostAtPos(file string, pos hcl.Pos) (Targets, bool) { + matchingTargets := make(Targets, 0) + + for _, target := range refs { + if target.RangePtr == nil { + continue + } + if target.RangePtr.Filename == file && target.RangePtr.ContainsPos(pos) { + matchingTargets = append(matchingTargets, target) + } + } + + var innermostTargets Targets + + for _, target := range matchingTargets { + if target.DefRangePtr != nil { + if target.DefRangePtr.Filename == file && + target.DefRangePtr.ContainsPos(pos) { + innermostTargets = append(innermostTargets, target) + continue + } + } + + nestedTargets, ok := target.NestedTargets.InnermostAtPos(file, pos) + if ok { + innermostTargets = nestedTargets + continue + } + + innermostTargets = append(innermostTargets, target) + } + + return innermostTargets, len(innermostTargets) > 0 +} diff --git a/reference/targets_test.go b/reference/targets_test.go new file mode 100644 index 00000000..ad41f7e1 --- /dev/null +++ b/reference/targets_test.go @@ -0,0 +1,324 @@ +package reference + +import ( + "fmt" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/hashicorp/hcl-lang/lang" + "github.com/hashicorp/hcl/v2" + "github.com/zclconf/go-cty-debug/ctydebug" + "github.com/zclconf/go-cty/cty" +) + +func TestTargets_FirstTargetableBy(t *testing.T) { + testCases := []struct { + name string + targets Targets + origin Origin + expectedRefTarget *Target + expectedFound bool + }{ + { + "no targets", + Targets{}, + Origin{ + Addr: lang.Address{ + lang.RootStep{Name: "var"}, + lang.AttrStep{Name: "test"}, + }, + }, + nil, + false, + }, + { + "single match", + Targets{ + { + Addr: lang.Address{ + lang.RootStep{Name: "var"}, + lang.AttrStep{Name: "test"}, + }, + }, + }, + Origin{ + Addr: lang.Address{ + lang.RootStep{Name: "var"}, + lang.AttrStep{Name: "test"}, + }, + Constraints: OriginConstraints{{}}, + }, + &Target{ + Addr: lang.Address{ + lang.RootStep{Name: "var"}, + lang.AttrStep{Name: "test"}, + }, + }, + true, + }, + { + "first of two matches", + Targets{ + { + Addr: lang.Address{ + lang.RootStep{Name: "data"}, + lang.AttrStep{Name: "foo"}, + }, + Type: cty.Bool, + }, + { + Addr: lang.Address{ + lang.RootStep{Name: "var"}, + lang.AttrStep{Name: "test"}, + }, + Type: cty.Bool, + }, + { + Addr: lang.Address{ + lang.RootStep{Name: "var"}, + lang.AttrStep{Name: "test"}, + }, + ScopeId: lang.ScopeId("variable"), + }, + }, + Origin{ + Addr: lang.Address{ + lang.RootStep{Name: "var"}, + lang.AttrStep{Name: "test"}, + }, + Constraints: OriginConstraints{ + {OfType: cty.Bool}, + }, + }, + &Target{ + Addr: lang.Address{ + lang.RootStep{Name: "var"}, + lang.AttrStep{Name: "test"}, + }, + Type: cty.Bool, + }, + true, + }, + { + "match of unknown type", + Targets{ + { + Addr: lang.Address{ + lang.RootStep{Name: "data"}, + lang.AttrStep{Name: "foo"}, + }, + Type: cty.Bool, + }, + { + Addr: lang.Address{ + lang.RootStep{Name: "var"}, + lang.AttrStep{Name: "foo"}, + }, + Type: cty.DynamicPseudoType, + }, + }, + Origin{ + Addr: lang.Address{ + lang.RootStep{Name: "var"}, + lang.AttrStep{Name: "foo"}, + lang.AttrStep{Name: "bar"}, + }, + Constraints: OriginConstraints{{}}, + }, + &Target{ + Addr: lang.Address{ + lang.RootStep{Name: "var"}, + lang.AttrStep{Name: "foo"}, + }, + Type: cty.DynamicPseudoType, + }, + true, + }, + { + "match of nested target", + Targets{ + { + Addr: lang.Address{ + lang.RootStep{Name: "data"}, + lang.AttrStep{Name: "foo"}, + }, + Type: cty.Bool, + }, + { + Addr: lang.Address{ + lang.RootStep{Name: "var"}, + lang.AttrStep{Name: "foo"}, + }, + Type: cty.Object(map[string]cty.Type{ + "bar": cty.String, + }), + NestedTargets: Targets{ + { + Addr: lang.Address{ + lang.RootStep{Name: "var"}, + lang.AttrStep{Name: "foo"}, + lang.AttrStep{Name: "bar"}, + }, + Type: cty.String, + }, + }, + }, + }, + Origin{ + Addr: lang.Address{ + lang.RootStep{Name: "var"}, + lang.AttrStep{Name: "foo"}, + lang.AttrStep{Name: "bar"}, + }, + Constraints: OriginConstraints{ + {OfType: cty.String}, + }, + }, + &Target{ + Addr: lang.Address{ + lang.RootStep{Name: "var"}, + lang.AttrStep{Name: "foo"}, + lang.AttrStep{Name: "bar"}, + }, + Type: cty.String, + }, + true, + }, + } + for i, tc := range testCases { + t.Run(fmt.Sprintf("%d-%s", i, tc.name), func(t *testing.T) { + refTarget, ok := tc.targets.FirstTargetableBy(tc.origin) + if !ok && tc.expectedFound { + t.Fatalf("expected targetable to be found") + } + + if diff := cmp.Diff(tc.expectedRefTarget, refTarget, ctydebug.CmpOptions); diff != "" { + t.Fatalf("mismatch of reference target: %s", diff) + } + }) + } +} + +func TestTargets_OutermostInFile(t *testing.T) { + testCases := []struct { + name string + targets Targets + filename string + expectedTargets Targets + }{ + { + "no targets", + Targets{}, + "test.tf", + Targets{}, + }, + { + "mismatching filename", + Targets{ + { + Addr: lang.Address{ + lang.RootStep{Name: "foo"}, + }, + RangePtr: &hcl.Range{ + Filename: "bar.tf", + Start: hcl.InitialPos, + End: hcl.Pos{ + Line: 2, + Column: 1, + Byte: 10, + }, + }, + }, + }, + "test.tf", + Targets{}, + }, + { + "matching file", + Targets{ + { + Addr: lang.Address{ + lang.RootStep{Name: "foo"}, + }, + RangePtr: &hcl.Range{ + Filename: "test.tf", + Start: hcl.InitialPos, + End: hcl.Pos{ + Line: 2, + Column: 1, + Byte: 10, + }, + }, + NestedTargets: Targets{ + { + Addr: lang.Address{ + lang.RootStep{Name: "foo"}, + lang.AttrStep{Name: "bar"}, + }, + RangePtr: &hcl.Range{ + Filename: "test.tf", + Start: hcl.InitialPos, + End: hcl.Pos{ + Line: 1, + Column: 5, + Byte: 4, + }, + }, + }, + }, + }, + }, + "test.tf", + Targets{ + { + Addr: lang.Address{ + lang.RootStep{Name: "foo"}, + }, + RangePtr: &hcl.Range{ + Filename: "test.tf", + Start: hcl.InitialPos, + End: hcl.Pos{ + Line: 2, + Column: 1, + Byte: 10, + }, + }, + NestedTargets: Targets{ + { + Addr: lang.Address{ + lang.RootStep{Name: "foo"}, + lang.AttrStep{Name: "bar"}, + }, + RangePtr: &hcl.Range{ + Filename: "test.tf", + Start: hcl.InitialPos, + End: hcl.Pos{ + Line: 1, + Column: 5, + Byte: 4, + }, + }, + }, + }, + }, + }, + }, + } + for i, tc := range testCases { + t.Run(fmt.Sprintf("%d-%s", i, tc.name), func(t *testing.T) { + targets := tc.targets.OutermostInFile(tc.filename) + + if diff := cmp.Diff(tc.expectedTargets, targets, ctydebug.CmpOptions); diff != "" { + t.Fatalf("mismatch of targets: %s", diff) + } + }) + } +} + +func TestTargets_MatchWalk(t *testing.T) { + t.Fatal("TODO") +} + +func TestTargets_InnermostAtPos(t *testing.T) { + t.Fatal("TODO") +} diff --git a/reference/traversal.go b/reference/traversal.go new file mode 100644 index 00000000..adc746c4 --- /dev/null +++ b/reference/traversal.go @@ -0,0 +1,52 @@ +package reference + +import ( + "github.com/hashicorp/hcl-lang/lang" + "github.com/hashicorp/hcl-lang/schema" + "github.com/hashicorp/hcl/v2" +) + +func TraversalsToOrigins(traversals []hcl.Traversal, tes schema.TraversalExprs) Origins { + origins := make(Origins, 0) + for _, traversal := range traversals { + origin, err := TraversalToOrigin(traversal, tes) + if err != nil { + continue + } + origins = append(origins, origin) + } + + return origins +} + +func TraversalToOrigin(traversal hcl.Traversal, tes schema.TraversalExprs) (Origin, error) { + addr, err := lang.TraversalToAddress(traversal) + if err != nil { + return Origin{}, err + } + + return Origin{ + Addr: addr, + Range: traversal.SourceRange(), + Constraints: traversalExpressionsToOriginConstraints(tes), + }, nil +} + +func traversalExpressionsToOriginConstraints(tes []schema.TraversalExpr) OriginConstraints { + if len(tes) == 0 { + return nil + } + + roc := make(OriginConstraints, 0) + for _, te := range tes { + if te.Address != nil { + // skip traversals which are targets by themselves (not origins) + continue + } + roc = append(roc, OriginConstraint{ + OfType: te.OfType, + OfScopeId: te.OfScopeId, + }) + } + return roc +} diff --git a/reference/traversal_test.go b/reference/traversal_test.go new file mode 100644 index 00000000..3fbf93b0 --- /dev/null +++ b/reference/traversal_test.go @@ -0,0 +1,100 @@ +package reference + +import ( + "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/hashicorp/hcl/v2/hclsyntax" + "github.com/zclconf/go-cty-debug/ctydebug" + "github.com/zclconf/go-cty/cty" +) + +func TestTraversalToOrigin(t *testing.T) { + testCases := []struct { + rawTraversal string + traversalExprs schema.TraversalExprs + expectedOrigin Origin + }{ + { + "one", + schema.TraversalExprs{}, + Origin{ + Addr: lang.Address{ + lang.RootStep{Name: "one"}, + }, + Range: hcl.Range{ + Filename: "test.tf", + Start: hcl.Pos{Line: 1, Column: 1, Byte: 0}, + End: hcl.Pos{Line: 1, Column: 4, Byte: 3}, + }, + }, + }, + { + "first.second", + schema.TraversalExprs{}, + Origin{ + Addr: lang.Address{ + lang.RootStep{Name: "first"}, + lang.AttrStep{Name: "second"}, + }, + Range: hcl.Range{ + Filename: "test.tf", + Start: hcl.Pos{Line: 1, Column: 1, Byte: 0}, + End: hcl.Pos{Line: 1, Column: 13, Byte: 12}, + }, + }, + }, + { + "foo[2]", + schema.TraversalExprs{}, + Origin{ + Addr: lang.Address{ + lang.RootStep{Name: "foo"}, + lang.IndexStep{Key: cty.NumberIntVal(2)}, + }, + Range: hcl.Range{ + Filename: "test.tf", + Start: hcl.Pos{Line: 1, Column: 1, Byte: 0}, + End: hcl.Pos{Line: 1, Column: 7, Byte: 6}, + }, + }, + }, + { + `foo["bar"]`, + schema.TraversalExprs{}, + Origin{ + Addr: lang.Address{ + lang.RootStep{Name: "foo"}, + lang.IndexStep{Key: cty.StringVal("bar")}, + }, + Range: hcl.Range{ + Filename: "test.tf", + Start: hcl.Pos{Line: 1, Column: 1, Byte: 0}, + End: hcl.Pos{Line: 1, Column: 11, Byte: 10}, + }, + }, + }, + } + + for i, tc := range testCases { + t.Run(fmt.Sprintf("%d", i), func(t *testing.T) { + traversal, diags := hclsyntax.ParseTraversalAbs([]byte(tc.rawTraversal), "test.tf", hcl.InitialPos) + if len(diags) > 0 { + t.Fatal(diags) + } + + origin, err := TraversalToOrigin(traversal, tc.traversalExprs) + if err != nil { + t.Fatal(err) + } + + if diff := cmp.Diff(tc.expectedOrigin, origin, ctydebug.CmpOptions); diff != "" { + t.Fatalf("origin mismatch: %s", diff) + } + }) + } +}