hcl2: Use the actual string snippet as it appears from undefined

With the updated undefined variable code, we attempt to pick the text of
`${....}` verbatim from the hcl body. Previously, we'd attempt to
regenerate the string from the AST and pray it matches input; the
generation is lossy, as the same AST can represent multiple variations
(e.g. `${v.0}` and `${v[0]}` have the same HCLv2 AST). In this change,
we attempt to go back to the hcl2 source and find the string snippet
corresponding to the variable reference.
This commit is contained in:
Mahmood Ali 2021-04-07 16:26:34 -04:00
parent 6de35bd1b7
commit 04bfeacc5b
2 changed files with 22 additions and 2 deletions

View File

@ -855,6 +855,7 @@ func TestParse_UndefinedVariables(t *testing.T) {
"foo-${attr.network.dev-us-east1-relay-vpc.external-ip.0}",
`${env["BLAH"]}`,
`${mixed-indexing.0[3]["FOO"].5}`,
`with spaces ${ root. field[ "FOO"].5 }`,
}
for _, c := range cases {

View File

@ -1,6 +1,7 @@
package jobspec2
import (
"bytes"
"fmt"
"strings"
@ -308,8 +309,26 @@ func (c *jobConfig) EvalContext() *hcl.EvalContext {
inputVariablesAccessor: cty.ObjectVal(vars),
localsAccessor: cty.ObjectVal(locals),
},
UnknownVariable: func(expr string) (cty.Value, error) {
v := "${" + expr + "}"
UndefinedVariable: func(t hcl.Traversal) (cty.Value, hcl.Diagnostics) {
body := c.ParseConfig.Body
start := t.SourceRange().Start.Byte
end := t.SourceRange().End.Byte
v := string(body[start:end])
if i := bytes.IndexByte(body[end:], '}'); i != -1 {
v += string(body[end : end+i+1])
} else {
// fallback for unexpected cases
v += "}"
}
if i := bytes.LastIndex(body[:start], []byte("${")); i != 0 {
v = string(body[i:start]) + v
} else {
// fallback for unexpected cases
v = "${" + v
}
return cty.StringVal(v), nil
},
}