Replies: 1 comment 7 replies
-
| 
         A good idea! You'll probably have to drop down to the  https://pkg.go.dev/github.com/hashicorp/hcl2/hcl/hclsyntax At the point where anything is represented as a Go  The lower-level  https://pkg.go.dev/github.com/hashicorp/hcl2/hcl/hclsyntax#ObjectConsExpr Here's a starting point for you, partially GPT-written but edited to get some of the finer points of type handling right: package main
import (
	"fmt"
	"github.com/hashicorp/hcl/v2"
	"github.com/hashicorp/hcl/v2/hclsyntax"
	"github.com/zclconf/go-cty/cty"
)
func main() {
	hclContent := `
resource "null_resource" "test" {
    triggers = {
        a = "b"
        a = "c"
				dynamic = "d"
				(dynamic) = "e"
				(unknown) = "f"
    }
}
`
	file, diagnostics := hclsyntax.ParseConfig([]byte(hclContent), "main.tf", hcl.InitialPos)
	if diagnostics.HasErrors() {
		panic(diagnostics.Error())
	}
	findDuplicateKeys(file.Body)
}
func findDuplicateKeys(body hcl.Body) {
	content, _, diagnostics := body.PartialContent(&hcl.BodySchema{
		Blocks: []hcl.BlockHeaderSchema{{Type: "resource", LabelNames: []string{"type", "name"}}},
	})
	if diagnostics.HasErrors() {
		panic(diagnostics.Error())
	}
	for _, block := range content.Blocks {
		processBlock(block)
	}
}
func processBlock(block *hcl.Block) {
	content, _, diags := block.Body.PartialContent(&hcl.BodySchema{
		Attributes: []hcl.AttributeSchema{{Name: "triggers"}},
	})
	if diags.HasErrors() {
		panic(diags.Error())
	}
	if triggers, exists := content.Attributes["triggers"]; exists {
		expr := triggers.Expr
		exprMap, ok := expr.(*hclsyntax.ObjectConsExpr)
		if !ok {
			panic("Expected object expression")
		}
		keys := make(map[string]struct{})
		for _, item := range exprMap.Items {
			expr := item.KeyExpr.(*hclsyntax.ObjectConsKeyExpr)
			kv, diags := expr.Value(&hcl.EvalContext{
				Variables: map[string]cty.Value{
					"dynamic": cty.StringVal("dynamic"),
					"unknown": cty.UnknownVal(cty.String),
				},
			})
			if diags.HasErrors() {
				panic(diags.Error())
			}
			if !kv.IsKnown() {
				fmt.Printf("Unknown key, continuing")
				continue
			}
			if _, exists := keys[kv.AsString()]; exists {
				fmt.Printf("Duplicate key: %s\n", kv.AsString())
			} else {
				keys[kv.AsString()] = struct{}{}
			}
		}
	}
}This prints: The  Hope this helps as a starting point!  | 
  
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
Hi there,
I ran into this issue yesterday in production and was looking to add some tests to make sure it doesn't happen again.
Would it be possible to implement this check in tflint? I'm willing to do so but unfamiliar with the codebase. Will need some guidance.
Beta Was this translation helpful? Give feedback.
All reactions