https://github.com/vbvictor updated 
https://github.com/llvm/llvm-project/pull/173669

>From 60ecb84f835c228fe899eba7c3f0973f802156b4 Mon Sep 17 00:00:00 2001
From: Victor Baranov <[email protected]>
Date: Tue, 23 Dec 2025 02:27:23 +0300
Subject: [PATCH 1/4] [clang-tidy] Add new check readability-trailing-comma

---
 .../clang-tidy/readability/CMakeLists.txt     |   1 +
 .../readability/ReadabilityTidyModule.cpp     |   3 +
 .../readability/TrailingCommaCheck.cpp        | 167 ++++++++++++++++++
 .../readability/TrailingCommaCheck.h          |  54 ++++++
 clang-tools-extra/docs/ReleaseNotes.rst       |   6 +
 .../docs/clang-tidy/checks/list.rst           |   1 +
 .../checks/readability/trailing-comma.rst     |  83 +++++++++
 .../trailing-comma-cxx20-remove.cpp           |  46 +++++
 .../readability/trailing-comma-cxx20.cpp      |  46 +++++
 .../readability/trailing-comma-remove.cpp     |  49 +++++
 .../readability/trailing-comma-threshold.cpp  |  20 +++
 .../checkers/readability/trailing-comma.c     |  40 +++++
 .../checkers/readability/trailing-comma.cpp   |  76 ++++++++
 13 files changed, 592 insertions(+)
 create mode 100644 
clang-tools-extra/clang-tidy/readability/TrailingCommaCheck.cpp
 create mode 100644 
clang-tools-extra/clang-tidy/readability/TrailingCommaCheck.h
 create mode 100644 
clang-tools-extra/docs/clang-tidy/checks/readability/trailing-comma.rst
 create mode 100644 
clang-tools-extra/test/clang-tidy/checkers/readability/trailing-comma-cxx20-remove.cpp
 create mode 100644 
clang-tools-extra/test/clang-tidy/checkers/readability/trailing-comma-cxx20.cpp
 create mode 100644 
clang-tools-extra/test/clang-tidy/checkers/readability/trailing-comma-remove.cpp
 create mode 100644 
clang-tools-extra/test/clang-tidy/checkers/readability/trailing-comma-threshold.cpp
 create mode 100644 
clang-tools-extra/test/clang-tidy/checkers/readability/trailing-comma.c
 create mode 100644 
clang-tools-extra/test/clang-tidy/checkers/readability/trailing-comma.cpp

diff --git a/clang-tools-extra/clang-tidy/readability/CMakeLists.txt 
b/clang-tools-extra/clang-tidy/readability/CMakeLists.txt
index 161a0d96caf41..79fa4e2273320 100644
--- a/clang-tools-extra/clang-tidy/readability/CMakeLists.txt
+++ b/clang-tools-extra/clang-tidy/readability/CMakeLists.txt
@@ -57,6 +57,7 @@ add_clang_library(clangTidyReadabilityModule STATIC
   StaticDefinitionInAnonymousNamespaceCheck.cpp
   StringCompareCheck.cpp
   SuspiciousCallArgumentCheck.cpp
+  TrailingCommaCheck.cpp
   UniqueptrDeleteReleaseCheck.cpp
   UppercaseLiteralSuffixCheck.cpp
   UseAnyOfAllOfCheck.cpp
diff --git a/clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp 
b/clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp
index 85b4fa322b263..c3dfd7ecac7a7 100644
--- a/clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp
+++ b/clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp
@@ -60,6 +60,7 @@
 #include "StaticDefinitionInAnonymousNamespaceCheck.h"
 #include "StringCompareCheck.h"
 #include "SuspiciousCallArgumentCheck.h"
+#include "TrailingCommaCheck.h"
 #include "UniqueptrDeleteReleaseCheck.h"
 #include "UppercaseLiteralSuffixCheck.h"
 #include "UseAnyOfAllOfCheck.h"
@@ -175,6 +176,8 @@ class ReadabilityModule : public ClangTidyModule {
         "readability-simplify-boolean-expr");
     CheckFactories.registerCheck<SuspiciousCallArgumentCheck>(
         "readability-suspicious-call-argument");
+    CheckFactories.registerCheck<TrailingCommaCheck>(
+        "readability-trailing-comma");
     CheckFactories.registerCheck<UniqueptrDeleteReleaseCheck>(
         "readability-uniqueptr-delete-release");
     CheckFactories.registerCheck<UppercaseLiteralSuffixCheck>(
diff --git a/clang-tools-extra/clang-tidy/readability/TrailingCommaCheck.cpp 
b/clang-tools-extra/clang-tidy/readability/TrailingCommaCheck.cpp
new file mode 100644
index 0000000000000..6552e6d540550
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/readability/TrailingCommaCheck.cpp
@@ -0,0 +1,167 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "TrailingCommaCheck.h"
+#include "../utils/LexerUtils.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy {
+
+template <>
+struct OptionEnumMapping<readability::TrailingCommaCheck::CommaPolicyKind> {
+  static llvm::ArrayRef<
+      std::pair<readability::TrailingCommaCheck::CommaPolicyKind, StringRef>>
+  getEnumMapping() {
+    static constexpr 
std::pair<readability::TrailingCommaCheck::CommaPolicyKind,
+                               StringRef>
+        Mapping[] = {
+            {readability::TrailingCommaCheck::CommaPolicyKind::Append,
+             "Append"},
+            {readability::TrailingCommaCheck::CommaPolicyKind::Remove,
+             "Remove"},
+        };
+    return {Mapping};
+  }
+};
+
+} // namespace clang::tidy
+
+namespace clang::tidy::readability {
+
+namespace {
+
+AST_POLYMORPHIC_MATCHER(isMacro,
+                        AST_POLYMORPHIC_SUPPORTED_TYPES(EnumDecl,
+                                                        InitListExpr)) {
+  return Node.getBeginLoc().isMacroID() || Node.getEndLoc().isMacroID();
+}
+
+AST_MATCHER(EnumDecl, isEmptyEnum) { return Node.enumerators().empty(); }
+
+AST_MATCHER(InitListExpr, isEmptyInitList) { return Node.getNumInits() == 0; }
+
+} // namespace
+
+TrailingCommaCheck::TrailingCommaCheck(StringRef Name,
+                                       ClangTidyContext *Context)
+    : ClangTidyCheck(Name, Context),
+      CommaPolicy(Options.get("CommaPolicy", CommaPolicyKind::Append)),
+      EnumThreshold(Options.get("EnumThreshold", 1U)),
+      InitListThreshold(Options.get("InitListThreshold", 3U)) {}
+
+void TrailingCommaCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
+  Options.store(Opts, "CommaPolicy", CommaPolicy);
+  Options.store(Opts, "EnumThreshold", EnumThreshold);
+  Options.store(Opts, "InitListThreshold", InitListThreshold);
+}
+
+void TrailingCommaCheck::registerMatchers(MatchFinder *Finder) {
+  Finder->addMatcher(
+      enumDecl(isDefinition(), unless(isEmptyEnum()), unless(isMacro()))
+          .bind("enum"),
+      this);
+
+  Finder->addMatcher(initListExpr(unless(isEmptyInitList()), unless(isMacro()))
+                         .bind("initlist"),
+                     this);
+}
+
+void TrailingCommaCheck::check(const MatchFinder::MatchResult &Result) {
+  if (const auto *Enum = Result.Nodes.getNodeAs<EnumDecl>("enum"))
+    checkEnumDecl(Enum, Result);
+  else if (const auto *InitList =
+               Result.Nodes.getNodeAs<InitListExpr>("initlist"))
+    checkInitListExpr(InitList, Result);
+  else
+    llvm_unreachable("No matches found");
+}
+
+void TrailingCommaCheck::checkEnumDecl(const EnumDecl *Enum,
+                                       const MatchFinder::MatchResult &Result) 
{
+  // Count enumerators and get the last one
+  unsigned NumEnumerators = 0;
+  const EnumConstantDecl *LastEnumerator = nullptr;
+  for (const EnumConstantDecl *ECD : Enum->enumerators()) {
+    LastEnumerator = ECD;
+    ++NumEnumerators;
+  }
+  assert(LastEnumerator);
+  assert(NumEnumerators > 0);
+
+  if (NumEnumerators < EnumThreshold)
+    return;
+
+  SourceLocation LastEnumLoc;
+  if (const Expr *Init = LastEnumerator->getInitExpr())
+    LastEnumLoc = Init->getEndLoc();
+  else
+    LastEnumLoc = LastEnumerator->getLocation();
+
+  if (LastEnumLoc.isInvalid())
+    return;
+
+  emitDiag(LastEnumLoc, DiagKind::Enum, Result);
+}
+
+void TrailingCommaCheck::checkInitListExpr(
+    const InitListExpr *InitList, const MatchFinder::MatchResult &Result) {
+  // We need to use the syntactic form for correct source locations.
+  if (InitList->isSemanticForm())
+    if (const InitListExpr *SyntacticForm = InitList->getSyntacticForm())
+      InitList = SyntacticForm;
+
+  const unsigned NumInits = InitList->getNumInits();
+  if (NumInits < InitListThreshold)
+    return;
+
+  const Expr *LastInit = InitList->getInit(NumInits - 1);
+  assert(LastInit);
+
+  // Skip pack expansions - they already have special syntax with '...'
+  if (isa<PackExpansionExpr>(LastInit))
+    return;
+
+  const SourceLocation LastInitLoc = LastInit->getEndLoc();
+  if (LastInitLoc.isInvalid())
+    return;
+
+  emitDiag(LastInitLoc, DiagKind::InitList, Result);
+}
+
+void TrailingCommaCheck::emitDiag(
+    SourceLocation LastLoc, DiagKind Kind,
+    const ast_matchers::MatchFinder::MatchResult &Result) {
+  const std::optional<Token> NextToken =
+      utils::lexer::findNextTokenSkippingComments(
+          LastLoc, *Result.SourceManager, getLangOpts());
+  if (!NextToken)
+    return;
+
+  const bool HasTrailingComma = NextToken->is(tok::comma);
+  const SourceLocation InsertLoc = Lexer::getLocForEndOfToken(
+      LastLoc, 0, *Result.SourceManager, getLangOpts());
+
+  if (CommaPolicy == CommaPolicyKind::Append && !HasTrailingComma) {
+    diag(InsertLoc, "%select{initializer list|enum}0 should have "
+                    "a trailing comma")
+        << Kind << FixItHint::CreateInsertion(InsertLoc, ",");
+  } else if (CommaPolicy == CommaPolicyKind::Remove && HasTrailingComma) {
+    const SourceLocation CommaLoc = NextToken->getLocation();
+    if (CommaLoc.isInvalid())
+      return;
+    diag(CommaLoc, "%select{initializer list|enum}0 should not have "
+                   "a trailing comma")
+        << Kind << FixItHint::CreateRemoval(CommaLoc);
+  }
+}
+
+} // namespace clang::tidy::readability
diff --git a/clang-tools-extra/clang-tidy/readability/TrailingCommaCheck.h 
b/clang-tools-extra/clang-tidy/readability/TrailingCommaCheck.h
new file mode 100644
index 0000000000000..789e4a6d30df4
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/readability/TrailingCommaCheck.h
@@ -0,0 +1,54 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_TRAILINGCOMMACHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_TRAILINGCOMMACHECK_H
+
+#include "../ClangTidyCheck.h"
+
+namespace clang::tidy::readability {
+
+/// Checks for presence or absence of trailing commas in enum definitions
+/// and initializer lists.
+///
+/// For the user-facing documentation see:
+/// 
https://clang.llvm.org/extra/clang-tidy/checks/readability/trailing-comma.html
+class TrailingCommaCheck : public ClangTidyCheck {
+public:
+  TrailingCommaCheck(StringRef Name, ClangTidyContext *Context);
+  void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+  bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
+    return LangOpts.CPlusPlus11 || LangOpts.C99;
+  }
+  std::optional<TraversalKind> getCheckTraversalKind() const override {
+    return TK_IgnoreUnlessSpelledInSource;
+  }
+
+  enum class CommaPolicyKind { Append, Remove };
+
+private:
+  const CommaPolicyKind CommaPolicy;
+  const unsigned EnumThreshold;
+  const unsigned InitListThreshold;
+
+  void checkEnumDecl(const EnumDecl *Enum,
+                     const ast_matchers::MatchFinder::MatchResult &Result);
+  void checkInitListExpr(const InitListExpr *InitList,
+                         const ast_matchers::MatchFinder::MatchResult &Result);
+
+  // Values correspond to %select{initializer list|enum}0 indices
+  enum DiagKind { InitList = 0, Enum = 1 };
+  void emitDiag(SourceLocation LastLoc, DiagKind Kind,
+                const ast_matchers::MatchFinder::MatchResult &Result);
+};
+
+} // namespace clang::tidy::readability
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_TRAILINGCOMMACHECK_H
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst 
b/clang-tools-extra/docs/ReleaseNotes.rst
index e4ff640811933..7b2d7196d14cf 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -271,6 +271,12 @@ New checks
 
   Finds redundant uses of the ``typename`` keyword.
 
