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  }