Author: Baranov Victor
Date: 2026-01-31T19:43:45Z
New Revision: 975d56272e353d6a9772de53de4270c0617b01a2

URL: 
https://github.com/llvm/llvm-project/commit/975d56272e353d6a9772de53de4270c0617b01a2
DIFF: 
https://github.com/llvm/llvm-project/commit/975d56272e353d6a9772de53de4270c0617b01a2.diff

LOG: [clang-tidy] Add new check readability-trailing-comma (#173669)

clang-format has a couple of similar options:

https://clang.llvm.org/docs/ClangFormatStyleOptions.html#enumtrailingcomma
- add trailing commas for enum

https://clang.llvm.org/docs/ClangFormatStyleOptions.html#inserttrailingcommas
- add trailing commas for C++
but generally they are marked with such warning:

> Warning
>
> Setting this option to any value other than Leave could lead to
incorrect code formatting due to clang-format’s lack of complete
semantic information. As such, extra care should be taken to review code
changes made by this option.

clang-tidy on the other hand has all semantic information, thus can
(hopefully) provide 0 false-positives.

Note that we have already overlapping checks in clang-format/clang-tidy
like:
https://clang.llvm.org/docs/ClangFormatStyleOptions.html#insertbraces vs
https://clang.llvm.org/extra/clang-tidy/checks/readability/braces-around-statements.html

Added: 
    clang-tools-extra/clang-tidy/readability/TrailingCommaCheck.cpp
    clang-tools-extra/clang-tidy/readability/TrailingCommaCheck.h
    clang-tools-extra/docs/clang-tidy/checks/readability/trailing-comma.rst
    
clang-tools-extra/test/clang-tidy/checkers/readability/trailing-comma-cxx11.cpp
    
clang-tools-extra/test/clang-tidy/checkers/readability/trailing-comma-cxx20-remove.cpp
    
clang-tools-extra/test/clang-tidy/checkers/readability/trailing-comma-cxx20.cpp
    
clang-tools-extra/test/clang-tidy/checkers/readability/trailing-comma-remove.cpp
    
clang-tools-extra/test/clang-tidy/checkers/readability/trailing-comma-wrong-config.cpp
    clang-tools-extra/test/clang-tidy/checkers/readability/trailing-comma.c
    clang-tools-extra/test/clang-tidy/checkers/readability/trailing-comma.cpp

Modified: 
    clang-tools-extra/clang-tidy/readability/CMakeLists.txt
    clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp
    clang-tools-extra/docs/ReleaseNotes.rst
    clang-tools-extra/docs/clang-tidy/checks/list.rst

Removed: 
    


################################################################################
diff  --git a/clang-tools-extra/clang-tidy/readability/CMakeLists.txt 
b/clang-tools-extra/clang-tidy/readability/CMakeLists.txt
index a55f0ab78f97f..f1f3cde32feff 100644
--- a/clang-tools-extra/clang-tidy/readability/CMakeLists.txt
+++ b/clang-tools-extra/clang-tidy/readability/CMakeLists.txt
@@ -58,6 +58,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 d2da125a9d6da..c582dc98eac6b 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"
@@ -177,6 +178,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..4dd881cf37993
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/readability/TrailingCommaCheck.cpp
@@ -0,0 +1,179 @@
+//===----------------------------------------------------------------------===//
+//
+// 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"},
+            {readability::TrailingCommaCheck::CommaPolicyKind::Ignore,
+             "Ignore"},
+        };
+    return {Mapping};
+  }
+};
+
+} // namespace clang::tidy
+
+namespace clang::tidy::readability {
+
+static bool isSingleLine(SourceRange Range, const SourceManager &SM) {
+  return SM.getExpansionLineNumber(Range.getBegin()) ==
+         SM.getExpansionLineNumber(Range.getEnd());
+}
+
+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),
+      SingleLineCommaPolicy(
+          Options.get("SingleLineCommaPolicy", CommaPolicyKind::Remove)),
+      MultiLineCommaPolicy(
+          Options.get("MultiLineCommaPolicy", CommaPolicyKind::Append)) {
+  if (SingleLineCommaPolicy == CommaPolicyKind::Ignore &&
+      MultiLineCommaPolicy == CommaPolicyKind::Ignore)
+    configurationDiag("The check '%0' will not perform any analysis because "
+                      "'SingleLineCommaPolicy' and 'MultiLineCommaPolicy' are "
+                      "both set to 'Ignore'.")
+        << Name;
+}
+
+void TrailingCommaCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
+  Options.store(Opts, "SingleLineCommaPolicy", SingleLineCommaPolicy);
+  Options.store(Opts, "MultiLineCommaPolicy", MultiLineCommaPolicy);
+}
+
+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) 
{
+  const bool IsSingleLine = isSingleLine(
+      {Enum->getBeginLoc(), Enum->getEndLoc()}, *Result.SourceManager);
+  const CommaPolicyKind Policy =
+      IsSingleLine ? SingleLineCommaPolicy : MultiLineCommaPolicy;
+
+  if (Policy == CommaPolicyKind::Ignore)
+    return;
+
+  const std::optional<Token> LastTok =
+      Lexer::findPreviousToken(Enum->getBraceRange().getEnd(),
+                               *Result.SourceManager, getLangOpts(), false);
+  if (!LastTok)
+    return;
+
+  emitDiag(LastTok->getLocation(), LastTok, DiagKind::Enum, Result, Policy);
+}
+
+void TrailingCommaCheck::checkInitListExpr(
+    const InitListExpr *InitList, const MatchFinder::MatchResult &Result) {
+  // We need to use non-empty syntactic form for correct source locations.
+  if (const InitListExpr *SynInitInitList = InitList->getSyntacticForm();
+      SynInitInitList && SynInitInitList->getNumInits() > 0)
+    InitList = SynInitInitList;
+
+  const bool IsSingleLine = isSingleLine(
+      {InitList->getBeginLoc(), InitList->getEndLoc()}, *Result.SourceManager);
+  const CommaPolicyKind Policy =
+      IsSingleLine ? SingleLineCommaPolicy : MultiLineCommaPolicy;
+
+  if (Policy == CommaPolicyKind::Ignore)
+    return;
+
+  const Expr *LastInit = InitList->inits().back();
+  assert(LastInit);
+
+  // Skip pack expansions - they already have special syntax with '...'
+  if (isa<PackExpansionExpr>(LastInit))
+    return;
+
+  const std::optional<Token> NextTok =
+      utils::lexer::findNextTokenSkippingComments(
+          LastInit->getEndLoc(), *Result.SourceManager, getLangOpts());
+
+  // If the next token is neither a comma nor closing brace, there might be
+  // a macro (e.g., #define COMMA ,) that we can't safely analyze.
+  if (NextTok && !NextTok->isOneOf(tok::comma, tok::r_brace))
+    return;
+
+  emitDiag(LastInit->getEndLoc(), NextTok, DiagKind::InitList, Result, Policy);
+}
+
+void TrailingCommaCheck::emitDiag(
+    SourceLocation LastLoc, std::optional<Token> Token, DiagKind Kind,
+    const ast_matchers::MatchFinder::MatchResult &Result,
+    CommaPolicyKind Policy) {
+  if (LastLoc.isInvalid() || !Token)
+    return;
+
+  const bool HasTrailingComma = Token->is(tok::comma);
+  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, ",");
+  } else if (Policy == CommaPolicyKind::Remove && HasTrailingComma) {
+    const SourceLocation CommaLoc = Token->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..f10b3fbdfb142
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/readability/TrailingCommaCheck.h
@@ -0,0 +1,55 @@
+//===----------------------------------------------------------------------===//
+//
+// 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.CPlusPlus || LangOpts.C99;
+  }
+  std::optional<TraversalKind> getCheckTraversalKind() const override {
+    return TK_IgnoreUnlessSpelledInSource;
+  }
+
+  enum class CommaPolicyKind { Append, Remove, Ignore };
+
+private:
+  const CommaPolicyKind SingleLineCommaPolicy;
+  const CommaPolicyKind MultiLineCommaPolicy;
+
+  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, std::optional<Token> Token,
+                DiagKind Kind,
+                const ast_matchers::MatchFinder::MatchResult &Result,
+                CommaPolicyKind Policy);
+};
+
+} // 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 976223e5c8149..8cf2006172d3f 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -128,6 +128,12 @@ New checks
   Finds and removes redundant conversions from 
