From fd4dfba625b45b3a08ccef65b01024618eefd37c Mon Sep 17 00:00:00 2001 From: Lasse Gaardsholt Date: Wed, 22 Oct 2025 19:43:55 +0000 Subject: [PATCH 1/5] AI added a test for this --- rules/terraform_lists_trailing_comma_test.go | 35 ++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/rules/terraform_lists_trailing_comma_test.go b/rules/terraform_lists_trailing_comma_test.go index 2f3f3b3..2e79804 100644 --- a/rules/terraform_lists_trailing_comma_test.go +++ b/rules/terraform_lists_trailing_comma_test.go @@ -32,6 +32,41 @@ func Test_TerraformListsTrailingCommaRule(t *testing.T) { }, }, }, + { + Name: "heredoc without trailing comma", + Content: `resource "terraform_data" "test" { + input = [ + "test", + <<-HERE + Lorem ipsum + HERE + ] +}`, + Expected: helper.Issues{ + { + Rule: NewTerraformListsTrailingCommaRule(), + Message: "Last item in lists should always end with a trailing comma", + Range: hcl.Range{ + Filename: "resource.tf", + Start: hcl.Pos{Line: 2, Column: 11}, + End: hcl.Pos{Line: 7, Column: 4}, + }, + }, + }, + }, + { + Name: "heredoc with trailing comma", + Content: `resource "terraform_data" "test" { + input = [ + "test", + <<-HERE + Lorem ipsum + HERE + , + ] +}`, + Expected: helper.Issues{}, + }, } rule := NewTerraformListsTrailingCommaRule() From d376c4d632c553ba9e64c2637ce361a50bfdb1b5 Mon Sep 17 00:00:00 2001 From: Lasse Gaardsholt Date: Thu, 23 Oct 2025 07:04:15 +0000 Subject: [PATCH 2/5] This should handle heredocs --- rules/terraform_lists_trailing_comma.go | 30 +++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/rules/terraform_lists_trailing_comma.go b/rules/terraform_lists_trailing_comma.go index 3f19f25..3acac45 100644 --- a/rules/terraform_lists_trailing_comma.go +++ b/rules/terraform_lists_trailing_comma.go @@ -60,13 +60,39 @@ func (r *TerraformListsTrailingCommaRule) Check(runner tflint.Runner) error { return nil } - if lastItemRange.End.Byte < len(file.Bytes) && file.Bytes[lastItemRange.End.Byte] != ',' { + // Check if there's already a trailing comma after the last item + // We need to skip whitespace and newlines to handle heredoc cases + hasTrailingComma := false + commaPos := lastItemRange.End.Byte + + // Skip whitespace and newlines after the last item to look for a comma + for commaPos < len(file.Bytes) && (file.Bytes[commaPos] == ' ' || file.Bytes[commaPos] == '\t' || file.Bytes[commaPos] == '\n' || file.Bytes[commaPos] == '\r') { + commaPos++ + } + + if commaPos < len(file.Bytes) && file.Bytes[commaPos] == ',' { + hasTrailingComma = true + } + + if !hasTrailingComma { + // Find the position after any whitespace to insert the comma + insertPos := lastItemRange.End.Byte + for insertPos < len(file.Bytes) && (file.Bytes[insertPos] == ' ' || file.Bytes[insertPos] == '\t') { + insertPos++ + } + + // Check if we're at a newline - if so, insert before it + insertText := "," + if insertPos < len(file.Bytes) && (file.Bytes[insertPos] == '\n' || file.Bytes[insertPos] == '\r') { + insertText = "," + } + if err := runner.EmitIssueWithFix( r, "Last item in lists should always end with a trailing comma", listRange, func(f tflint.Fixer) error { - return f.InsertTextAfter(lastItemRange, ",") + return f.InsertTextAfter(lastItemRange, insertText) }, ); err != nil { return hcl.Diagnostics{ From b2d7575bd650e200d1addc8d171210eb260dc227 Mon Sep 17 00:00:00 2001 From: Lasse Gaardsholt Date: Fri, 24 Oct 2025 09:25:16 +0200 Subject: [PATCH 3/5] Refactor trailing comma check to handle heredocs correctly Signed-off-by: Lasse Gaardsholt --- rules/terraform_lists_trailing_comma.go | 57 ++++++++++++++----------- 1 file changed, 33 insertions(+), 24 deletions(-) diff --git a/rules/terraform_lists_trailing_comma.go b/rules/terraform_lists_trailing_comma.go index 3acac45..c2a0b99 100644 --- a/rules/terraform_lists_trailing_comma.go +++ b/rules/terraform_lists_trailing_comma.go @@ -74,34 +74,43 @@ func (r *TerraformListsTrailingCommaRule) Check(runner tflint.Runner) error { hasTrailingComma = true } - if !hasTrailingComma { - // Find the position after any whitespace to insert the comma - insertPos := lastItemRange.End.Byte - for insertPos < len(file.Bytes) && (file.Bytes[insertPos] == ' ' || file.Bytes[insertPos] == '\t') { - insertPos++ - } + if hasTrailingComma { + return nil + } - // Check if we're at a newline - if so, insert before it - insertText := "," - if insertPos < len(file.Bytes) && (file.Bytes[insertPos] == '\n' || file.Bytes[insertPos] == '\r') { - insertText = "," + // Check if the last item is a heredoc. + // A heredoc is a TemplateExpr with a single LiteralValueExpr part. + isHeredoc := false + if template, ok := lastItem.(*hclsyntax.TemplateExpr); ok { + if len(template.Parts) == 1 { + if _, isLiteral := template.Parts[0].(*hclsyntax.LiteralValueExpr); isLiteral { + // This is a strong indicator of a heredoc, especially if it spans multiple lines. + if template.Range().Start.Line != template.Range().End.Line { + isHeredoc = true + } + } } + } + + insertText := "," + if isHeredoc { + insertText = "\n," + } - if err := runner.EmitIssueWithFix( - r, - "Last item in lists should always end with a trailing comma", - listRange, - func(f tflint.Fixer) error { - return f.InsertTextAfter(lastItemRange, insertText) + if err := runner.EmitIssueWithFix( + r, + "Last item in lists should always end with a trailing comma", + listRange, + func(f tflint.Fixer) error { + return f.InsertTextAfter(lastItemRange, insertText) + }, + ); err != nil { + return hcl.Diagnostics{ + { + Severity: hcl.DiagError, + Summary: "failed to call EmitIssueWithFix()", + Detail: err.Error(), }, - ); err != nil { - return hcl.Diagnostics{ - { - Severity: hcl.DiagError, - Summary: "failed to call EmitIssueWithFix()", - Detail: err.Error(), - }, - } } } From 7b0940cd4edbb36bfadcba41c934a9c612ba21ea Mon Sep 17 00:00:00 2001 From: Lasse Gaardsholt Date: Fri, 24 Oct 2025 09:31:46 +0200 Subject: [PATCH 4/5] simplifed Signed-off-by: Lasse Gaardsholt --- rules/terraform_lists_trailing_comma.go | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/rules/terraform_lists_trailing_comma.go b/rules/terraform_lists_trailing_comma.go index c2a0b99..2abb4b6 100644 --- a/rules/terraform_lists_trailing_comma.go +++ b/rules/terraform_lists_trailing_comma.go @@ -78,25 +78,20 @@ func (r *TerraformListsTrailingCommaRule) Check(runner tflint.Runner) error { return nil } + insertText := "," // Check if the last item is a heredoc. // A heredoc is a TemplateExpr with a single LiteralValueExpr part. - isHeredoc := false if template, ok := lastItem.(*hclsyntax.TemplateExpr); ok { if len(template.Parts) == 1 { if _, isLiteral := template.Parts[0].(*hclsyntax.LiteralValueExpr); isLiteral { // This is a strong indicator of a heredoc, especially if it spans multiple lines. if template.Range().Start.Line != template.Range().End.Line { - isHeredoc = true + insertText = "\n," } } } } - insertText := "," - if isHeredoc { - insertText = "\n," - } - if err := runner.EmitIssueWithFix( r, "Last item in lists should always end with a trailing comma", From 017a4bd070fdd64ebe700cce05795adf2597cd36 Mon Sep 17 00:00:00 2001 From: Lasse Gaardsholt Date: Fri, 24 Oct 2025 09:33:04 +0200 Subject: [PATCH 5/5] Remove unnecessary variable for trailing comma check in Check function Signed-off-by: Lasse Gaardsholt --- rules/terraform_lists_trailing_comma.go | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/rules/terraform_lists_trailing_comma.go b/rules/terraform_lists_trailing_comma.go index 2abb4b6..e1060b5 100644 --- a/rules/terraform_lists_trailing_comma.go +++ b/rules/terraform_lists_trailing_comma.go @@ -62,7 +62,6 @@ func (r *TerraformListsTrailingCommaRule) Check(runner tflint.Runner) error { // Check if there's already a trailing comma after the last item // We need to skip whitespace and newlines to handle heredoc cases - hasTrailingComma := false commaPos := lastItemRange.End.Byte // Skip whitespace and newlines after the last item to look for a comma @@ -71,10 +70,7 @@ func (r *TerraformListsTrailingCommaRule) Check(runner tflint.Runner) error { } if commaPos < len(file.Bytes) && file.Bytes[commaPos] == ',' { - hasTrailingComma = true - } - - if hasTrailingComma { + // It already has a trailling comma return nil }