2017-09-11 23:40:37 +00:00
|
|
|
package hclsyntax
|
2017-05-24 15:05:52 +00:00
|
|
|
|
|
|
|
import (
|
2019-09-09 23:08:19 +00:00
|
|
|
"github.com/hashicorp/hcl/v2"
|
2017-05-24 15:05:52 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// Variables returns all of the variables referenced within a given experssion.
|
|
|
|
//
|
|
|
|
// This is the implementation of the "Variables" method on every native
|
|
|
|
// expression.
|
2017-09-11 23:40:37 +00:00
|
|
|
func Variables(expr Expression) []hcl.Traversal {
|
|
|
|
var vars []hcl.Traversal
|
2017-06-13 15:53:33 +00:00
|
|
|
|
2017-06-14 15:03:32 +00:00
|
|
|
walker := &variablesWalker{
|
2017-09-11 23:40:37 +00:00
|
|
|
Callback: func(t hcl.Traversal) {
|
2017-06-14 15:03:32 +00:00
|
|
|
vars = append(vars, t)
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
Walk(expr, walker)
|
2017-06-13 15:53:33 +00:00
|
|
|
|
2017-05-24 15:05:52 +00:00
|
|
|
return vars
|
|
|
|
}
|
2017-06-14 15:03:32 +00:00
|
|
|
|
|
|
|
// variablesWalker is a Walker implementation that calls its callback for any
|
|
|
|
// root scope traversal found while walking.
|
|
|
|
type variablesWalker struct {
|
2017-09-11 23:40:37 +00:00
|
|
|
Callback func(hcl.Traversal)
|
2017-06-14 15:03:32 +00:00
|
|
|
localScopes []map[string]struct{}
|
|
|
|
}
|
|
|
|
|
2017-09-11 23:40:37 +00:00
|
|
|
func (w *variablesWalker) Enter(n Node) hcl.Diagnostics {
|
2017-06-14 15:03:32 +00:00
|
|
|
switch tn := n.(type) {
|
|
|
|
case *ScopeTraversalExpr:
|
|
|
|
t := tn.Traversal
|
|
|
|
|
|
|
|
// Check if the given root name appears in any of the active
|
|
|
|
// local scopes. We don't want to return local variables here, since
|
|
|
|
// the goal of walking variables is to tell the calling application
|
|
|
|
// which names it needs to populate in the _root_ scope.
|
|
|
|
name := t.RootName()
|
|
|
|
for _, names := range w.localScopes {
|
|
|
|
if _, localized := names[name]; localized {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
w.Callback(t)
|
|
|
|
case ChildScope:
|
|
|
|
w.localScopes = append(w.localScopes, tn.LocalNames)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2017-09-11 23:40:37 +00:00
|
|
|
func (w *variablesWalker) Exit(n Node) hcl.Diagnostics {
|
2017-06-14 15:03:32 +00:00
|
|
|
switch n.(type) {
|
|
|
|
case ChildScope:
|
|
|
|
// pop the latest local scope, assuming that the walker will
|
|
|
|
// behave symmetrically as promised.
|
|
|
|
w.localScopes = w.localScopes[:len(w.localScopes)-1]
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// ChildScope is a synthetic AST node that is visited during a walk to
|
|
|
|
// indicate that its descendent will be evaluated in a child scope, which
|
|
|
|
// may mask certain variables from the parent scope as locals.
|
|
|
|
//
|
|
|
|
// ChildScope nodes don't really exist in the AST, but are rather synthesized
|
|
|
|
// on the fly during walk. Therefore it doesn't do any good to transform them;
|
|
|
|
// instead, transform either parent node that created a scope or the expression
|
|
|
|
// that the child scope struct wraps.
|
|
|
|
type ChildScope struct {
|
|
|
|
LocalNames map[string]struct{}
|
2018-09-26 14:38:43 +00:00
|
|
|
Expr Expression
|
2017-06-14 15:03:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (e ChildScope) walkChildNodes(w internalWalkFunc) {
|
2018-09-26 14:38:43 +00:00
|
|
|
w(e.Expr)
|
2017-06-14 15:03:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Range returns the range of the expression that the ChildScope is
|
|
|
|
// encapsulating. It isn't really very useful to call Range on a ChildScope.
|
2017-09-11 23:40:37 +00:00
|
|
|
func (e ChildScope) Range() hcl.Range {
|
2018-09-26 14:38:43 +00:00
|
|
|
return e.Expr.Range()
|
2017-06-14 15:03:32 +00:00
|
|
|
}
|