addrs: Don't return zero-length slice in Reference.Remaining.

Although this rarely matters, making it always be nil when empty makes
deep assertions simpler in tests.

This also includes a minor update to the test in the core package that
first encountered this problem, to improve the quality of its output
on failure.
This commit is contained in:
Martin Atkins 2018-05-10 15:59:47 -07:00
parent e1bc27ef6c
commit 26e820d829
2 changed files with 51 additions and 38 deletions

View File

@ -28,6 +28,48 @@ type Reference struct {
// If error diagnostics are returned then the Reference value is invalid and
// must not be used.
func ParseRef(traversal hcl.Traversal) (*Reference, tfdiags.Diagnostics) {
ref, diags := parseRef(traversal)
// Normalize a little to make life easier for callers.
if ref != nil {
if len(ref.Remaining) == 0 {
ref.Remaining = nil
}
}
return ref, diags
}
// ParseRefStr is a helper wrapper around ParseRef that takes a string
// and parses it with the HCL native syntax traversal parser before
// interpreting it.
//
// This should be used only in specialized situations since it will cause the
// created references to not have any meaningful source location information.
// If a reference string is coming from a source that should be identified in
// error messages then the caller should instead parse it directly using a
// suitable function from the HCL API and pass the traversal itself to
// ParseRef.
//
// Error diagnostics are returned if either the parsing fails or the analysis
// of the traversal fails. There is no way for the caller to distinguish the
// two kinds of diagnostics programmatically. If error diagnostics are returned
// the returned reference may be nil or incomplete.
func ParseRefStr(str string) (*Reference, tfdiags.Diagnostics) {
var diags tfdiags.Diagnostics
traversal, parseDiags := hclsyntax.ParseTraversalAbs([]byte(str), "", hcl.Pos{Line: 1, Column: 1})
diags = diags.Append(parseDiags)
if parseDiags.HasErrors() {
return nil, diags
}
ref, targetDiags := ParseRef(traversal)
diags = diags.Append(targetDiags)
return ref, diags
}
func parseRef(traversal hcl.Traversal) (*Reference, tfdiags.Diagnostics) {
var diags tfdiags.Diagnostics
root := traversal.RootName()
@ -174,35 +216,6 @@ func ParseRef(traversal hcl.Traversal) (*Reference, tfdiags.Diagnostics) {
}
}
// ParseRefStr is a helper wrapper around ParseRef that takes a string
// and parses it with the HCL native syntax traversal parser before
// interpreting it.
//
// This should be used only in specialized situations since it will cause the
// created references to not have any meaningful source location information.
// If a reference string is coming from a source that should be identified in
// error messages then the caller should instead parse it directly using a
// suitable function from the HCL API and pass the traversal itself to
// ParseRef.
//
// Error diagnostics are returned if either the parsing fails or the analysis
// of the traversal fails. There is no way for the caller to distinguish the
// two kinds of diagnostics programmatically. If error diagnostics are returned
// the returned reference may be nil or incomplete.
func ParseRefStr(str string) (*Reference, tfdiags.Diagnostics) {
var diags tfdiags.Diagnostics
traversal, parseDiags := hclsyntax.ParseTraversalAbs([]byte(str), "", hcl.Pos{Line: 1, Column: 1})
diags = diags.Append(parseDiags)
if parseDiags.HasErrors() {
return nil, diags
}
ref, targetDiags := ParseRef(traversal)
diags = diags.Append(targetDiags)
return ref, diags
}
func parseResourceRef(mode ResourceMode, startRange hcl.Range, traversal hcl.Traversal) (*Reference, tfdiags.Diagnostics) {
var diags tfdiags.Diagnostics
@ -271,7 +284,6 @@ func parseResourceRef(mode ResourceMode, startRange hcl.Range, traversal hcl.Tra
return &Reference{
Subject: resourceInstAddr,
SourceRange: tfdiags.SourceRangeFromHCL(rng),
Remaining: remain,
}, diags
}

View File

@ -4,6 +4,7 @@ import (
"reflect"
"testing"
"github.com/go-test/deep"
"github.com/hashicorp/hcl2/hcl"
"github.com/hashicorp/hcl2/hcl/hclsyntax"
@ -72,14 +73,14 @@ func TestNodeApplyableModuleVariableReference(t *testing.T) {
},
}
expected := []*addrs.Reference{
want := []*addrs.Reference{
{
Subject: addrs.InputVariable{Name: "foo"},
},
}
actual := n.References()
if !reflect.DeepEqual(actual, expected) {
t.Fatalf("%#v != %#v", actual, expected)
got := n.References()
for _, problem := range deep.Equal(got, want) {
t.Error(problem)
}
}
@ -100,13 +101,13 @@ func TestNodeApplyableModuleVariableReference_grandchild(t *testing.T) {
},
}
expected := []*addrs.Reference{
want := []*addrs.Reference{
{
Subject: addrs.InputVariable{Name: "foo"},
},
}
actual := n.References()
if !reflect.DeepEqual(actual, expected) {
t.Fatalf("%#v != %#v", actual, expected)
got := n.References()
for _, problem := range deep.Equal(got, want) {
t.Error(problem)
}
}