+- New :doc:`readability-trailing-comma
+  <clang-tidy/checks/readability/trailing-comma>` check.
+
+  Checks for presence or absence of trailing commas in enum definitions and
+  initializer lists.
+
 New check aliases
 ^^^^^^^^^^^^^^^^^
 
diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst 
b/clang-tools-extra/docs/clang-tidy/checks/list.rst
index e5e77b5cc418b..678ebf09ad444 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/list.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst
@@ -422,6 +422,7 @@ Clang-Tidy Checks
    :doc:`readability-static-definition-in-anonymous-namespace 
<readability/static-definition-in-anonymous-namespace>`, "Yes"
    :doc:`readability-string-compare <readability/string-compare>`, "Yes"
    :doc:`readability-suspicious-call-argument 
<readability/suspicious-call-argument>`,
+   :doc:`readability-trailing-comma <readability/trailing-comma>`, "Yes"
    :doc:`readability-uniqueptr-delete-release 
<readability/uniqueptr-delete-release>`, "Yes"
    :doc:`readability-uppercase-literal-suffix 
<readability/uppercase-literal-suffix>`, "Yes"
    :doc:`readability-use-anyofallof <readability/use-anyofallof>`,
diff --git 
a/clang-tools-extra/docs/clang-tidy/checks/readability/trailing-comma.rst 
b/clang-tools-extra/docs/clang-tidy/checks/readability/trailing-comma.rst
new file mode 100644
index 0000000000000..f1bd09d5d4e08
--- /dev/null
+++ b/clang-tools-extra/docs/clang-tidy/checks/readability/trailing-comma.rst
@@ -0,0 +1,83 @@
+.. title:: clang-tidy - readability-trailing-comma
+
+readability-trailing-comma
+==========================
+
+Checks for presence or absence of trailing commas in enum definitions and
+initializer lists.
+
+The check can either append trailing commas where they are missing or remove
+them where they are present, based on the configured policy.
+
+Trailing commas offer several benefits:
+
+- Adding or removing elements may only changes a single line, making diffs
+  smaller and easier to read.
+- Formatters may change code to a more desired style.
+- Code generators avoid for need special handling of the last element.
+
+.. code-block:: c++
+
+  // Without trailing commas - adding "Yellow" requires modifying the "Blue" 
line
+  enum Color {
+    Red,
+    Green,
+    Blue
+  };
+
+  // With trailing commas - adding "Yellow" is a clean, single-line change
+  enum Color {
+    Red,
+    Green,
+    Blue,
+  };
+
+
+Limitations
+-----------
+
+The check currently don't analyze code inside macros.
+
+
+Options
+-------
+
+.. option:: CommaPolicy
+
+  Controls whether to add or remove trailing commas.
+  Valid values are:
+
+  - `Append`: Add trailing commas where missing.
+  - `Remove`: Remove trailing commas where present.
+
+  Example with `CommaPolicy` set to `Append`:
+
+  .. code-block:: c++
+
+    enum Status {
+      OK,
+      Error     // warning: enum should have a trailing comma
+    };
+
+  Example with `CommaPolicy` set to `Remove`:
+
+  .. code-block:: c++
+
+    enum Status {
+      OK,
+      Error,    // warning: enum should not have a trailing comma
+    };
+
+  Default is `Append`.
+
+.. option:: EnumThreshold
+
+  The minimum number of enumerators required in an enum before the check
+  will warn. This applies to both `Append` and `Remove` policies.
+  Default is `1` (always check enums).
+
+.. option:: InitListThreshold
+
+  The minimum number of elements required in an initializer list before
+  the check will warn. This applies to both `Append` and `Remove` policies.
+  Default is `3`.
diff --git 
a/clang-tools-extra/test/clang-tidy/checkers/readability/trailing-comma-cxx20-remove.cpp
 
b/clang-tools-extra/test/clang-tidy/checkers/readability/trailing-comma-cxx20-remove.cpp
new file mode 100644
index 0000000000000..7d4a2b1cf3565
--- /dev/null
+++ 
b/clang-tools-extra/test/clang-tidy/checkers/readability/trailing-comma-cxx20-remove.cpp
@@ -0,0 +1,46 @@
+// RUN: %check_clang_tidy -std=c++20-or-later %s readability-trailing-comma %t 
-- \
+// RUN:   -config='{CheckOptions: {readability-trailing-comma.CommaPolicy: 
Remove, \
+// RUN:             readability-trailing-comma.InitListThreshold: 1}}'
+
+struct S { int x, y; };
+
+void f() {
+  S s1 = {.x = 1, .y = 2,};
+  // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: initializer list should not 
have a trailing comma
+  // CHECK-FIXES: S s1 = {.x = 1, .y = 2};
+
+  S s2 = {.x = 1,};
+  // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: initializer list should not 
have a trailing comma
+  // CHECK-FIXES: S s2 = {.x = 1};
+
+  int a[3] = {[0] = 1, [2] = 3,};
+  // CHECK-MESSAGES: :[[@LINE-1]]:31: warning: initializer list should not 
have a trailing comma
+  // CHECK-FIXES: int a[3] = {[0] = 1, [2] = 3};
+
+  // No warnings
+  S s3 = {.x = 1, .y = 2};
+}
+
+struct N { S a, b; };
+
+void nested() {
+  N n = {.a = {.x = 1, .y = 2,}, .b = {.x = 3, .y = 4,},};
+  // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: initializer list should not 
have a trailing comma
+  // CHECK-MESSAGES: :[[@LINE-2]]:54: warning: initializer list should not 
have a trailing comma
+  // CHECK-MESSAGES: :[[@LINE-3]]:56: warning: initializer list should not 
have a trailing comma
+
+  N n2 = {.a = {.x = 1, .y = 2}, .b = {.x = 3, .y = 4}};
+}
+
+struct WithArray {
+  int values[3];
+  int count;
+};
+
+void with_array() {
+  WithArray w1 = {.values = {1, 2, 3,}, .count = 3,};
+  // CHECK-MESSAGES: :[[@LINE-1]]:37: warning: initializer list should not 
have a trailing comma [readability-trailing-comma]
+  // CHECK-MESSAGES: :[[@LINE-2]]:51: warning: initializer list should not 
have a trailing comma [readability-trailing-comma]
+
+  WithArray w2 = {.values = {1, 2, 3}, .count = 3};
+}
diff --git 
a/clang-tools-extra/test/clang-tidy/checkers/readability/trailing-comma-cxx20.cpp
 
b/clang-tools-extra/test/clang-tidy/checkers/readability/trailing-comma-cxx20.cpp
new file mode 100644
index 0000000000000..a0b2ce08fce50
--- /dev/null
+++ 
b/clang-tools-extra/test/clang-tidy/checkers/readability/trailing-comma-cxx20.cpp
@@ -0,0 +1,46 @@
+// RUN: %check_clang_tidy -std=c++20-or-later %s readability-trailing-comma %t 
-- \
+// RUN:   -config='{CheckOptions: 
{readability-trailing-comma.InitListThreshold: 1}}'
+
+struct S { int x, y; };
+
+void f() {
+  S s1 = {.x = 1, .y = 2};
+  // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: initializer list should have a 
trailing comma
+  // CHECK-FIXES: S s1 = {.x = 1, .y = 2,};
+
+  S s2 = {.x = 1};
+  // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: initializer list should have a 
trailing comma
+  // CHECK-FIXES: S s2 = {.x = 1,};
+
+  int a[3] = {[0] = 1, [2] = 3};
+  // CHECK-MESSAGES: :[[@LINE-1]]:31: warning: initializer list should have a 
trailing comma
+  // CHECK-FIXES: int a[3] = {[0] = 1, [2] = 3,};
+
+  // No warnings
+  S s3 = {.x = 1, .y = 2,};
+}
+
+struct N { S a, b; };
+
+void nested() {
+  N n = {.a = {.x = 1, .y = 2}, .b = {.x = 3, .y = 4}};
+  // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: initializer list should have a 
trailing comma
+  // CHECK-MESSAGES: :[[@LINE-2]]:53: warning: initializer list should have a 
trailing comma
+  // CHECK-MESSAGES: :[[@LINE-3]]:54: warning: initializer list should have a 
trailing comma
+
+  // No warning
+  N n2 = {.a = {.x = 1, .y = 2,}, .b = {.x = 3, .y = 4,},};
+}
+
+struct WithArray {
+  int values[3];
+  int count;
+};
+
+void with_array() {
+  WithArray w1 = {.values = {1, 2, 3}, .count = 3};
+  // CHECK-MESSAGES: :[[@LINE-1]]:37: warning: initializer list should have a 
trailing comma [readability-trailing-comma]
+  // CHECK-MESSAGES: :[[@LINE-2]]:50: warning: initializer list should have a 
trailing comma [readability-trailing-comma]
+
+  WithArray w2 = {.values = {1, 2, 3,}, .count = 3,};
+}
diff --git 
a/clang-tools-extra/test/clang-tidy/checkers/readability/trailing-comma-remove.cpp
 
