github.com/terraform-linters/tflint@v0.51.2-0.20240520175844-3750771571b6/terraform/tfhcl/variables_hclext.go (about)

     1  package tfhcl
     2  
     3  import (
     4  	"github.com/hashicorp/hcl/v2"
     5  	"github.com/hashicorp/hcl/v2/ext/dynblock"
     6  	"github.com/terraform-linters/tflint-plugin-sdk/hclext"
     7  )
     8  
     9  // ExpandVariablesHCLExt is a wrapper around dynblock.WalkVariables that
    10  // uses the given hclext.BodySchema to automatically drive the recursive
    11  // walk through nested blocks in the given body.
    12  //
    13  // Note that it's a wrapper around ExpandVariables, not WalkExpandVariables.
    14  // This package evaluates expressions immediately on expansion, so we always
    15  // need all variables to expand. It also implicitly walks count/for_each to
    16  // support expansion by meta-arguments.
    17  func ExpandVariablesHCLExt(body hcl.Body, schema *hclext.BodySchema) []hcl.Traversal {
    18  	rootNode := dynblock.WalkVariables(body)
    19  	return walkVariablesWithHCLExt(rootNode, schema)
    20  }
    21  
    22  func walkVariablesWithHCLExt(node dynblock.WalkVariablesNode, schema *hclext.BodySchema) []hcl.Traversal {
    23  	vars, children := node.Visit(extendSchema(asHCLSchema(schema)))
    24  
    25  	if len(children) > 0 {
    26  		childSchemas := childBlockTypes(schema)
    27  		for _, child := range children {
    28  			if childSchema, exists := childSchemas[child.BlockTypeName]; exists {
    29  				vars = append(vars, walkVariablesWithHCLExt(child.Node, childSchema.Body)...)
    30  			}
    31  		}
    32  	}
    33  
    34  	return vars
    35  }
    36  
    37  func asHCLSchema(in *hclext.BodySchema) *hcl.BodySchema {
    38  	out := &hcl.BodySchema{}
    39  	if in == nil || in.Mode == hclext.SchemaJustAttributesMode {
    40  		return out
    41  	}
    42  
    43  	out.Attributes = make([]hcl.AttributeSchema, len(in.Attributes))
    44  	for idx, attr := range in.Attributes {
    45  		out.Attributes[idx] = hcl.AttributeSchema{Name: attr.Name, Required: attr.Required}
    46  	}
    47  	out.Blocks = make([]hcl.BlockHeaderSchema, len(in.Blocks))
    48  	for idx, block := range in.Blocks {
    49  		out.Blocks[idx] = hcl.BlockHeaderSchema{Type: block.Type, LabelNames: block.LabelNames}
    50  	}
    51  	return out
    52  }
    53  
    54  func extendSchema(schema *hcl.BodySchema) *hcl.BodySchema {
    55  	schema.Attributes = append(schema.Attributes, hcl.AttributeSchema{Name: "count"}, hcl.AttributeSchema{Name: "for_each"})
    56  	return schema
    57  }
    58  
    59  func childBlockTypes(schema *hclext.BodySchema) map[string]hclext.BlockSchema {
    60  	ret := make(map[string]hclext.BlockSchema)
    61  	for _, block := range schema.Blocks {
    62  		ret[block.Type] = block
    63  	}
    64  	return ret
    65  }