hcl/json: Detect variable references in property names
When a JSON object is representing an expression, template sequences are permitted in the property names as well as the values. We must detect the references here so that applications that do dynamic scope construction or dependency analysis will get the right result.
This commit is contained in:
parent
47d7fce5a6
commit
ebe27107e1
@ -424,7 +424,7 @@ func (e *expression) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
|
||||
known := true
|
||||
for _, jsonAttr := range v.Attrs {
|
||||
// In this one context we allow keys to contain interpolation
|
||||
// experessions too, assuming we're evaluating in interpolation
|
||||
// expressions too, assuming we're evaluating in interpolation
|
||||
// mode. This achieves parity with the native syntax where
|
||||
// object expressions can have dynamic keys, while block contents
|
||||
// may not.
|
||||
@ -533,6 +533,11 @@ func (e *expression) Variables() []hcl.Traversal {
|
||||
}
|
||||
case *objectVal:
|
||||
for _, jsonAttr := range v.Attrs {
|
||||
keyExpr := &stringVal{ // we're going to treat key as an expression in this context
|
||||
Value: jsonAttr.Name,
|
||||
SrcRange: jsonAttr.NameRange,
|
||||
}
|
||||
vars = append(vars, (&expression{src: keyExpr}).Variables()...)
|
||||
vars = append(vars, (&expression{src: jsonAttr.Value}).Variables()...)
|
||||
}
|
||||
}
|
||||
|
@ -1190,6 +1190,95 @@ func TestJustAttributes(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestExpressionVariables(t *testing.T) {
|
||||
tests := []struct {
|
||||
Src string
|
||||
Want []hcl.Traversal
|
||||
}{
|
||||
{
|
||||
`{"a":true}`,
|
||||
nil,
|
||||
},
|
||||
{
|
||||
`{"a":"${foo}"}`,
|
||||
[]hcl.Traversal{
|
||||
{
|
||||
hcl.TraverseRoot{
|
||||
Name: "foo",
|
||||
SrcRange: hcl.Range{
|
||||
Filename: "test.json",
|
||||
Start: hcl.Pos{Line: 1, Column: 9, Byte: 8},
|
||||
End: hcl.Pos{Line: 1, Column: 12, Byte: 11},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
`{"a":["${foo}"]}`,
|
||||
[]hcl.Traversal{
|
||||
{
|
||||
hcl.TraverseRoot{
|
||||
Name: "foo",
|
||||
SrcRange: hcl.Range{
|
||||
Filename: "test.json",
|
||||
Start: hcl.Pos{Line: 1, Column: 10, Byte: 9},
|
||||
End: hcl.Pos{Line: 1, Column: 13, Byte: 12},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
`{"a":{"b":"${foo}"}}`,
|
||||
[]hcl.Traversal{
|
||||
{
|
||||
hcl.TraverseRoot{
|
||||
Name: "foo",
|
||||
SrcRange: hcl.Range{
|
||||
Filename: "test.json",
|
||||
Start: hcl.Pos{Line: 1, Column: 14, Byte: 13},
|
||||
End: hcl.Pos{Line: 1, Column: 17, Byte: 16},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
`{"a":{"${foo}":"b"}}`,
|
||||
[]hcl.Traversal{
|
||||
{
|
||||
hcl.TraverseRoot{
|
||||
Name: "foo",
|
||||
SrcRange: hcl.Range{
|
||||
Filename: "test.json",
|
||||
Start: hcl.Pos{Line: 1, Column: 10, Byte: 9},
|
||||
End: hcl.Pos{Line: 1, Column: 13, Byte: 12},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.Src, func(t *testing.T) {
|
||||
file, diags := Parse([]byte(test.Src), "test.json")
|
||||
if len(diags) != 0 {
|
||||
t.Fatalf("Parse produced diagnostics: %s", diags)
|
||||
}
|
||||
attrs, diags := file.Body.JustAttributes()
|
||||
if len(diags) != 0 {
|
||||
t.Fatalf("JustAttributes produced diagnostics: %s", diags)
|
||||
}
|
||||
got := attrs["a"].Expr.Variables()
|
||||
if !reflect.DeepEqual(got, test.Want) {
|
||||
t.Errorf("wrong result\ngot: %s\nwant: %s", spew.Sdump(got), spew.Sdump(test.Want))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestExpressionAsTraversal(t *testing.T) {
|
||||
e := &expression{
|
||||
src: &stringVal{
|
||||
|
Loading…
Reference in New Issue
Block a user