github.com/opentofu/opentofu@v1.7.1/internal/lang/blocktoattr/variables.go (about)

     1  // Copyright (c) The OpenTofu Authors
     2  // SPDX-License-Identifier: MPL-2.0
     3  // Copyright (c) 2023 HashiCorp, Inc.
     4  // SPDX-License-Identifier: MPL-2.0
     5  
     6  package blocktoattr
     7  
     8  import (
     9  	"github.com/hashicorp/hcl/v2"
    10  	"github.com/hashicorp/hcl/v2/ext/dynblock"
    11  	"github.com/hashicorp/hcl/v2/hcldec"
    12  	"github.com/opentofu/opentofu/internal/configs/configschema"
    13  )
    14  
    15  // ExpandedVariables finds all of the global variables referenced in the
    16  // given body with the given schema while taking into account the possibilities
    17  // both of "dynamic" blocks being expanded and the possibility of certain
    18  // attributes being written instead as nested blocks as allowed by the
    19  // FixUpBlockAttrs function.
    20  //
    21  // This function exists to allow variables to be analyzed prior to dynamic
    22  // block expansion while also dealing with the fact that dynamic block expansion
    23  // might in turn produce nested blocks that are subject to FixUpBlockAttrs.
    24  //
    25  // This is intended as a drop-in replacement for dynblock.VariablesHCLDec,
    26  // which is itself a drop-in replacement for hcldec.Variables.
    27  func ExpandedVariables(body hcl.Body, schema *configschema.Block) []hcl.Traversal {
    28  	rootNode := dynblock.WalkVariables(body)
    29  	return walkVariables(rootNode, body, schema)
    30  }
    31  
    32  func walkVariables(node dynblock.WalkVariablesNode, body hcl.Body, schema *configschema.Block) []hcl.Traversal {
    33  	givenRawSchema := hcldec.ImpliedSchema(schema.DecoderSpec())
    34  	ambiguousNames := ambiguousNames(schema)
    35  	effectiveRawSchema := effectiveSchema(givenRawSchema, body, ambiguousNames, false)
    36  	vars, children := node.Visit(effectiveRawSchema)
    37  
    38  	for _, child := range children {
    39  		if blockS, exists := schema.BlockTypes[child.BlockTypeName]; exists {
    40  			vars = append(vars, walkVariables(child.Node, child.Body(), &blockS.Block)...)
    41  		} else if attrS, exists := schema.Attributes[child.BlockTypeName]; exists && attrS.Type.IsCollectionType() && attrS.Type.ElementType().IsObjectType() {
    42  			// ☝️Check for collection type before element type, because if this is a mis-placed reference,
    43  			// a panic here will prevent other useful diags from being elevated to show the user what to fix
    44  			synthSchema := SchemaForCtyElementType(attrS.Type.ElementType())
    45  			vars = append(vars, walkVariables(child.Node, child.Body(), synthSchema)...)
    46  		}
    47  	}
    48  
    49  	return vars
    50  }