``std::[w|u8|u16|u32]string_view`` to
   ``std::[...]string`` in call expressions expecting ``std::[...]string_view``.
 
+- 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 2c9d5face0bee..34d1c2ce0a174 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/list.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst
@@ -429,6 +429,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..83e05fb1bead6
--- /dev/null
+++ b/clang-tools-extra/docs/clang-tidy/checks/readability/trailing-comma.rst
@@ -0,0 +1,68 @@
+.. 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 supports separate policies for single-line and multi-line constructs,
+allowing 
diff erent styles for each. By default, the check enforces trailing
+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 at the end only changes a single line, making
+  
diff s 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.
+
+.. 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 doesn't analyze code inside macros.
+
+
+Options
+-------
+
+.. option:: SingleLineCommaPolicy
+
+  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.
+
+  Default is `Remove`.
+
+.. option:: MultiLineCommaPolicy
+
+  Controls whether to add, remove, or ignore trailing commas in multi-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 multi-line constructs.
+
+  Default is `Append`.

diff  --git 
a/clang-tools-extra/test/clang-tidy/checkers/readability/trailing-comma-cxx11.cpp
 
b/clang-tools-extra/test/clang-tidy/checkers/readability/trailing-comma-cxx11.cpp
new file mode 100644
index 0000000000000..9f37db2c837c3
--- /dev/null
+++ 
b/clang-tools-extra/test/clang-tidy/checkers/readability/trailing-comma-cxx11.cpp
@@ -0,0 +1,56 @@
+// RUN: %check_clang_tidy -std=c++11-or-later %s readability-trailing-comma %t
+
+enum class SingleLine2 { X1, Y1 = 1, };
+// CHECK-MESSAGES: :[[@LINE-1]]:36: warning: enum should not have a trailing 
comma
+// CHECK-FIXES: enum class SingleLine2 { X1, Y1 = 1 };
+
+enum EnumWithAttrs {
+  E1 [[deprecated]] = 1,
+  E2 [[deprecated]]
+};
+// CHECK-MESSAGES: :[[@LINE-2]]:20: warning: enum should have a trailing comma
+// CHECK-FIXES: enum EnumWithAttrs {
+// CHECK-FIXES-NEXT:   E1 {{\[\[}}deprecated{{\]\]}} = 1,
+// CHECK-FIXES-NEXT:   E2 {{\[\[}}deprecated{{\]\]}},
+// CHECK-FIXES-NEXT: };
+
+enum EnumWithAttrs2 {
+  E3 [[deprecated]] = 1,
+  E4 [[deprecated]] = 2
+};
+// CHECK-MESSAGES: :[[@LINE-2]]:24: warning: enum should have a trailing comma
+// CHECK-FIXES: enum EnumWithAttrs2 {
+// CHECK-FIXES-NEXT:   E3 {{\[\[}}deprecated{{\]\]}} = 1,
+// CHECK-FIXES-NEXT:   E4 {{\[\[}}deprecated{{\]\]}} = 2,
+// CHECK-FIXES-NEXT: };
+
+enum EnumWithAttrsTrailing {
+  E5 [[deprecated]] = 1,
+  E6 [[deprecated]],
+};
+
+enum SingleLineAttrs { E7 [[deprecated]], E8 [[deprecated]] [[deprecated  ]] , 
};
+// CHECK-MESSAGES: :[[@LINE-1]]:78: warning: enum should not have a trailing 
comma
+// CHECK-FIXES: enum SingleLineAttrs { E7 {{\[\[}}deprecated{{\]\]}}, E8 
{{\[\[}}deprecated{{\]\]}} {{\[\[}}deprecated  {{\]\]}}  };
+
+enum SingleLineAttrs2 { E9 [[deprecated]], E10 [[deprecated]] = 1, };
+// CHECK-MESSAGES: :[[@LINE-1]]:66: warning: enum should not have a trailing 
comma
+// CHECK-FIXES: enum SingleLineAttrs2 { E9 {{\[\[}}deprecated{{\]\]}}, E10 
{{\[\[}}deprecated{{\]\]}} = 1 };
+
+// Template pack expansions - no warnings
+template <typename T, typename... Ts>
+struct Pack {
+  int values[sizeof...(Ts) + 1] = {sizeof(T), sizeof(Ts)...};
+};
+
+Pack<int> one;
+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;

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..6c18678f88fec
--- /dev/null
+++ 
b/clang-tools-extra/test/clang-tidy/checkers/readability/trailing-comma-cxx20-remove.cpp
@@ -0,0 +1,64 @@
+// RUN: %check_clang_tidy -std=c++20-or-later %s readability-trailing-comma %t 
-- \
+// RUN:   -config='{CheckOptions: 
{readability-trailing-comma.SingleLineCommaPolicy: Remove, 
readability-trailing-comma.MultiLineCommaPolicy: Remove}}'
+
+struct S { int x, y; };
+
+void f() {
+  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:   };
+
+  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:   };
+
+  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};
+
+  S s3 = {
+    .x = 1,
+    .y = 2
+  };
+}
+
+struct N { S a, b; };
+
+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}
+  };
+}
+
+struct WithArray {
+  int values[3];
+  int count;
+};
+
+void with_array() {
+  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 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
new file mode 100644
index 0000000000000..b2f6ed072563f
--- /dev/null
+++ 
b/clang-tools-extra/test/clang-tidy/checkers/readability/trailing-comma-cxx20.cpp
@@ -0,0 +1,89 @@
+// 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-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:   };
+
+  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:   };
+
+  S s2 = {.x = 1, .y = 2};
+  S s3 = {.x = 1};
+
+  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-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:   };
+
+  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 {
+  int values[3];
+  int count;
+};
+
+void with_array() {
+  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 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
new file mode 100644
index 0000000000000..fadce1808876f
--- /dev/null
+++ 
b/clang-tools-extra/test/clang-tidy/checkers/readability/trailing-comma-remove.cpp
@@ -0,0 +1,79 @@
+// RUN: %check_clang_tidy -std=c++98-or-later %s readability-trailing-comma %t 
-- \
+// RUN:   -config='{CheckOptions: 
{readability-trailing-comma.SingleLineCommaPolicy: Remove, 
readability-trailing-comma.MultiLineCommaPolicy: Remove}}'
+
+struct S { int x, y; };
+
+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 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 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 E3 {
+  P,
+  Q
+};
+enum Empty {};
+
+void f() {
+  // 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-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, 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: S s2 = {1, 2};
+
+  int c[] = {
+    1,
+    2
+  };
+  int d[] = {};
+}
+
+struct N { S a, b; };
+void nested() {
+  N n = {{3, 4,},};
+  // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: initializer list should not 
have a trailing comma
+  // 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-wrong-config.cpp
 
b/clang-tools-extra/test/clang-tidy/checkers/readability/trailing-comma-wrong-config.cpp
new file mode 100644
index 0000000000000..a3ce4e70d426a
--- /dev/null
+++ 
b/clang-tools-extra/test/clang-tidy/checkers/readability/trailing-comma-wrong-config.cpp
@@ -0,0 +1,11 @@
+// RUN: %check_clang_tidy -std=c++98-or-later %s readability-trailing-comma %t 
-- \
+// RUN:   -config='{CheckOptions: 
{readability-trailing-comma.SingleLineCommaPolicy: Ignore, 
readability-trailing-comma.MultiLineCommaPolicy: Ignore}}'
+
+
+// CHECK-MESSAGES: warning: The check 'readability-trailing-comma' will not 
perform any analysis because 'SingleLineCommaPolicy' and 'MultiLineCommaPolicy' 
are both set to 'Ignore'. [clang-tidy-config]
+
+enum E1 {
+  A
+};
+
+enum E2 { B, };

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..bdf9912e54155
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/readability/trailing-comma.c
@@ -0,0 +1,117 @@
+// RUN: %check_clang_tidy %s readability-trailing-comma %t
+
+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: };
+
+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;
+  int y;
+};
+
+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-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: };
+
+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 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}};
+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: };
+
+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
new file mode 100644
index 0000000000000..76fb4bbf0c37d
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/readability/trailing-comma.cpp
@@ -0,0 +1,161 @@
+// RUN: %check_clang_tidy -std=c++98-or-later %s readability-trailing-comma %t
+
+struct S { int x, y; };
+
+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: };
+
+enum SingleLine { A1, B1, C1 };
+
+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() {
+  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:   };
+
+  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,
+  };
+  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; };
+void nested() {
+  N n = {{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() {
+  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},
+  };
+
+  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
+#define ENUM(n, a, b) enum n { a, b }
+#define INIT {1, 2}
+#define ITEMS 1,2
+
+ENUM(E1M, Xm, Ym);
+int macroArr[] = INIT;
+int a[] = { ITEMS };
+
+// Comma from macro should not trigger false positive
+#define COMMA ,
+int comma_from_macro[] = {
+    1
+    COMMA
+};


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

Reply via email to