[clang-tools-extra] [clang-tidy] Add new check readability-trailing-comma (PR #173669)
Baranov Victor via cfe-commits
cfe-commits at lists.llvm.org
Fri Dec 26 07:48:47 PST 2025
https://github.com/vbvictor created https://github.com/llvm/llvm-project/pull/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
>From 60ecb84f835c228fe899eba7c3f0973f802156b4 Mon Sep 17 00:00:00 2001
From: Victor Baranov <bar.victor.2002 at gmail.com>
Date: Tue, 23 Dec 2025 02:27:23 +0300
Subject: [PATCH] [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;
More information about the cfe-commits
mailing list