rules/terraform_count_index_usage.go (95 lines of code) (raw):

package rules import ( "github.com/hashicorp/go-multierror" "github.com/hashicorp/hcl/v2" "github.com/hashicorp/hcl/v2/hclsyntax" "github.com/terraform-linters/tflint-plugin-sdk/logger" "github.com/terraform-linters/tflint-plugin-sdk/tflint" ) var _ tflint.Rule = &TerraformCountIndexUsageRule{} // TerraformCountIndexUsageRule checks whether count.index is used as subscript of list/map type TerraformCountIndexUsageRule struct { tflint.DefaultRule } func (r *TerraformCountIndexUsageRule) Enabled() bool { return false } // NewTerraformCountIndexUsageRule returns a new rule func NewTerraformCountIndexUsageRule() *TerraformCountIndexUsageRule { return &TerraformCountIndexUsageRule{} } // Name returns the rule name func (r *TerraformCountIndexUsageRule) Name() string { return "terraform_count_index_usage" } // Severity returns the rule severity func (r *TerraformCountIndexUsageRule) Severity() tflint.Severity { return tflint.WARNING } func (r *TerraformCountIndexUsageRule) Check(runner tflint.Runner) error { return ForFiles(runner, r.CheckFile) } func (r *TerraformCountIndexUsageRule) CheckFile(runner tflint.Runner, file *hcl.File) error { body, ok := file.Body.(*hclsyntax.Body) if !ok { logger.Debug("skip terraform_count_index_usage since it's not hcl file") return nil } blocks := body.Blocks var err error for _, block := range blocks { if subErr := r.visitBlock(runner, block); subErr != nil { err = multierror.Append(err, subErr) } } return err } func (r *TerraformCountIndexUsageRule) visitBlock(runner tflint.Runner, block *hclsyntax.Block) error { var err error for _, attr := range block.Body.Attributes { if subErr := r.visitExp(runner, attr.Expr); subErr != nil { err = multierror.Append(err, subErr) } } for _, nestedBlock := range block.Body.Blocks { if subErr := r.visitBlock(runner, nestedBlock); subErr != nil { err = multierror.Append(err, subErr) } } return err } func (r *TerraformCountIndexUsageRule) visitExp(runner tflint.Runner, exp hclsyntax.Expression) error { file, _ := runner.GetFile(exp.Range().Filename) tokens, diags := hclsyntax.LexExpression(exp.Range().SliceBytes(file.Bytes), exp.Range().Filename, exp.StartRange().Start) if diags.HasErrors() { return diags } var err error depth := 0 for i, token := range tokens { switch token.Type { case hclsyntax.TokenOBrack: depth++ case hclsyntax.TokenCBrack: depth-- case hclsyntax.TokenIdent: if depth == 0 || i+2 >= len(tokens) { continue } first, second, third := string(token.Bytes), string(tokens[i+1].Bytes), string(tokens[i+2].Bytes) if first == "count" && second == "." && third == "index" { subErr := runner.EmitIssue( r, "`count.index` is not recommended to be used as the subscript of list/map, use for_each instead", hcl.Range{ Filename: token.Range.Filename, Start: token.Range.Start, End: tokens[i+2].Range.End, }, ) if subErr != nil { err = multierror.Append(err, subErr) } } } } return err }