Files
opentf/internal/lang/exprs/closure.go
Martin Atkins aa4743f23f lang/eval+lang/exprs: More exprs.EvalError markings
It doesn't hurt to be liberal in handling this everywhere that we know
there's an error because applying the mark where it was already present
is effectively a no-op, so we'll introduce more of these both for
robustness and to help folks who are reading this code in future to
learn the EvalError patterns by observation.

Signed-off-by: Martin Atkins <mart@degeneration.co.uk>
2025-10-27 10:15:41 -07:00

65 lines
2.3 KiB
Go

// Copyright (c) The OpenTofu Authors
// SPDX-License-Identifier: MPL-2.0
// Copyright (c) 2023 HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package exprs
import (
"context"
"github.com/hashicorp/hcl/v2"
"github.com/zclconf/go-cty/cty"
"github.com/opentofu/opentofu/internal/tfdiags"
)
// Closure is an [Evalable] bound to the [Scope] where it was declared, so
// that the two can travel together.
//
// Closure essentially turns an [Evalable] into a [Valuer], allowing it
// to be evaluated without separately tracking the scope it belongs to.
type Closure struct {
evalable Evalable
scope Scope
}
var _ Valuer = (*Closure)(nil)
// NewClosure associates the given [Evalable] with the given [Scope] so that
// it can be evaluated somewhere else later without losing track of what symbols
// and functions were available where it was declared.
//
// Passing a nil Scope is valid, and represents that there are absolutely no
// symbols or functions available for use in the given Evalable. Note that HCL's
// JSON syntax treats that situation quite differently by taking JSON strings
// totally literally instead of trying to interpret them as HCL templates, and
// so switching to or from a nil scope is typically a breaking change for what's
// allowed in a particular position.
func NewClosure(evalable Evalable, scope Scope) *Closure {
return &Closure{evalable, scope}
}
// StaticCheckTraversal checks whether the given traversal could apply to any
// possible result from [Closure.Value] on this closure, returning error
// diagnostics if not.
func (c *Closure) StaticCheckTraversal(traversal hcl.Traversal) tfdiags.Diagnostics {
return StaticCheckTraversal(traversal, c.evalable)
}
// Value returns the result of evaluating the enclosed [Evalable] in the
// enclosed [Scope].
//
// Some [Evalable] implementations block on potentially-time-consuming
// operations, in which case they should respond gracefully to cancellation
// of the given context.
func (c *Closure) Value(ctx context.Context) (cty.Value, tfdiags.Diagnostics) {
return EvalResult(Evaluate(ctx, c.evalable, c.scope))
}
// SourceRange returns the source range of the underlying [Evalable].
func (c *Closure) ValueSourceRange() *tfdiags.SourceRange {
ret := c.evalable.EvalableSourceRange()
return &ret
}