package hclsyntax import ( "github.com/hashicorp/hcl/v2" ) // Variables returns all of the variables referenced within a given experssion. // // This is the implementation of the "Variables" method on every native // expression. func Variables(expr Expression) []hcl.Traversal { var vars []hcl.Traversal walker := &variablesWalker{ Callback: func(t hcl.Traversal) { vars = append(vars, t) }, } Walk(expr, walker) return vars } // variablesWalker is a Walker implementation that calls its callback for any // root scope traversal found while walking. type variablesWalker struct { Callback func(hcl.Traversal) localScopes []map[string]struct{} } func (w *variablesWalker) Enter(n Node) hcl.Diagnostics { 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 } func (w *variablesWalker) Exit(n Node) hcl.Diagnostics { 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{} Expr Expression } func (e ChildScope) walkChildNodes(w internalWalkFunc) { w(e.Expr) } // Range returns the range of the expression that the ChildScope is // encapsulating. It isn't really very useful to call Range on a ChildScope. func (e ChildScope) Range() hcl.Range { return e.Expr.Range() }