b/clang-tools-extra/test/clang-tidy/checkers/readability/trailing-comma-remove.cpp
new file mode 100644
index 0000000000000..501362aed17cd
--- /dev/null
+++ 
b/clang-tools-extra/test/clang-tidy/checkers/readability/trailing-comma-remove.cpp
@@ -0,0 +1,49 @@
+// RUN: %check_clang_tidy -std=c++11-or-later %s readability-trailing-comma %t 
-- \
+// RUN:   -config='{CheckOptions: {readability-trailing-comma.CommaPolicy: 
Remove, \
+// RUN:             readability-trailing-comma.InitListThreshold: 1}}'
+
+struct S { int x, y; };
+
+enum E1 { A, B, C, };
+// CHECK-MESSAGES: :[[@LINE-1]]:18: warning: enum should not have a trailing 
comma [readability-trailing-comma]
+// CHECK-FIXES: enum E1 { A, B, C };
+
+enum class E2 { X, Y, };
+// CHECK-MESSAGES: :[[@LINE-1]]:21: warning: enum should not have a trailing 
comma
+// CHECK-FIXES: enum class E2 { X, Y };
+
+enum E3 { V = 1, };
+// CHECK-MESSAGES: :[[@LINE-1]]:16: warning: enum should not have a trailing 
comma
+// CHECK-FIXES: enum E3 { V = 1 };
+
+enum E4 { P, Q };
+enum Empty {};
+
+void f() {
+  int a[] = {1, 2,};
+  // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: initializer list should not 
have a trailing comma
+  // CHECK-FIXES: int a[] = {1, 2};
+
+  S s = {1, 2,};
+  // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: initializer list should not 
have a trailing comma
+  // CHECK-FIXES: S s = {1, 2};
+
+  int b[] = {1,};
+  // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: initializer list should not 
have a trailing comma
+  // CHECK-FIXES: int b[] = {1};
+
+  int c[] = {1, 2};
+  S s2 = {1, 2};
+  int d[] = {};
+}
+
+struct N { S a, b; };
+
+void nested() {
+  N n = {{1, 2,}, {3, 4,},};
+  // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: initializer list should not 
have a trailing comma
+  // CHECK-MESSAGES: :[[@LINE-2]]:24: warning: initializer list should not 
have a trailing comma
+  // CHECK-MESSAGES: :[[@LINE-3]]:26: warning: initializer list should not 
have a trailing comma
+
+  N n2 = {{1, 2}, {3, 4}};
+}
diff --git 
a/clang-tools-extra/test/clang-tidy/checkers/readability/trailing-comma-threshold.cpp
 
b/clang-tools-extra/test/clang-tidy/checkers/readability/trailing-comma-threshold.cpp
new file mode 100644
index 0000000000000..3bb4256d6aa9f
--- /dev/null
+++ 
b/clang-tools-extra/test/clang-tidy/checkers/readability/trailing-comma-threshold.cpp
@@ -0,0 +1,20 @@
+// RUN: %check_clang_tidy -std=c++11-or-later %s readability-trailing-comma %t
+
+enum E0 {};
+
+enum E1 { A };
+// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: enum should have a trailing comma
+// CHECK-FIXES: enum E1 { A, };
+
+enum E2 { B, C };
+// CHECK-MESSAGES: :[[@LINE-1]]:15: warning: enum should have a trailing comma
+// CHECK-FIXES: enum E2 { B, C, };
+
+// Init lists: default InitListThreshold=3
+void f() {
+  int a[] = {1};       // No warning - only 1 element
+  int b[] = {1, 2};    // No warning - only 2 elements
+  int c[] = {1, 2, 3};
+  // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: initializer list should have a 
trailing comma
+  // CHECK-FIXES: int c[] = {1, 2, 3,};
+}
diff --git 
a/clang-tools-extra/test/clang-tidy/checkers/readability/trailing-comma.c 
b/clang-tools-extra/test/clang-tidy/checkers/readability/trailing-comma.c
new file mode 100644
index 0000000000000..61b02fa621bd2
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/readability/trailing-comma.c
@@ -0,0 +1,40 @@
+// RUN: %check_clang_tidy %s readability-trailing-comma %t -- \
+// RUN:   -config='{CheckOptions: 
{readability-trailing-comma.InitListThreshold: 1}}'
+
+enum Color { Red, Green, Blue };
+// CHECK-MESSAGES: :[[@LINE-1]]:30: warning: enum should have a trailing comma
+// CHECK-FIXES: enum Color { Red, Green, Blue, };
+
+enum SingleValue { Only };
+// CHECK-MESSAGES: :[[@LINE-1]]:24: warning: enum should have a trailing comma
+// CHECK-FIXES: enum SingleValue { Only, };
+
+int arr[] = {1, 2, 3};
+// CHECK-MESSAGES: :[[@LINE-1]]:21: warning: initializer list should have a 
trailing comma
+// CHECK-FIXES: int arr[] = {1, 2, 3,};
+
+struct Point {
+  int x;
+  int y;
+};
+
+struct Point p = {10, 20};
+// CHECK-MESSAGES: :[[@LINE-1]]:25: warning: initializer list should have a 
trailing comma
+// CHECK-FIXES: struct Point p = {10, 20,};
+
+struct Point p2 = {.x = 1, .y = 2};
+// CHECK-MESSAGES: :[[@LINE-1]]:34: warning: initializer list should have a 
trailing comma
+// CHECK-FIXES: struct Point p2 = {.x = 1, .y = 2,};
+
+struct Point p3 = {.x = 5, 10};
+// CHECK-MESSAGES: :[[@LINE-1]]:30: warning: initializer list should have a 
trailing comma
+// CHECK-FIXES: struct Point p3 = {.x = 5, 10,};
+
+int arr2[5] = {[0] = 1, [4] = 5};
+// CHECK-MESSAGES: :[[@LINE-1]]:32: warning: initializer list should have a 
trailing comma
+// CHECK-FIXES: int arr2[5] = {[0] = 1, [4] = 5,};
+
+int matrix[2][2] = {{1, 2}, {3, 4}};
+// CHECK-MESSAGES: :[[@LINE-1]]:26: warning: initializer list should have a 
trailing comma
+// CHECK-MESSAGES: :[[@LINE-2]]:34: warning: initializer list should have a 
trailing comma
+// CHECK-MESSAGES: :[[@LINE-3]]:35: warning: initializer list should have a 
trailing comma
diff --git 
a/clang-tools-extra/test/clang-tidy/checkers/readability/trailing-comma.cpp 
b/clang-tools-extra/test/clang-tidy/checkers/readability/trailing-comma.cpp
new file mode 100644
index 0000000000000..942d6964da9c1
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/readability/trailing-comma.cpp
@@ -0,0 +1,76 @@
+// RUN: %check_clang_tidy -std=c++11-or-later %s readability-trailing-comma %t 
-- \
+// RUN:   -config='{CheckOptions: 
{readability-trailing-comma.InitListThreshold: 1}}'
+
+struct S { int x, y; };
+
+enum E1 { A, B, C };
+// CHECK-MESSAGES: :[[@LINE-1]]:18: warning: enum should have a trailing comma 
[readability-trailing-comma]
+// CHECK-FIXES: enum E1 { A, B, C, };
+
+enum class E2 { X, Y };
+// CHECK-MESSAGES: :[[@LINE-1]]:21: warning: enum should have a trailing comma
+// CHECK-FIXES: enum class E2 { X, Y, };
+
+enum E3 { V = 1 };
+// CHECK-MESSAGES: :[[@LINE-1]]:16: warning: enum should have a trailing comma
+// CHECK-FIXES: enum E3 { V = 1, };
+
+// No warnings - already have trailing commas or empty
+enum E4 { P, Q, };
+enum Empty {};
+
+void f() {
+  int a[] = {1, 2};
+  // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: initializer list should have a 
trailing comma
+  // CHECK-FIXES: int a[] = {1, 2,};
+
+  S s = {1, 2};
+  // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: initializer list should have a 
trailing comma
+  // CHECK-FIXES: S s = {1, 2,};
+
+  int b[] = {1};
+  // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: initializer list should have a 
trailing comma
+  // CHECK-FIXES: int b[] = {1,};
+
+  int c[] = {1, 2,};
+  S s2 = {1, 2,};
+  int d[] = {};
+}
+
+struct N { S a, b; };
+
+void nested() {
+  N n = {{1, 2}, {3, 4}};
+  // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: initializer list should have a 
trailing comma
+  // CHECK-MESSAGES: :[[@LINE-2]]:23: warning: initializer list should have a 
trailing comma
+  // CHECK-MESSAGES: :[[@LINE-3]]:24: warning: initializer list should have a 
trailing comma
+
+  N n2 = {{1, 2,}, {3, 4,},};
+}
+
+#define ENUM(n, a, b) enum n { a, b }
+#define INIT {1, 2}
+
+ENUM(E1M, X, Y);
+int macroArr[] = INIT;
+
+enum E2M { Am, Bm };
+// CHECK-MESSAGES: :[[@LINE-1]]:18: warning: enum should have a trailing comma
+// CHECK-FIXES: enum E2M { Am, Bm, };
+
+template <typename T, typename... Ts>
+struct Pack {
+  int values[sizeof...(Ts) + 1] = {sizeof(T), sizeof(Ts)...};
+};
+
+Pack<int> single;
+Pack<int, double> two;
+Pack<int, double, char> three;
+
+template <typename... Ts>
+struct PackSingle {
+  int values[sizeof...(Ts)] = {sizeof(Ts)...};
+};
+
+PackSingle<int> p1;
+PackSingle<int, double, char> p3;

>From 1cacab7ffd4c0a390b41fb05bfed509ca294037f Mon Sep 17 00:00:00 2001
From: Victor Baranov <[email protected]>
Date: Fri, 2 Jan 2026 22:23:20 +0300
Subject: [PATCH 2/4] count lines instead of init-expr

---
 .../readability/TrailingCommaCheck.cpp        |  42 +++----
 .../readability/TrailingCommaCheck.h          |   2 -
 .../trailing-comma-cxx20-remove.cpp           |  64 ++++++----
 .../readability/trailing-comma-cxx20.cpp      |  89 ++++++++++----
 .../readability/trailing-comma-remove.cpp     |  86 ++++++++-----
 .../readability/trailing-comma-threshold.cpp  |  20 ---
 .../checkers/readability/trailing-comma.c     |  81 ++++++++----
 .../checkers/readability/trailing-comma.cpp   | 115 ++++++++++++------
 8 files changed, 315 insertions(+), 184 deletions(-)
 delete mode 100644 
clang-tools-extra/test/clang-tidy/checkers/readability/trailing-comma-threshold.cpp

