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
