1package hclsyntax 2 3import ( 4 "github.com/hashicorp/hcl2/hcl" 5) 6 7// Variables returns all of the variables referenced within a given experssion. 8// 9// This is the implementation of the "Variables" method on every native 10// expression. 11func Variables(expr Expression) []hcl.Traversal { 12 var vars []hcl.Traversal 13 14 walker := &variablesWalker{ 15 Callback: func(t hcl.Traversal) { 16 vars = append(vars, t) 17 }, 18 } 19 20 Walk(expr, walker) 21 22 return vars 23} 24 25// variablesWalker is a Walker implementation that calls its callback for any 26// root scope traversal found while walking. 27type variablesWalker struct { 28 Callback func(hcl.Traversal) 29 localScopes []map[string]struct{} 30} 31 32func (w *variablesWalker) Enter(n Node) hcl.Diagnostics { 33 switch tn := n.(type) { 34 case *ScopeTraversalExpr: 35 t := tn.Traversal 36 37 // Check if the given root name appears in any of the active 38 // local scopes. We don't want to return local variables here, since 39 // the goal of walking variables is to tell the calling application 40 // which names it needs to populate in the _root_ scope. 41 name := t.RootName() 42 for _, names := range w.localScopes { 43 if _, localized := names[name]; localized { 44 return nil 45 } 46 } 47 48 w.Callback(t) 49 case ChildScope: 50 w.localScopes = append(w.localScopes, tn.LocalNames) 51 } 52 return nil 53} 54 55func (w *variablesWalker) Exit(n Node) hcl.Diagnostics { 56 switch n.(type) { 57 case ChildScope: 58 // pop the latest local scope, assuming that the walker will 59 // behave symmetrically as promised. 60 w.localScopes = w.localScopes[:len(w.localScopes)-1] 61 } 62 return nil 63} 64 65// ChildScope is a synthetic AST node that is visited during a walk to 66// indicate that its descendent will be evaluated in a child scope, which 67// may mask certain variables from the parent scope as locals. 68// 69// ChildScope nodes don't really exist in the AST, but are rather synthesized 70// on the fly during walk. Therefore it doesn't do any good to transform them; 71// instead, transform either parent node that created a scope or the expression 72// that the child scope struct wraps. 73type ChildScope struct { 74 LocalNames map[string]struct{} 75 Expr Expression 76} 77 78func (e ChildScope) walkChildNodes(w internalWalkFunc) { 79 w(e.Expr) 80} 81 82// Range returns the range of the expression that the ChildScope is 83// encapsulating. It isn't really very useful to call Range on a ChildScope. 84func (e ChildScope) Range() hcl.Range { 85 return e.Expr.Range() 86} 87