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