Require static scope stack to have at least one entry

Kind of odd code smell, but the only alternative I could think of was a
panic.  Would rather ensure this requirement at compile time instead.

Signed-off-by: Christian Mesh <christianmesh1@gmail.com>
This commit is contained in:
Christian Mesh 2024-12-11 07:59:02 -05:00 committed by Martin Atkins
parent 7fdc4909d6
commit 00bc17917a
2 changed files with 15 additions and 13 deletions

View File

@ -22,9 +22,9 @@ import (
)
// newStaticScope creates a lang.Scope that's backed by the static view of the module represented by the StaticEvaluator
func newStaticScope(eval *StaticEvaluator, stack ...StaticIdentifier) *lang.Scope {
func newStaticScope(eval *StaticEvaluator, stack0 StaticIdentifier, stack ...StaticIdentifier) *lang.Scope {
return &lang.Scope{
Data: staticScopeData{eval, stack},
Data: staticScopeData{eval, append([]StaticIdentifier{stack0}, stack...)},
ParseRef: addrs.ParseRef,
BaseDir: ".", // Always current working directory for now. (same as Evaluator.Scope())
PureOnly: false,
@ -56,16 +56,11 @@ func (s staticScopeData) scope(ident StaticIdentifier) (*lang.Scope, tfdiags.Dia
})
}
}
return newStaticScope(s.eval, append(s.stack, ident)...), diags
return newStaticScope(s.eval, s.stack[0], append(s.stack[1:], ident)...), diags
}
// If an error occurs when resolving a dependent value, we need to add additional context to the diagnostics
func (s staticScopeData) enhanceDiagnostics(ident StaticIdentifier, diags tfdiags.Diagnostics) tfdiags.Diagnostics {
if len(s.stack) == 0 {
// No enhancement possible if we have nothing on our identifier stack.
// (We should not typically get here, but can in some contrived situations like unit tests)
return diags
}
if diags.HasErrors() {
top := s.stack[len(s.stack)-1]
diags = diags.Append(&hcl.Diagnostic{

View File

@ -20,6 +20,9 @@ import (
)
func TestStaticScope_GetInputVariable(t *testing.T) {
test_ident := StaticIdentifier{
Subject: "local.test_scenario",
}
t.Run("valid", func(t *testing.T) {
// This covers an assortment of valid cases that we can test in a single
// pass because they are all independent of one another.
@ -191,7 +194,7 @@ func TestStaticScope_GetInputVariable(t *testing.T) {
}
eval := NewStaticEvaluator(mod, call)
scope := newStaticScope(eval)
scope := newStaticScope(eval, test_ident)
for name, test := range tests {
t.Run(name, func(t *testing.T) {
@ -227,7 +230,7 @@ func TestStaticScope_GetInputVariable(t *testing.T) {
assertNoDiagnostics(t, diags)
eval := NewStaticEvaluator(mod, call)
scope := newStaticScope(eval)
scope := newStaticScope(eval, test_ident)
addr := addrs.InputVariable{Name: "bad_default"}
_, moreDiags := scope.Data.GetInputVariable(addr, tfdiags.SourceRange{Filename: "test.tf"})
@ -265,7 +268,7 @@ func TestStaticScope_GetInputVariable(t *testing.T) {
assertNoDiagnostics(t, diags)
eval := NewStaticEvaluator(mod, call)
scope := newStaticScope(eval)
scope := newStaticScope(eval, test_ident)
addr := addrs.InputVariable{Name: "not_nullable"}
_, moreDiags := scope.Data.GetInputVariable(addr, tfdiags.SourceRange{Filename: "test.tf"})
@ -280,6 +283,10 @@ func TestStaticScope_GetInputVariable(t *testing.T) {
}
func TestStaticScope_GetLocalValue(t *testing.T) {
test_ident := StaticIdentifier{
Subject: "local.test_scenario",
}
t.Run("valid", func(t *testing.T) {
p := testParser(map[string]string{
"test.tf": `
@ -303,7 +310,7 @@ func TestStaticScope_GetLocalValue(t *testing.T) {
assertNoDiagnostics(t, diags)
eval := NewStaticEvaluator(mod, call)
scope := newStaticScope(eval)
scope := newStaticScope(eval, test_ident)
addr := addrs.LocalValue{Name: "foo"}
got, moreDiags := scope.Data.GetLocalValue(addr, tfdiags.SourceRange{Filename: "test.tf"})
@ -334,7 +341,7 @@ func TestStaticScope_GetLocalValue(t *testing.T) {
assertNoDiagnostics(t, diags)
eval := NewStaticEvaluator(mod, call)
scope := newStaticScope(eval)
scope := newStaticScope(eval, test_ident)
addr := addrs.LocalValue{Name: "nonexist"}
_, moreDiags := scope.Data.GetLocalValue(addr, tfdiags.SourceRange{Filename: "test.tf"})