summaryrefslogtreecommitdiff
path: root/hclsyntax/variables.go
diff options
context:
space:
mode:
authorMartin Atkins <mart@degeneration.co.uk>2019-09-09 16:08:19 -0700
committerMartin Atkins <mart@degeneration.co.uk>2019-09-09 16:08:19 -0700
commit6c4344623b6ac528a57f9b80e4622acfab2fde40 (patch)
tree83031084d3ab54abbe40e3fd749e439c8b28e9d1 /hclsyntax/variables.go
parent0f5ab3bd563c111077917020c0cbc8c211e1bff3 (diff)
Unfold the "hcl" directory up into the root
The main HCL package is more visible this way, and so it's easier than having to pick it out from dozens of other package directories.
Diffstat (limited to 'hclsyntax/variables.go')
-rw-r--r--hclsyntax/variables.go86
1 files changed, 86 insertions, 0 deletions
diff --git a/hclsyntax/variables.go b/hclsyntax/variables.go
new file mode 100644
index 0000000..3d68c41
--- /dev/null
+++ b/hclsyntax/variables.go
@@ -0,0 +1,86 @@
+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()
+}