diff --git a/clang-tools-extra/clang-tidy/readability/TrailingCommaCheck.cpp 
b/clang-tools-extra/clang-tidy/readability/TrailingCommaCheck.cpp
index 6552e6d540550..5af89698c39f8 100644
--- a/clang-tools-extra/clang-tidy/readability/TrailingCommaCheck.cpp
+++ b/clang-tools-extra/clang-tidy/readability/TrailingCommaCheck.cpp
@@ -37,6 +37,11 @@ struct 
OptionEnumMapping<readability::TrailingCommaCheck::CommaPolicyKind> {
 
 namespace clang::tidy::readability {
 
+static bool isSingleLine(SourceLocation Begin, SourceLocation End,
+                         const SourceManager &SM) {
+  return SM.getExpansionLineNumber(Begin) == SM.getExpansionLineNumber(End);
+}
+
 namespace {
 
 AST_POLYMORPHIC_MATCHER(isMacro,
@@ -54,14 +59,10 @@ AST_MATCHER(InitListExpr, isEmptyInitList) { return 
Node.getNumInits() == 0; }
 TrailingCommaCheck::TrailingCommaCheck(StringRef Name,
                                        ClangTidyContext *Context)
     : ClangTidyCheck(Name, Context),
-      CommaPolicy(Options.get("CommaPolicy", CommaPolicyKind::Append)),
-      EnumThreshold(Options.get("EnumThreshold", 1U)),
-      InitListThreshold(Options.get("InitListThreshold", 3U)) {}
+      CommaPolicy(Options.get("CommaPolicy", CommaPolicyKind::Append)) {}
 
 void TrailingCommaCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
   Options.store(Opts, "CommaPolicy", CommaPolicy);
-  Options.store(Opts, "EnumThreshold", EnumThreshold);
-  Options.store(Opts, "InitListThreshold", InitListThreshold);
 }
 
 void TrailingCommaCheck::registerMatchers(MatchFinder *Finder) {
@@ -87,18 +88,15 @@ void TrailingCommaCheck::check(const 
MatchFinder::MatchResult &Result) {
 
 void TrailingCommaCheck::checkEnumDecl(const EnumDecl *Enum,
                                        const MatchFinder::MatchResult &Result) 
{
-  // Count enumerators and get the last one
-  unsigned NumEnumerators = 0;
+  if (isSingleLine(Enum->getBeginLoc(), Enum->getEndLoc(),
+                   *Result.SourceManager) &&
+      CommaPolicy == CommaPolicyKind::Append)
+    return;
+
   const EnumConstantDecl *LastEnumerator = nullptr;
-  for (const EnumConstantDecl *ECD : Enum->enumerators()) {
+  for (const EnumConstantDecl *ECD : Enum->enumerators())
     LastEnumerator = ECD;
-    ++NumEnumerators;
-  }
   assert(LastEnumerator);
-  assert(NumEnumerators > 0);
-
-  if (NumEnumerators < EnumThreshold)
-    return;
 
   SourceLocation LastEnumLoc;
   if (const Expr *Init = LastEnumerator->getInitExpr())
@@ -114,15 +112,17 @@ void TrailingCommaCheck::checkEnumDecl(const EnumDecl 
*Enum,
 
 void TrailingCommaCheck::checkInitListExpr(
     const InitListExpr *InitList, const MatchFinder::MatchResult &Result) {
-  // We need to use the syntactic form for correct source locations.
-  if (InitList->isSemanticForm())
-    if (const InitListExpr *SyntacticForm = InitList->getSyntacticForm())
-      InitList = SyntacticForm;
-
-  const unsigned NumInits = InitList->getNumInits();
-  if (NumInits < InitListThreshold)
+  // We need to use non-empty syntactic form for correct source locations.
+  if (const InitListExpr *SynInitInitList = InitList->getSyntacticForm();
+      SynInitInitList && SynInitInitList->getNumInits() > 0)
+    InitList = SynInitInitList;
+
+  if (isSingleLine(InitList->getBeginLoc(), InitList->getEndLoc(),
+                   *Result.SourceManager) &&
+      CommaPolicy == CommaPolicyKind::Append)
     return;
 
+  const unsigned NumInits = InitList->getNumInits();
   const Expr *LastInit = InitList->getInit(NumInits - 1);
   assert(LastInit);
 
diff --git a/clang-tools-extra/clang-tidy/readability/TrailingCommaCheck.h 
b/clang-tools-extra/clang-tidy/readability/TrailingCommaCheck.h
index 789e4a6d30df4..0b91a05fc14e1 100644
--- a/clang-tools-extra/clang-tidy/readability/TrailingCommaCheck.h
+++ b/clang-tools-extra/clang-tidy/readability/TrailingCommaCheck.h
@@ -35,8 +35,6 @@ class TrailingCommaCheck : public ClangTidyCheck {
 
 private:
   const CommaPolicyKind CommaPolicy;
-  const unsigned EnumThreshold;
-  const unsigned InitListThreshold;
 
   void checkEnumDecl(const EnumDecl *Enum,
                      const ast_matchers::MatchFinder::MatchResult &Result);
diff --git 
a/clang-tools-extra/test/clang-tidy/checkers/readability/trailing-comma-cxx20-remove.cpp
 
b/clang-tools-extra/test/clang-tidy/checkers/readability/trailing-comma-cxx20-remove.cpp
index 7d4a2b1cf3565..78bc8d96cbf06 100644
--- 
a/clang-tools-extra/test/clang-tidy/checkers/readability/trailing-comma-cxx20-remove.cpp
+++ 
b/clang-tools-extra/test/clang-tidy/checkers/readability/trailing-comma-cxx20-remove.cpp
@@ -1,35 +1,49 @@
 // RUN: %check_clang_tidy -std=c++20-or-later %s readability-trailing-comma %t 
-- \
-// RUN:   -config='{CheckOptions: {readability-trailing-comma.CommaPolicy: 
Remove, \
-// RUN:             readability-trailing-comma.InitListThreshold: 1}}'
+// RUN:   -config='{CheckOptions: {readability-trailing-comma.CommaPolicy: 
Remove}}'
 
 struct S { int x, y; };
 
 void f() {
-  S s1 = {.x = 1, .y = 2,};
-  // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: initializer list should not 
have a trailing comma
-  // CHECK-FIXES: S s1 = {.x = 1, .y = 2};
+  S s1 = {
+    .x = 1,
+    .y = 2,
+  };
+  // CHECK-MESSAGES: :[[@LINE-2]]:11: warning: initializer list should not 
have a trailing comma
+  // CHECK-FIXES: S s1 = {
+  // CHECK-FIXES-NEXT:     .x = 1,
+  // CHECK-FIXES-NEXT:     .y = 2
+  // CHECK-FIXES-NEXT:   };
 
-  S s2 = {.x = 1,};
-  // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: initializer list should not 
have a trailing comma
-  // CHECK-FIXES: S s2 = {.x = 1};
+  int a[3] = {
+    [0] = 1,
+  };
+  // CHECK-MESSAGES: :[[@LINE-2]]:12: warning: initializer list should not 
have a trailing comma
+  // CHECK-FIXES: int a[3] = {
+  // CHECK-FIXES-NEXT:     [0] = 1
+  // CHECK-FIXES-NEXT:   };
 
-  int a[3] = {[0] = 1, [2] = 3,};
-  // CHECK-MESSAGES: :[[@LINE-1]]:31: warning: initializer list should not 
have a trailing comma
-  // CHECK-FIXES: int a[3] = {[0] = 1, [2] = 3};
+  S s2 = {.x = 1, .y = 2,};
+  // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: initializer list should not 
have a trailing comma
+  // CHECK-FIXES: S s2 = {.x = 1, .y = 2};
 
-  // No warnings
-  S s3 = {.x = 1, .y = 2};
+  S s3 = {
+    .x = 1,
+    .y = 2
+  };
 }
 
 struct N { S a, b; };
 
-void nested() {
-  N n = {.a = {.x = 1, .y = 2,}, .b = {.x = 3, .y = 4,},};
-  // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: initializer list should not 
have a trailing comma
-  // CHECK-MESSAGES: :[[@LINE-2]]:54: warning: initializer list should not 
have a trailing comma
-  // CHECK-MESSAGES: :[[@LINE-3]]:56: warning: initializer list should not 
have a trailing comma
+void nested() { 
+  N n1 = {.a = {.x = 1, .y = 2}, .b = {.x = 3, .y = 4,},};
+  // CHECK-MESSAGES: :[[@LINE-1]]:54: warning: initializer list should not 
have a trailing comma
+  // CHECK-MESSAGES: :[[@LINE-2]]:56: warning: initializer list should not 
have a trailing comma
+  // CHECK-FIXES: N n1 = {.a = {.x = 1, .y = 2}, .b = {.x = 3, .y = 4}};
 
-  N n2 = {.a = {.x = 1, .y = 2}, .b = {.x = 3, .y = 4}};
+  N n2 = {
+    .a = {.x = 1, .y = 2},
+    .b = {.x = 3, .y = 4}
+  };
 }
 
 struct WithArray {
@@ -38,9 +52,13 @@ struct WithArray {
 };
 
 void with_array() {
-  WithArray w1 = {.values = {1, 2, 3,}, .count = 3,};
-  // CHECK-MESSAGES: :[[@LINE-1]]:37: warning: initializer list should not 
have a trailing comma [readability-trailing-comma]
-  // CHECK-MESSAGES: :[[@LINE-2]]:51: warning: initializer list should not 
have a trailing comma [readability-trailing-comma]
+  WithArray w2 = {.values = {1, 2, 3,}, .count = 3,};
+  // CHECK-MESSAGES: :[[@LINE-1]]:37: warning: initializer list should not 
have a trailing comma
+  // CHECK-MESSAGES: :[[@LINE-2]]:51: warning: initializer list should not 
have a trailing comma
+  // CHECK-FIXES: WithArray w2 = {.values = {1, 2, 3}, .count = 3};
 
-  WithArray w2 = {.values = {1, 2, 3}, .count = 3};
+  WithArray w3 = {
+    .values = {1, 2, 3},
+    .count = 3
+  };
 }
diff --git 
a/clang-tools-extra/test/clang-tidy/checkers/readability/trailing-comma-cxx20.cpp
 
b/clang-tools-extra/test/clang-tidy/checkers/readability/trailing-comma-cxx20.cpp
index a0b2ce08fce50..b2f6ed072563f 100644
--- 
a/clang-tools-extra/test/clang-tidy/checkers/readability/trailing-comma-cxx20.cpp
+++ 
b/clang-tools-extra/test/clang-tidy/checkers/readability/trailing-comma-cxx20.cpp
@@ -1,35 +1,63 @@
-// RUN: %check_clang_tidy -std=c++20-or-later %s readability-trailing-comma %t 
-- \
-// RUN:   -config='{CheckOptions: 
{readability-trailing-comma.InitListThreshold: 1}}'
+// RUN: %check_clang_tidy -std=c++20-or-later %s readability-trailing-comma %t
 
 struct S { int x, y; };
 
 void f() {
-  S s1 = {.x = 1, .y = 2};
-  // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: initializer list should have a 
trailing comma
-  // CHECK-FIXES: S s1 = {.x = 1, .y = 2,};
+  S s1 = {
+    .x = 1,
+    .y = 2
+  };
+  // CHECK-MESSAGES: :[[@LINE-2]]:11: warning: initializer list should have a 
trailing comma
+  // CHECK-FIXES: S s1 = {
+  // CHECK-FIXES-NEXT:     .x = 1,
+  // CHECK-FIXES-NEXT:     .y = 2,
+  // CHECK-FIXES-NEXT:   };
 
-  S s2 = {.x = 1};
-  // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: initializer list should have a 
trailing comma
-  // CHECK-FIXES: S s2 = {.x = 1,};
+  int a[3] = {
+    [0] = 1
+  };
+  // CHECK-MESSAGES: :[[@LINE-2]]:12: warning: initializer list should have a 
trailing comma
+  // CHECK-FIXES: int a[3] = {
+  // CHECK-FIXES-NEXT:     [0] = 1,
+  // CHECK-FIXES-NEXT:   };
 
-  int a[3] = {[0] = 1, [2] = 3};
-  // CHECK-MESSAGES: :[[@LINE-1]]:31: warning: initializer list should have a 
trailing comma
-  // CHECK-FIXES: int a[3] = {[0] = 1, [2] = 3,};
+  S s2 = {.x = 1, .y = 2};
+  S s3 = {.x = 1};
 
-  // No warnings
-  S s3 = {.x = 1, .y = 2,};
+  S s4 = {
+    .x = 1,
+  };
 }
 
 struct N { S a, b; };
 
 void nested() {
-  N n = {.a = {.x = 1, .y = 2}, .b = {.x = 3, .y = 4}};
-  // CHECK-MESSAGES: :[[@LINE-1]]:30: warning: initializer list should have a 
trailing comma
-  // CHECK-MESSAGES: :[[@LINE-2]]:53: warning: initializer list should have a 
trailing comma
-  // CHECK-MESSAGES: :[[@LINE-3]]:54: warning: initializer list should have a 
trailing comma
+  N n = {
+    .a = {.x = 1, .y = 2},
+    .b = {
+      .x = 3,
+      .y = 4
+    }
+  };
+  // CHECK-MESSAGES: :[[@LINE-3]]:13: warning: initializer list should have a 
trailing comma
+  // CHECK-MESSAGES: :[[@LINE-3]]:6: warning: initializer list should have a 
trailing comma
+  // CHECK-FIXES: N n = {
+  // CHECK-FIXES-NEXT:    .a = {.x = 1, .y = 2},
+  // CHECK-FIXES-NEXT:    .b = {
+  // CHECK-FIXES-NEXT:      .x = 3,
+  // CHECK-FIXES-NEXT:      .y = 4,
+  // CHECK-FIXES-NEXT:    },
+  // CHECK-FIXES-NEXT:   };
 
-  // No warning
-  N n2 = {.a = {.x = 1, .y = 2,}, .b = {.x = 3, .y = 4,},};
+  N n2 = {.a = {.x = 1, .y = 2}, .b = {.x = 3, .y = 4}};
+
+  N n3 = {
+    .a = {.x = 1, .y = 2},
+    .b = {
+      .x = 3,
+      .y = 4,
+    },
+  };
 }
 
 struct WithArray {
@@ -38,9 +66,24 @@ struct WithArray {
 };
 
 void with_array() {
-  WithArray w1 = {.values = {1, 2, 3}, .count = 3};
-  // CHECK-MESSAGES: :[[@LINE-1]]:37: warning: initializer list should have a 
trailing comma [readability-trailing-comma]
-  // CHECK-MESSAGES: :[[@LINE-2]]:50: warning: initializer list should have a 
trailing comma [readability-trailing-comma]
+  WithArray w1 = {
+    .values = {1, 2,
+      3
+    },
+    .count = 3
+  };
+  // CHECK-MESSAGES: :[[@LINE-4]]:8: warning: initializer list should have a 
trailing comma
+  // CHECK-MESSAGES: :[[@LINE-3]]:15: warning: initializer list should have a 
trailing comma
+  // CHECK-FIXES: WithArray w1 = {
+  // CHECK-FIXES-NEXT:    .values = {1, 2,
+  // CHECK-FIXES-NEXT:      3,
+  // CHECK-FIXES-NEXT:    },
+  // CHECK-FIXES-NEXT:    .count = 3,
+  // CHECK-FIXES-NEXT:   };
 
-  WithArray w2 = {.values = {1, 2, 3,}, .count = 3,};
+  WithArray w2 = {.values = {1, 2, 3}, .count = 3};
+  WithArray w3 = {
+    .values = {1, 2, 3},
+    .count = 3,
+  };
 }
diff --git 
a/clang-tools-extra/test/clang-tidy/checkers/readability/trailing-comma-remove.cpp
 
b/clang-tools-extra/test/clang-tidy/checkers/readability/trailing-comma-remove.cpp
index 501362aed17cd..b74daeee090ad 100644
--- 
a/clang-tools-extra/test/clang-tidy/checkers/readability/trailing-comma-remove.cpp
+++ 
b/clang-tools-extra/test/clang-tidy/checkers/readability/trailing-comma-remove.cpp
@@ -1,49 +1,79 @@
 // RUN: %check_clang_tidy -std=c++11-or-later %s readability-trailing-comma %t 
-- \
-// RUN:   -config='{CheckOptions: {readability-trailing-comma.CommaPolicy: 
Remove, \
-// RUN:             readability-trailing-comma.InitListThreshold: 1}}'
+// RUN:   -config='{CheckOptions: {readability-trailing-comma.CommaPolicy: 
Remove}}'
 
 struct S { int x, y; };
 
-enum E1 { A, B, C, };
-// CHECK-MESSAGES: :[[@LINE-1]]:18: warning: enum should not have a trailing 
comma [readability-trailing-comma]
-// CHECK-FIXES: enum E1 { A, B, C };
+enum E1 {
+  A,
+  B,
+  C,
+};
+// CHECK-MESSAGES: :[[@LINE-2]]:4: warning: enum should not have a trailing 
comma [readability-trailing-comma]
+// CHECK-FIXES: enum E1 {
+// CHECK-FIXES-NEXT:   A,
+// CHECK-FIXES-NEXT:   B,
+// CHECK-FIXES-NEXT:   C
+// CHECK-FIXES-NEXT: };
 
-enum class E2 { X, Y, };
-// CHECK-MESSAGES: :[[@LINE-1]]:21: warning: enum should not have a trailing 
comma
-// CHECK-FIXES: enum class E2 { X, Y };
+enum E2 {
+  V = 1,
+};
+// CHECK-MESSAGES: :[[@LINE-2]]:8: warning: enum should not have a trailing 
comma
+// CHECK-FIXES: enum E2 {
+// CHECK-FIXES-NEXT:   V = 1
+// CHECK-FIXES-NEXT: };
 
-enum E3 { V = 1, };
-// CHECK-MESSAGES: :[[@LINE-1]]:16: warning: enum should not have a trailing 
comma
-// CHECK-FIXES: enum E3 { V = 1 };
+enum SingleLine { A1, B1, C1, };
+// CHECK-MESSAGES: :[[@LINE-1]]:29: warning: enum should not have a trailing 
comma
+// CHECK-FIXES: enum SingleLine { A1, B1, C1 };
 
-enum E4 { P, Q };
+enum E3 {
+  P,
+  Q
+};
 enum Empty {};
 
 void f() {
-  int a[] = {1, 2,};
-  // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: initializer list should not 
have a trailing comma
-  // CHECK-FIXES: int a[] = {1, 2};
+  // Multi-line init lists with trailing commas - should warn to remove
+  int a[] = {
+    1,
+    2,
+  };
+  // CHECK-MESSAGES: :[[@LINE-2]]:6: warning: initializer list should not have 
a trailing comma
+  // CHECK-FIXES: int a[] = {
+  // CHECK-FIXES-NEXT:     1,
+  // CHECK-FIXES-NEXT:     2
+  // CHECK-FIXES-NEXT:   };
 
-  S s = {1, 2,};
-  // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: initializer list should not 
have a trailing comma
-  // CHECK-FIXES: S s = {1, 2};
+  S s = {
+    1,
+    2,
+  };
+  // CHECK-MESSAGES: :[[@LINE-2]]:6: warning: initializer list should not have 
a trailing comma
+  // CHECK-FIXES: S s = {
+  // CHECK-FIXES-NEXT:     1,
+  // CHECK-FIXES-NEXT:     2
+  // CHECK-FIXES-NEXT:   };
 
-  int b[] = {1,};
+  int b[] = {1, 2, 3,};
+  // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: initializer list should not 
have a trailing comma
+  // CHECK-FIXES: int b[] = {1, 2, 3};
+  S s2 = {1, 2,};
   // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: initializer list should not 
have a trailing comma
-  // CHECK-FIXES: int b[] = {1};
+  // CHECK-FIXES: S s2 = {1, 2};
 
-  int c[] = {1, 2};
-  S s2 = {1, 2};
+  int c[] = {
+    1,
+    2
+  };
   int d[] = {};
 }
 
 struct N { S a, b; };
-
 void nested() {
-  N n = {{1, 2,}, {3, 4,},};
+  N n = {{3, 4,},};
   // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: initializer list should not 
have a trailing comma
-  // CHECK-MESSAGES: :[[@LINE-2]]:24: warning: initializer list should not 
have a trailing comma
-  // CHECK-MESSAGES: :[[@LINE-3]]:26: warning: initializer list should not 
have a trailing comma
-
-  N n2 = {{1, 2}, {3, 4}};
+  // CHECK-MESSAGES: :[[@LINE-2]]:17: warning: initializer list should not 
have a trailing comma
+  // CHECK-FIXES: N n = {{[{][{]3, 4[}][}]}};
+  N n2 = {{3, 4}};
 }
diff --git 
a/clang-tools-extra/test/clang-tidy/checkers/readability/trailing-comma-threshold.cpp
 
b/clang-tools-extra/test/clang-tidy/checkers/readability/trailing-comma-threshold.cpp
deleted file mode 100644
index 3bb4256d6aa9f..0000000000000
--- 
a/clang-tools-extra/test/clang-tidy/checkers/readability/trailing-comma-threshold.cpp
+++ /dev/null
@@ -1,20 +0,0 @@
-// RUN: %check_clang_tidy -std=c++11-or-later %s readability-trailing-comma %t
-
-enum E0 {};
-
-enum E1 { A };
-// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: enum should have a trailing comma
-// CHECK-FIXES: enum E1 { A, };
-
-enum E2 { B, C };
-// CHECK-MESSAGES: :[[@LINE-1]]:15: warning: enum should have a trailing comma
-// CHECK-FIXES: enum E2 { B, C, };
-
-// Init lists: default InitListThreshold=3
-void f() {
-  int a[] = {1};       // No warning - only 1 element
-  int b[] = {1, 2};    // No warning - only 2 elements
-  int c[] = {1, 2, 3};
-  // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: initializer list should have a 
trailing comma
-  // CHECK-FIXES: int c[] = {1, 2, 3,};
-}
diff --git 
a/clang-tools-extra/test/clang-tidy/checkers/readability/trailing-comma.c 
b/clang-tools-extra/test/clang-tidy/checkers/readability/trailing-comma.c
index 61b02fa621bd2..d416f3cc03384 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/readability/trailing-comma.c
+++ b/clang-tools-extra/test/clang-tidy/checkers/readability/trailing-comma.c
@@ -1,40 +1,67 @@
-// RUN: %check_clang_tidy %s readability-trailing-comma %t -- \
-// RUN:   -config='{CheckOptions: 
{readability-trailing-comma.InitListThreshold: 1}}'
+// RUN: %check_clang_tidy %s readability-trailing-comma %t
 
-enum Color { Red, Green, Blue };
-// CHECK-MESSAGES: :[[@LINE-1]]:30: warning: enum should have a trailing comma
-// CHECK-FIXES: enum Color { Red, Green, Blue, };
-
-enum SingleValue { Only };
-// CHECK-MESSAGES: :[[@LINE-1]]:24: warning: enum should have a trailing comma
-// CHECK-FIXES: enum SingleValue { Only, };
+enum Color {
+  Red,
+  Green,
+  Blue
+};
+// CHECK-MESSAGES: :[[@LINE-2]]:7: warning: enum should have a trailing comma
+// CHECK-FIXES: enum Color {
+// CHECK-FIXES-NEXT:   Red,
+// CHECK-FIXES-NEXT:   Green,
+// CHECK-FIXES-NEXT:   Blue,
+// CHECK-FIXES-NEXT: };
 
-int arr[] = {1, 2, 3};
-// CHECK-MESSAGES: :[[@LINE-1]]:21: warning: initializer list should have a 
trailing comma
-// CHECK-FIXES: int arr[] = {1, 2, 3,};
+enum SingleLine { A, B, C };
+enum SingleLine2 { X1, Y1, };
 
 struct Point {
   int x;
   int y;
 };
 
-struct Point p = {10, 20};
-// CHECK-MESSAGES: :[[@LINE-1]]:25: warning: initializer list should have a 
trailing comma
-// CHECK-FIXES: struct Point p = {10, 20,};
+struct Point p = {
+  10,
+  20
+};
+// CHECK-MESSAGES: :[[@LINE-2]]:5: warning: initializer list should have a 
trailing comma
+// CHECK-FIXES: struct Point p = {
+// CHECK-FIXES-NEXT:   10,
+// CHECK-FIXES-NEXT:   20,
+// CHECK-FIXES-NEXT: };
 
-struct Point p2 = {.x = 1, .y = 2};
-// CHECK-MESSAGES: :[[@LINE-1]]:34: warning: initializer list should have a 
trailing comma
-// CHECK-FIXES: struct Point p2 = {.x = 1, .y = 2,};
+struct Point p2 = {
+  .x = 1,
+  .y = 2
+};
+// CHECK-MESSAGES: :[[@LINE-2]]:9: warning: initializer list should have a 
trailing comma
+// CHECK-FIXES: struct Point p2 = {
+// CHECK-FIXES-NEXT:   .x = 1,
+// CHECK-FIXES-NEXT:   .y = 2,
+// CHECK-FIXES-NEXT: };
 
-struct Point p3 = {.x = 5, 10};
-// CHECK-MESSAGES: :[[@LINE-1]]:30: warning: initializer list should have a 
trailing comma
-// CHECK-FIXES: struct Point p3 = {.x = 5, 10,};
+int arr2[5] = {
+  [0] = 1
+};
+// CHECK-MESSAGES: :[[@LINE-2]]:10: warning: initializer list should have a 
trailing comma
+// CHECK-FIXES: int arr2[5] = {
+// CHECK-FIXES-NEXT:   [0] = 1,
+// CHECK-FIXES-NEXT: };
 
-int arr2[5] = {[0] = 1, [4] = 5};
-// CHECK-MESSAGES: :[[@LINE-1]]:32: warning: initializer list should have a 
trailing comma
-// CHECK-FIXES: int arr2[5] = {[0] = 1, [4] = 5,};
+int multiArr[] = {
+  1
+};
+// CHECK-MESSAGES: :[[@LINE-2]]:4: warning: initializer list should have a 
trailing comma
+// CHECK-FIXES: int multiArr[] = {
+// CHECK-FIXES-NEXT:   1,
+// CHECK-FIXES-NEXT: };
 
+int arr[] = {1, 2, 3};
+struct Point p3 = {10, 20};
+struct Point p4 = {.x = 1, .y = 2};
 int matrix[2][2] = {{1, 2}, {3, 4}};
-// CHECK-MESSAGES: :[[@LINE-1]]:26: warning: initializer list should have a 
trailing comma
-// CHECK-MESSAGES: :[[@LINE-2]]:34: warning: initializer list should have a 
trailing comma
-// CHECK-MESSAGES: :[[@LINE-3]]:35: warning: initializer list should have a 
trailing comma
+
+enum WithComma {
+  X,
+  Y,
+};
diff --git 
a/clang-tools-extra/test/clang-tidy/checkers/readability/trailing-comma.cpp 
b/clang-tools-extra/test/clang-tidy/checkers/readability/trailing-comma.cpp
index 942d6964da9c1..11cdbd23babcf 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/readability/trailing-comma.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/readability/trailing-comma.cpp
@@ -1,63 +1,98 @@
-// RUN: %check_clang_tidy -std=c++11-or-later %s readability-trailing-comma %t 
-- \
-// RUN:   -config='{CheckOptions: 
{readability-trailing-comma.InitListThreshold: 1}}'
+// RUN: %check_clang_tidy -std=c++11-or-later %s readability-trailing-comma %t
 
 struct S { int x, y; };
 
-enum E1 { A, B, C };
-// CHECK-MESSAGES: :[[@LINE-1]]:18: warning: enum should have a trailing comma 
[readability-trailing-comma]
-// CHECK-FIXES: enum E1 { A, B, C, };
-
-enum class E2 { X, Y };
-// CHECK-MESSAGES: :[[@LINE-1]]:21: warning: enum should have a trailing comma
-// CHECK-FIXES: enum class E2 { X, Y, };
-
-enum E3 { V = 1 };
-// CHECK-MESSAGES: :[[@LINE-1]]:16: warning: enum should have a trailing comma
-// CHECK-FIXES: enum E3 { V = 1, };
-
-// No warnings - already have trailing commas or empty
-enum E4 { P, Q, };
+enum E1 {
+  A,
+  B,
+  C
+};
+// CHECK-MESSAGES: :[[@LINE-2]]:4: warning: enum should have a trailing comma 
[readability-trailing-comma]
+// CHECK-FIXES: enum E1 {
+// CHECK-FIXES-NEXT:   A,
+// CHECK-FIXES-NEXT:   B,
+// CHECK-FIXES-NEXT:   C,
+// CHECK-FIXES-NEXT: };
+
+enum E2 {
+  V = 1
+};
+// CHECK-MESSAGES: :[[@LINE-2]]:8: warning: enum should have a trailing comma
+// CHECK-FIXES: enum E2 {
+// CHECK-FIXES-NEXT:   V = 1,
+// CHECK-FIXES-NEXT: };
+
+// Single-line enums - no warnings
+enum SingleLine { A1, B1, C1 };
+enum class SingleLine2 { X1, Y1, };
+
+enum E3 {
+  P,
+  Q,
+};
 enum Empty {};
 
 void f() {
-  int a[] = {1, 2};
-  // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: initializer list should have a 
trailing comma
-  // CHECK-FIXES: int a[] = {1, 2,};
-
-  S s = {1, 2};
-  // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: initializer list should have a 
trailing comma
-  // CHECK-FIXES: S s = {1, 2,};
-
-  int b[] = {1};
-  // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: initializer list should have a 
trailing comma
-  // CHECK-FIXES: int b[] = {1,};
-
-  int c[] = {1, 2,};
-  S s2 = {1, 2,};
+  int a[] = {
+    1
+  };
+  // CHECK-MESSAGES: :[[@LINE-2]]:6: warning: initializer list should have a 
trailing comma
+  // CHECK-FIXES: int a[] = {
+  // CHECK-FIXES-NEXT:     1,
+  // CHECK-FIXES-NEXT:   };
+
+  S s = {
+    1,
+    2
+  };
+  // CHECK-MESSAGES: :[[@LINE-2]]:6: warning: initializer list should have a 
trailing comma
+  // CHECK-FIXES: S s = {
+  // CHECK-FIXES-NEXT:     1,
+  // CHECK-FIXES-NEXT:     2,
+  // CHECK-FIXES-NEXT:   };
+
+  // Single-line init lists - no warnings
+  int b[] = {1, 2, 3};
+  S s2 = {1, 2};
+
+  int c[] = {
+    1,
+    2,
+  };
   int d[] = {};
 }
 
 struct N { S a, b; };
-
 void nested() {
   N n = {{1, 2}, {3, 4}};
-  // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: initializer list should have a 
trailing comma
-  // CHECK-MESSAGES: :[[@LINE-2]]:23: warning: initializer list should have a 
trailing comma
-  // CHECK-MESSAGES: :[[@LINE-3]]:24: warning: initializer list should have a 
trailing comma
-
   N n2 = {{1, 2,}, {3, 4,},};
 }
 
+void nestedMultiLine() {
+  N n = {
+    {1, 2},
+    {3, 4}
+  };
+  // CHECK-MESSAGES: :[[@LINE-2]]:11: warning: initializer list should have a 
trailing comma
+  // CHECK-FIXES: N n = {
+  // CHECK-FIXES-NEXT:     {1, 2},
+  // CHECK-FIXES-NEXT:     {3, 4},
+  // CHECK-FIXES-NEXT:   };
+
+  N n2 = {
+    {1, 2},
+    {3, 4},
+  };
+}
+
+// Macros are ignored
 #define ENUM(n, a, b) enum n { a, b }
 #define INIT {1, 2}
 
-ENUM(E1M, X, Y);
+ENUM(E1M, Xm, Ym);
 int macroArr[] = INIT;
 
-enum E2M { Am, Bm };
-// CHECK-MESSAGES: :[[@LINE-1]]:18: warning: enum should have a trailing comma
-// CHECK-FIXES: enum E2M { Am, Bm, };
-
+// Template pack expansions - no warnings
 template <typename T, typename... Ts>
 struct Pack {
   int values[sizeof...(Ts) + 1] = {sizeof(T), sizeof(Ts)...};

>From f93108d6632ea204826aebff905ae598e97e0e7e Mon Sep 17 00:00:00 2001
From: Victor Baranov <[email protected]>
Date: Fri, 2 Jan 2026 23:21:13 +0300
Subject: [PATCH 3/4] added 2 different policies

---
 .../readability/TrailingCommaCheck.cpp        | 39 +++++++++-----
 .../readability/TrailingCommaCheck.h          |  8 +--
 .../checks/readability/trailing-comma.rst     | 53 +++++++------------
 .../trailing-comma-cxx20-remove.cpp           |  2 +-
 .../readability/trailing-comma-remove.cpp     |  2 +-
 .../checkers/readability/trailing-comma.c     |  2 +
 .../checkers/readability/trailing-comma.cpp   | 13 +++--
 7 files changed, 64 insertions(+), 55 deletions(-)

diff --git a/clang-tools-extra/clang-tidy/readability/TrailingCommaCheck.cpp 
b/clang-tools-extra/clang-tidy/readability/TrailingCommaCheck.cpp
index 5af89698c39f8..9ba116c8216ef 100644
--- a/clang-tools-extra/clang-tidy/readability/TrailingCommaCheck.cpp
+++ b/clang-tools-extra/clang-tidy/readability/TrailingCommaCheck.cpp
@@ -28,6 +28,8 @@ struct 
OptionEnumMapping<readability::TrailingCommaCheck::CommaPolicyKind> {
              "Append"},
             {readability::TrailingCommaCheck::CommaPolicyKind::Remove,
              "Remove"},
+            {readability::TrailingCommaCheck::CommaPolicyKind::Ignore,
+             "Ignore"},
         };
     return {Mapping};
   }
@@ -59,10 +61,14 @@ AST_MATCHER(InitListExpr, isEmptyInitList) { return 
Node.getNumInits() == 0; }
 TrailingCommaCheck::TrailingCommaCheck(StringRef Name,
                                        ClangTidyContext *Context)
     : ClangTidyCheck(Name, Context),
-      CommaPolicy(Options.get("CommaPolicy", CommaPolicyKind::Append)) {}
+      SingleLineCommaPolicy(
+          Options.get("SingleLineCommaPolicy", CommaPolicyKind::Remove)),
+      MultiLineCommaPolicy(
+          Options.get("MultiLineCommaPolicy", CommaPolicyKind::Append)) {}
 
 void TrailingCommaCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
-  Options.store(Opts, "CommaPolicy", CommaPolicy);
+  Options.store(Opts, "SingleLineCommaPolicy", SingleLineCommaPolicy);
+  Options.store(Opts, "MultiLineCommaPolicy", MultiLineCommaPolicy);
 }
 
 void TrailingCommaCheck::registerMatchers(MatchFinder *Finder) {
@@ -88,9 +94,12 @@ void TrailingCommaCheck::check(const 
MatchFinder::MatchResult &Result) {
 
 void TrailingCommaCheck::checkEnumDecl(const EnumDecl *Enum,
                                        const MatchFinder::MatchResult &Result) 
{
-  if (isSingleLine(Enum->getBeginLoc(), Enum->getEndLoc(),
-                   *Result.SourceManager) &&
-      CommaPolicy == CommaPolicyKind::Append)
+  const bool IsSingleLine = isSingleLine(Enum->getBeginLoc(), 
Enum->getEndLoc(),
+                                         *Result.SourceManager);
+  const CommaPolicyKind Policy =
+      IsSingleLine ? SingleLineCommaPolicy : MultiLineCommaPolicy;
+
+  if (Policy == CommaPolicyKind::Ignore)
     return;
 
   const EnumConstantDecl *LastEnumerator = nullptr;
@@ -107,7 +116,7 @@ void TrailingCommaCheck::checkEnumDecl(const EnumDecl *Enum,
   if (LastEnumLoc.isInvalid())
     return;
 
-  emitDiag(LastEnumLoc, DiagKind::Enum, Result);
+  emitDiag(LastEnumLoc, DiagKind::Enum, Result, Policy);
 }
 
 void TrailingCommaCheck::checkInitListExpr(
@@ -117,9 +126,12 @@ void TrailingCommaCheck::checkInitListExpr(
       SynInitInitList && SynInitInitList->getNumInits() > 0)
     InitList = SynInitInitList;
 
-  if (isSingleLine(InitList->getBeginLoc(), InitList->getEndLoc(),
-                   *Result.SourceManager) &&
-      CommaPolicy == CommaPolicyKind::Append)
+  const bool IsSingleLine = isSingleLine(
+      InitList->getBeginLoc(), InitList->getEndLoc(), *Result.SourceManager);
+  const CommaPolicyKind Policy =
+      IsSingleLine ? SingleLineCommaPolicy : MultiLineCommaPolicy;
+
+  if (Policy == CommaPolicyKind::Ignore)
     return;
 
   const unsigned NumInits = InitList->getNumInits();
@@ -134,12 +146,13 @@ void TrailingCommaCheck::checkInitListExpr(
   if (LastInitLoc.isInvalid())
     return;
 
-  emitDiag(LastInitLoc, DiagKind::InitList, Result);
+  emitDiag(LastInitLoc, DiagKind::InitList, Result, Policy);
 }
 
 void TrailingCommaCheck::emitDiag(
     SourceLocation LastLoc, DiagKind Kind,
-    const ast_matchers::MatchFinder::MatchResult &Result) {
+    const ast_matchers::MatchFinder::MatchResult &Result,
+    CommaPolicyKind Policy) {
   const std::optional<Token> NextToken =
       utils::lexer::findNextTokenSkippingComments(
           LastLoc, *Result.SourceManager, getLangOpts());
@@ -150,11 +163,11 @@ void TrailingCommaCheck::emitDiag(
   const SourceLocation InsertLoc = Lexer::getLocForEndOfToken(
       LastLoc, 0, *Result.SourceManager, getLangOpts());
 
-  if (CommaPolicy == CommaPolicyKind::Append && !HasTrailingComma) {
+  if (Policy == CommaPolicyKind::Append && !HasTrailingComma) {
     diag(InsertLoc, "%select{initializer list|enum}0 should have "
                     "a trailing comma")
         << Kind << FixItHint::CreateInsertion(InsertLoc, ",");
-  } else if (CommaPolicy == CommaPolicyKind::Remove && HasTrailingComma) {
+  } else if (Policy == CommaPolicyKind::Remove && HasTrailingComma) {
     const SourceLocation CommaLoc = NextToken->getLocation();
     if (CommaLoc.isInvalid())
       return;
diff --git a/clang-tools-extra/clang-tidy/readability/TrailingCommaCheck.h 
b/clang-tools-extra/clang-tidy/readability/TrailingCommaCheck.h
index 0b91a05fc14e1..a1adedda6a36c 100644
--- a/clang-tools-extra/clang-tidy/readability/TrailingCommaCheck.h
+++ b/clang-tools-extra/clang-tidy/readability/TrailingCommaCheck.h
@@ -31,10 +31,11 @@ class TrailingCommaCheck : public ClangTidyCheck {
     return TK_IgnoreUnlessSpelledInSource;
   }
 
-  enum class CommaPolicyKind { Append, Remove };
+  enum class CommaPolicyKind { Append, Remove, Ignore };
 
 private:
-  const CommaPolicyKind CommaPolicy;
+  const CommaPolicyKind SingleLineCommaPolicy;
+  const CommaPolicyKind MultiLineCommaPolicy;
 
   void checkEnumDecl(const EnumDecl *Enum,
                      const ast_matchers::MatchFinder::MatchResult &Result);
@@ -44,7 +45,8 @@ class TrailingCommaCheck : public ClangTidyCheck {
   // Values correspond to %select{initializer list|enum}0 indices
   enum DiagKind { InitList = 0, Enum = 1 };
   void emitDiag(SourceLocation LastLoc, DiagKind Kind,
-                const ast_matchers::MatchFinder::MatchResult &Result);
+                const ast_matchers::MatchFinder::MatchResult &Result,
+                CommaPolicyKind Policy);
 };
 
 } // namespace clang::tidy::readability
diff --git 
a/clang-tools-extra/docs/clang-tidy/checks/readability/trailing-comma.rst 
b/clang-tools-extra/docs/clang-tidy/checks/readability/trailing-comma.rst
index f1bd09d5d4e08..c61b42d21e2a4 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/readability/trailing-comma.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/readability/trailing-comma.rst
@@ -6,15 +6,16 @@ readability-trailing-comma
 Checks for presence or absence of trailing commas in enum definitions and
 initializer lists.
 
-The check can either append trailing commas where they are missing or remove
-them where they are present, based on the configured policy.
+The check supports separate policies for single-line and multi-line constructs,
+allowing different styles for each. By default, the check enforces trailing
+commas in multi-line constructs and removes them from single-line constructs.
 
-Trailing commas offer several benefits:
+Trailing commas in multi-line constructs offer several benefits:
 
-- Adding or removing elements may only changes a single line, making diffs
-  smaller and easier to read.
+- Adding or removing elements only changes a single line, making diffs smaller
+  and easier to read.
 - Formatters may change code to a more desired style.
-- Code generators avoid for need special handling of the last element.
+- Code generators avoid the need for special handling of the last element.
 
 .. code-block:: c++
 
@@ -42,42 +43,26 @@ The check currently don't analyze code inside macros.
 Options
 -------
 
-.. option:: CommaPolicy
+.. option:: SingleLineCommaPolicy
 
-  Controls whether to add or remove trailing commas.
+  Controls whether to add, remove, or ignore trailing commas in single-line
+  enum definitions and initializer lists.
   Valid values are:
 
   - `Append`: Add trailing commas where missing.
   - `Remove`: Remove trailing commas where present.
+  - `Ignore`: Do not check single-line constructs.
 
-  Example with `CommaPolicy` set to `Append`:
+  Default is `Remove`.
 
-  .. code-block:: c++
+.. option:: MultiLineCommaPolicy
 
-    enum Status {
-      OK,
-      Error     // warning: enum should have a trailing comma
-    };
-
-  Example with `CommaPolicy` set to `Remove`:
-
-  .. code-block:: c++
+  Controls whether to add, remove, or ignore trailing commas in multi-line
+  enum definitions and initializer lists.
+  Valid values are:
 
-    enum Status {
-      OK,
-      Error,    // warning: enum should not have a trailing comma
-    };
+  - `Append`: Add trailing commas where missing.
+  - `Remove`: Remove trailing commas where present.
+  - `Ignore`: Do not check multi-line constructs.
 
   Default is `Append`.
-
-.. option:: EnumThreshold
-
-  The minimum number of enumerators required in an enum before the check
-  will warn. This applies to both `Append` and `Remove` policies.
-  Default is `1` (always check enums).
-
-.. option:: InitListThreshold
-
-  The minimum number of elements required in an initializer list before
-  the check will warn. This applies to both `Append` and `Remove` policies.
-  Default is `3`.
diff --git 
a/clang-tools-extra/test/clang-tidy/checkers/readability/trailing-comma-cxx20-remove.cpp
 
b/clang-tools-extra/test/clang-tidy/checkers/readability/trailing-comma-cxx20-remove.cpp
index 78bc8d96cbf06..6c18678f88fec 100644
--- 
a/clang-tools-extra/test/clang-tidy/checkers/readability/trailing-comma-cxx20-remove.cpp
+++ 
b/clang-tools-extra/test/clang-tidy/checkers/readability/trailing-comma-cxx20-remove.cpp
@@ -1,5 +1,5 @@
 // RUN: %check_clang_tidy -std=c++20-or-later %s readability-trailing-comma %t 
-- \
-// RUN:   -config='{CheckOptions: {readability-trailing-comma.CommaPolicy: 
Remove}}'
+// RUN:   -config='{CheckOptions: 
{readability-trailing-comma.SingleLineCommaPolicy: Remove, 
readability-trailing-comma.MultiLineCommaPolicy: Remove}}'
 
 struct S { int x, y; };
 
diff --git 
a/clang-tools-extra/test/clang-tidy/checkers/readability/trailing-comma-remove.cpp
 
b/clang-tools-extra/test/clang-tidy/checkers/readability/trailing-comma-remove.cpp
index b74daeee090ad..9e1f50253949a 100644
--- 
a/clang-tools-extra/test/clang-tidy/checkers/readability/trailing-comma-remove.cpp
+++ 
b/clang-tools-extra/test/clang-tidy/checkers/readability/trailing-comma-remove.cpp
@@ -1,5 +1,5 @@
 // RUN: %check_clang_tidy -std=c++11-or-later %s readability-trailing-comma %t 
-- \
-// RUN:   -config='{CheckOptions: {readability-trailing-comma.CommaPolicy: 
Remove}}'
+// RUN:   -config='{CheckOptions: 
{readability-trailing-comma.SingleLineCommaPolicy: Remove, 
readability-trailing-comma.MultiLineCommaPolicy: Remove}}'
 
 struct S { int x, y; };
 
diff --git 
a/clang-tools-extra/test/clang-tidy/checkers/readability/trailing-comma.c 
b/clang-tools-extra/test/clang-tidy/checkers/readability/trailing-comma.c
index d416f3cc03384..a11222cd0e150 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/readability/trailing-comma.c
+++ b/clang-tools-extra/test/clang-tidy/checkers/readability/trailing-comma.c
@@ -14,6 +14,8 @@ enum Color {
 
 enum SingleLine { A, B, C };
 enum SingleLine2 { X1, Y1, };
+// CHECK-MESSAGES: :[[@LINE-1]]:26: warning: enum should not have a trailing 
comma
+// CHECK-FIXES: enum SingleLine2 { X1, Y1 };
 
 struct Point {
   int x;
diff --git 
a/clang-tools-extra/test/clang-tidy/checkers/readability/trailing-comma.cpp 
b/clang-tools-extra/test/clang-tidy/checkers/readability/trailing-comma.cpp
index 11cdbd23babcf..8614dc098ddca 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/readability/trailing-comma.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/readability/trailing-comma.cpp
@@ -22,9 +22,10 @@ enum E2 {
 // CHECK-FIXES-NEXT:   V = 1,
 // CHECK-FIXES-NEXT: };
 
-// Single-line enums - no warnings
 enum SingleLine { A1, B1, C1 };
 enum class SingleLine2 { X1, Y1, };
+// CHECK-MESSAGES: :[[@LINE-1]]:32: warning: enum should not have a trailing 
comma
+// CHECK-FIXES: enum class SingleLine2 { X1, Y1 };
 
 enum E3 {
   P,
@@ -51,10 +52,13 @@ void f() {
   // CHECK-FIXES-NEXT:     2,
   // CHECK-FIXES-NEXT:   };
 
-  // Single-line init lists - no warnings
   int b[] = {1, 2, 3};
   S s2 = {1, 2};
 
+  int e[] = {1, 2, 3,};
+  // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: initializer list should not 
have a trailing comma
+  // CHECK-FIXES: int e[] = {1, 2, 3};
+
   int c[] = {
     1,
     2,
@@ -65,7 +69,10 @@ void f() {
 struct N { S a, b; };
 void nested() {
   N n = {{1, 2}, {3, 4}};
-  N n2 = {{1, 2,}, {3, 4,},};
+  N n2 = {{3, 4,},};
+  // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: initializer list should not 
have a trailing comma
+  // CHECK-MESSAGES: :[[@LINE-2]]:18: warning: initializer list should not 
have a trailing comma
+  // CHECK-FIXES: N n2 = {{[{][{]3, 4[}][}]}};
 }
 
 void nestedMultiLine() {

>From f915b8b0aa1d2050eb771104908a28c6a6b2be5b Mon Sep 17 00:00:00 2001
From: Victor Baranov <[email protected]>
Date: Sat, 3 Jan 2026 00:09:33 +0300
Subject: [PATCH 4/4] more tests

---
 .../readability/TrailingCommaCheck.cpp        |  8 +--
 .../checks/readability/trailing-comma.rst     |  6 +-
 .../checkers/readability/trailing-comma.c     | 54 +++++++++++++++++-
 .../checkers/readability/trailing-comma.cpp   | 57 ++++++++++++++++++-
 4 files changed, 113 insertions(+), 12 deletions(-)

diff --git a/clang-tools-extra/clang-tidy/readability/TrailingCommaCheck.cpp 
b/clang-tools-extra/clang-tidy/readability/TrailingCommaCheck.cpp
index 9ba116c8216ef..6cf65c0f42561 100644
--- a/clang-tools-extra/clang-tidy/readability/TrailingCommaCheck.cpp
+++ b/clang-tools-extra/clang-tidy/readability/TrailingCommaCheck.cpp
@@ -134,8 +134,7 @@ void TrailingCommaCheck::checkInitListExpr(
   if (Policy == CommaPolicyKind::Ignore)
     return;
 
-  const unsigned NumInits = InitList->getNumInits();
-  const Expr *LastInit = InitList->getInit(NumInits - 1);
+  const Expr *LastInit = LastInit = InitList->inits().back();
   assert(LastInit);
 
   // Skip pack expansions - they already have special syntax with '...'
@@ -160,10 +159,9 @@ void TrailingCommaCheck::emitDiag(
     return;
 
   const bool HasTrailingComma = NextToken->is(tok::comma);
-  const SourceLocation InsertLoc = Lexer::getLocForEndOfToken(
-      LastLoc, 0, *Result.SourceManager, getLangOpts());
-
   if (Policy == CommaPolicyKind::Append && !HasTrailingComma) {
+    const SourceLocation InsertLoc = Lexer::getLocForEndOfToken(
+        LastLoc, 0, *Result.SourceManager, getLangOpts());
     diag(InsertLoc, "%select{initializer list|enum}0 should have "
                     "a trailing comma")
         << Kind << FixItHint::CreateInsertion(InsertLoc, ",");
diff --git 
a/clang-tools-extra/docs/clang-tidy/checks/readability/trailing-comma.rst 
b/clang-tools-extra/docs/clang-tidy/checks/readability/trailing-comma.rst
index c61b42d21e2a4..83e05fb1bead6 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/readability/trailing-comma.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/readability/trailing-comma.rst
@@ -12,8 +12,8 @@ commas in multi-line constructs and removes them from 
single-line constructs.
 
 Trailing commas in multi-line constructs offer several benefits:
 
-- Adding or removing elements only changes a single line, making diffs smaller
-  and easier to read.
+- Adding or removing elements at the end only changes a single line, making
+  diffs smaller and easier to read.
 - Formatters may change code to a more desired style.
 - Code generators avoid the need for special handling of the last element.
 
@@ -37,7 +37,7 @@ Trailing commas in multi-line constructs offer several 
benefits:
 Limitations
 -----------
 
-The check currently don't analyze code inside macros.
+The check currently doesn't analyze code inside macros.
 
 
 Options
diff --git 
a/clang-tools-extra/test/clang-tidy/checkers/readability/trailing-comma.c 
b/clang-tools-extra/test/clang-tidy/checkers/readability/trailing-comma.c
index a11222cd0e150..bdf9912e54155 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/readability/trailing-comma.c
+++ b/clang-tools-extra/test/clang-tidy/checkers/readability/trailing-comma.c
@@ -16,6 +16,17 @@ enum SingleLine { A, B, C };
 enum SingleLine2 { X1, Y1, };
 // CHECK-MESSAGES: :[[@LINE-1]]:26: warning: enum should not have a trailing 
comma
 // CHECK-FIXES: enum SingleLine2 { X1, Y1 };
+enum SingleVal { VAL1 };
+enum SingleVal2 { VAL2, };
+// CHECK-MESSAGES: :[[@LINE-1]]:23: warning: enum should not have a trailing 
comma
+// CHECK-FIXES: enum SingleVal2 { VAL2 };
+enum SingleVal3 {
+  VAL3
+};
+// CHECK-MESSAGES: :[[@LINE-2]]:7: warning: enum should have a trailing comma
+// CHECK-FIXES: enum SingleVal3 {
+// CHECK-FIXES-NEXT:   VAL3,
+// CHECK-FIXES-NEXT: };
 
 struct Point {
   int x;
@@ -62,8 +73,45 @@ int arr[] = {1, 2, 3};
 struct Point p3 = {10, 20};
 struct Point p4 = {.x = 1, .y = 2};
 int matrix[2][2] = {{1, 2}, {3, 4}};
+int single1[1] = {42};
+int single2[1] = {42,};
+// CHECK-MESSAGES: :[[@LINE-1]]:21: warning: initializer list should not have 
a trailing comma
+// CHECK-FIXES: int single2[1] = {42};
+int single3[1] = {
+  42
+};
+// CHECK-MESSAGES: :[[@LINE-2]]:5: warning: initializer list should have a 
trailing comma
+// CHECK-FIXES: int single3[1] = {
+// CHECK-FIXES-NEXT:   42,
+// CHECK-FIXES-NEXT: };
+int single4[1] = {
+  42,
+};
+int empty1[] = {};
+struct Nested { int a[2]; int b; };
+struct Nested nest1 = {{1}, 2};
+struct Nested nest2 = {{1,}, 2};
+// CHECK-MESSAGES: :[[@LINE-1]]:26: warning: initializer list should not have 
a trailing comma
+// CHECK-FIXES: struct Nested nest2 = {{[{][{]}}1}, 2};
+struct Nested nest3 = {
+  {1},
+  2
+};
+// CHECK-MESSAGES: :[[@LINE-2]]:4: warning: initializer list should have a 
trailing comma
+// CHECK-FIXES: struct Nested nest3 = {
+// CHECK-FIXES-NEXT:   {1},
+// CHECK-FIXES-NEXT:   2,
+// CHECK-FIXES-NEXT: };
 
-enum WithComma {
-  X,
-  Y,
+struct Point singleDesig1 = {.x = 10};
+struct Point singleDesig2 = {.x = 10,};
+// CHECK-MESSAGES: :[[@LINE-1]]:37: warning: initializer list should not have 
a trailing comma
+// CHECK-FIXES: struct Point singleDesig2 = {.x = 10};
+struct Point singleDesig3 = {};
+struct Point singleDesig4 = {
+  .x = 10
 };
+// CHECK-MESSAGES: :[[@LINE-2]]:10: warning: initializer list should have a 
trailing comma
+// CHECK-FIXES: struct Point singleDesig4 = {
+// CHECK-FIXES-NEXT:   .x = 10,
+// CHECK-FIXES-NEXT: };
diff --git 
a/clang-tools-extra/test/clang-tidy/checkers/readability/trailing-comma.cpp 
b/clang-tools-extra/test/clang-tidy/checkers/readability/trailing-comma.cpp
index 8614dc098ddca..c75a4af1df888 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/readability/trailing-comma.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/readability/trailing-comma.cpp
@@ -31,6 +31,22 @@ enum E3 {
   P,
   Q,
 };
+
+enum SingleEnum1 { ONE };   
+enum SingleEnum2 { TWO, };  
+// CHECK-MESSAGES: :[[@LINE-1]]:23: warning: enum should not have a trailing 
comma
+// CHECK-FIXES: enum SingleEnum2 { TWO };  
+enum SingleEnum3 {
+  THREE
+};
+// CHECK-MESSAGES: :[[@LINE-2]]:8: warning: enum should have a trailing comma
+// CHECK-FIXES: enum SingleEnum3 {
+// CHECK-FIXES-NEXT:    THREE,
+// CHECK-FIXES-NEXT:  };
+enum SingleEnum4 {
+  FOUR,
+};
+
 enum Empty {};
 
 void f() {
@@ -64,6 +80,25 @@ void f() {
     2,
   };
   int d[] = {};
+
+  int single1[] = {1};
+  int single2[] = {1,};
+  // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: initializer list should not 
have a trailing comma
+  // CHECK-FIXES:  int single2[] = {1};       
+  int single3[] = {
+    1
+  };
+  // CHECK-MESSAGES: :[[@LINE-2]]:6: warning: initializer list should have a 
trailing comma
+  // CHECK-FIXES:  int single3[] = {
+  // CHECK-FIXES-NEXT:    1,
+  // CHECK-FIXES-NEXT:  };
+  int single4[] = {
+    1,
+  };
+  S singleS1 = {42};          
+  S singleS2 = {42,};         
+  // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: initializer list should not 
have a trailing comma
+  // CHECK-FIXES:  S singleS2 = {42};      
 }
 
 struct N { S a, b; };
@@ -90,6 +125,26 @@ void nestedMultiLine() {
     {1, 2},
     {3, 4},
   };
+
+  struct Container { int arr[3]; };
+  Container c1 = {{}};
+  Container c2 = {{},};
+  // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: initializer list should not 
have a trailing comma
+  // CHECK-FIXES:   Container c2 = {{[{][{][}][}]}};
+
+  struct Wrapper { S s; };
+  Wrapper w1 = {{1}};
+  Wrapper w2 = {{1,}};
+  // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: initializer list should not 
have a trailing comma
+  // CHECK-FIXES:   Wrapper w2 = {{[{][{]1[}][}]}};
+
+  Wrapper w3 = {
+    {1}
+  };
+  // CHECK-MESSAGES: :[[@LINE-2]]:8: warning: initializer list should have a 
trailing comma
+  // CHECK-FIXES: Wrapper w3 = {
+  // CHECK-FIXES-NEXT:     {1},
+  // CHECK-FIXES-NEXT:   };
 }
 
 // Macros are ignored
@@ -105,7 +160,7 @@ struct Pack {
   int values[sizeof...(Ts) + 1] = {sizeof(T), sizeof(Ts)...};
 };
 
-Pack<int> single;
+Pack<int> one;
 Pack<int, double> two;
 Pack<int, double, char> three;
 

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to