Author: Baranov Victor Date: 2026-01-31T19:43:45Z New Revision: 975d56272e353d6a9772de53de4270c0617b01a2
URL: https://github.com/llvm/llvm-project/commit/975d56272e353d6a9772de53de4270c0617b01a2 DIFF: https://github.com/llvm/llvm-project/commit/975d56272e353d6a9772de53de4270c0617b01a2.diff LOG: [clang-tidy] Add new check readability-trailing-comma (#173669) clang-format has a couple of similar options: https://clang.llvm.org/docs/ClangFormatStyleOptions.html#enumtrailingcomma - add trailing commas for enum https://clang.llvm.org/docs/ClangFormatStyleOptions.html#inserttrailingcommas - add trailing commas for C++ but generally they are marked with such warning: > Warning > > Setting this option to any value other than Leave could lead to incorrect code formatting due to clang-format’s lack of complete semantic information. As such, extra care should be taken to review code changes made by this option. clang-tidy on the other hand has all semantic information, thus can (hopefully) provide 0 false-positives. Note that we have already overlapping checks in clang-format/clang-tidy like: https://clang.llvm.org/docs/ClangFormatStyleOptions.html#insertbraces vs https://clang.llvm.org/extra/clang-tidy/checks/readability/braces-around-statements.html Added: clang-tools-extra/clang-tidy/readability/TrailingCommaCheck.cpp clang-tools-extra/clang-tidy/readability/TrailingCommaCheck.h clang-tools-extra/docs/clang-tidy/checks/readability/trailing-comma.rst clang-tools-extra/test/clang-tidy/checkers/readability/trailing-comma-cxx11.cpp clang-tools-extra/test/clang-tidy/checkers/readability/trailing-comma-cxx20-remove.cpp clang-tools-extra/test/clang-tidy/checkers/readability/trailing-comma-cxx20.cpp clang-tools-extra/test/clang-tidy/checkers/readability/trailing-comma-remove.cpp clang-tools-extra/test/clang-tidy/checkers/readability/trailing-comma-wrong-config.cpp clang-tools-extra/test/clang-tidy/checkers/readability/trailing-comma.c clang-tools-extra/test/clang-tidy/checkers/readability/trailing-comma.cpp Modified: clang-tools-extra/clang-tidy/readability/CMakeLists.txt clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp clang-tools-extra/docs/ReleaseNotes.rst clang-tools-extra/docs/clang-tidy/checks/list.rst Removed: ################################################################################ diff --git a/clang-tools-extra/clang-tidy/readability/CMakeLists.txt b/clang-tools-extra/clang-tidy/readability/CMakeLists.txt index a55f0ab78f97f..f1f3cde32feff 100644 --- a/clang-tools-extra/clang-tidy/readability/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/readability/CMakeLists.txt @@ -58,6 +58,7 @@ add_clang_library(clangTidyReadabilityModule STATIC StaticDefinitionInAnonymousNamespaceCheck.cpp StringCompareCheck.cpp SuspiciousCallArgumentCheck.cpp + TrailingCommaCheck.cpp UniqueptrDeleteReleaseCheck.cpp UppercaseLiteralSuffixCheck.cpp UseAnyOfAllOfCheck.cpp diff --git a/clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp b/clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp index d2da125a9d6da..c582dc98eac6b 100644 --- a/clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp +++ b/clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp @@ -60,6 +60,7 @@ #include "StaticDefinitionInAnonymousNamespaceCheck.h" #include "StringCompareCheck.h" #include "SuspiciousCallArgumentCheck.h" +#include "TrailingCommaCheck.h" #include "UniqueptrDeleteReleaseCheck.h" #include "UppercaseLiteralSuffixCheck.h" #include "UseAnyOfAllOfCheck.h" @@ -177,6 +178,8 @@ class ReadabilityModule : public ClangTidyModule { "readability-simplify-boolean-expr"); CheckFactories.registerCheck<SuspiciousCallArgumentCheck>( "readability-suspicious-call-argument"); + CheckFactories.registerCheck<TrailingCommaCheck>( + "readability-trailing-comma"); CheckFactories.registerCheck<UniqueptrDeleteReleaseCheck>( "readability-uniqueptr-delete-release"); CheckFactories.registerCheck<UppercaseLiteralSuffixCheck>( diff --git a/clang-tools-extra/clang-tidy/readability/TrailingCommaCheck.cpp b/clang-tools-extra/clang-tidy/readability/TrailingCommaCheck.cpp new file mode 100644 index 0000000000000..4dd881cf37993 --- /dev/null +++ b/clang-tools-extra/clang-tidy/readability/TrailingCommaCheck.cpp @@ -0,0 +1,179 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "TrailingCommaCheck.h" +#include "../utils/LexerUtils.h" +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/Lex/Lexer.h" + +using namespace clang::ast_matchers; + +namespace clang::tidy { + +template <> +struct OptionEnumMapping<readability::TrailingCommaCheck::CommaPolicyKind> { + static llvm::ArrayRef< + std::pair<readability::TrailingCommaCheck::CommaPolicyKind, StringRef>> + getEnumMapping() { + static constexpr std::pair<readability::TrailingCommaCheck::CommaPolicyKind, + StringRef> + Mapping[] = { + {readability::TrailingCommaCheck::CommaPolicyKind::Append, + "Append"}, + {readability::TrailingCommaCheck::CommaPolicyKind::Remove, + "Remove"}, + {readability::TrailingCommaCheck::CommaPolicyKind::Ignore, + "Ignore"}, + }; + return {Mapping}; + } +}; + +} // namespace clang::tidy + +namespace clang::tidy::readability { + +static bool isSingleLine(SourceRange Range, const SourceManager &SM) { + return SM.getExpansionLineNumber(Range.getBegin()) == + SM.getExpansionLineNumber(Range.getEnd()); +} + +namespace { + +AST_POLYMORPHIC_MATCHER(isMacro, + AST_POLYMORPHIC_SUPPORTED_TYPES(EnumDecl, + InitListExpr)) { + return Node.getBeginLoc().isMacroID() || Node.getEndLoc().isMacroID(); +} + +AST_MATCHER(EnumDecl, isEmptyEnum) { return Node.enumerators().empty(); } + +AST_MATCHER(InitListExpr, isEmptyInitList) { return Node.getNumInits() == 0; } + +} // namespace + +TrailingCommaCheck::TrailingCommaCheck(StringRef Name, + ClangTidyContext *Context) + : ClangTidyCheck(Name, Context), + SingleLineCommaPolicy( + Options.get("SingleLineCommaPolicy", CommaPolicyKind::Remove)), + MultiLineCommaPolicy( + Options.get("MultiLineCommaPolicy", CommaPolicyKind::Append)) { + if (SingleLineCommaPolicy == CommaPolicyKind::Ignore && + MultiLineCommaPolicy == CommaPolicyKind::Ignore) + configurationDiag("The check '%0' will not perform any analysis because " + "'SingleLineCommaPolicy' and 'MultiLineCommaPolicy' are " + "both set to 'Ignore'.") + << Name; +} + +void TrailingCommaCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { + Options.store(Opts, "SingleLineCommaPolicy", SingleLineCommaPolicy); + Options.store(Opts, "MultiLineCommaPolicy", MultiLineCommaPolicy); +} + +void TrailingCommaCheck::registerMatchers(MatchFinder *Finder) { + Finder->addMatcher( + enumDecl(isDefinition(), unless(isEmptyEnum()), unless(isMacro())) + .bind("enum"), + this); + + Finder->addMatcher(initListExpr(unless(isEmptyInitList()), unless(isMacro())) + .bind("initlist"), + this); +} + +void TrailingCommaCheck::check(const MatchFinder::MatchResult &Result) { + if (const auto *Enum = Result.Nodes.getNodeAs<EnumDecl>("enum")) + checkEnumDecl(Enum, Result); + else if (const auto *InitList = + Result.Nodes.getNodeAs<InitListExpr>("initlist")) + checkInitListExpr(InitList, Result); + else + llvm_unreachable("No matches found"); +} + +void TrailingCommaCheck::checkEnumDecl(const EnumDecl *Enum, + const MatchFinder::MatchResult &Result) { + const bool IsSingleLine = isSingleLine( + {Enum->getBeginLoc(), Enum->getEndLoc()}, *Result.SourceManager); + const CommaPolicyKind Policy = + IsSingleLine ? SingleLineCommaPolicy : MultiLineCommaPolicy; + + if (Policy == CommaPolicyKind::Ignore) + return; + + const std::optional<Token> LastTok = + Lexer::findPreviousToken(Enum->getBraceRange().getEnd(), + *Result.SourceManager, getLangOpts(), false); + if (!LastTok) + return; + + emitDiag(LastTok->getLocation(), LastTok, DiagKind::Enum, Result, Policy); +} + +void TrailingCommaCheck::checkInitListExpr( + const InitListExpr *InitList, const MatchFinder::MatchResult &Result) { + // We need to use non-empty syntactic form for correct source locations. + if (const InitListExpr *SynInitInitList = InitList->getSyntacticForm(); + SynInitInitList && SynInitInitList->getNumInits() > 0) + InitList = SynInitInitList; + + const bool IsSingleLine = isSingleLine( + {InitList->getBeginLoc(), InitList->getEndLoc()}, *Result.SourceManager); + const CommaPolicyKind Policy = + IsSingleLine ? SingleLineCommaPolicy : MultiLineCommaPolicy; + + if (Policy == CommaPolicyKind::Ignore) + return; + + const Expr *LastInit = InitList->inits().back(); + assert(LastInit); + + // Skip pack expansions - they already have special syntax with '...' + if (isa<PackExpansionExpr>(LastInit)) + return; + + const std::optional<Token> NextTok = + utils::lexer::findNextTokenSkippingComments( + LastInit->getEndLoc(), *Result.SourceManager, getLangOpts()); + + // If the next token is neither a comma nor closing brace, there might be + // a macro (e.g., #define COMMA ,) that we can't safely analyze. + if (NextTok && !NextTok->isOneOf(tok::comma, tok::r_brace)) + return; + + emitDiag(LastInit->getEndLoc(), NextTok, DiagKind::InitList, Result, Policy); +} + +void TrailingCommaCheck::emitDiag( + SourceLocation LastLoc, std::optional<Token> Token, DiagKind Kind, + const ast_matchers::MatchFinder::MatchResult &Result, + CommaPolicyKind Policy) { + if (LastLoc.isInvalid() || !Token) + return; + + const bool HasTrailingComma = Token->is(tok::comma); + if (Policy == CommaPolicyKind::Append && !HasTrailingComma) { + const SourceLocation InsertLoc = Lexer::getLocForEndOfToken( + LastLoc, 0, *Result.SourceManager, getLangOpts()); + diag(InsertLoc, "%select{initializer list|enum}0 should have " + "a trailing comma") + << Kind << FixItHint::CreateInsertion(InsertLoc, ","); + } else if (Policy == CommaPolicyKind::Remove && HasTrailingComma) { + const SourceLocation CommaLoc = Token->getLocation(); + if (CommaLoc.isInvalid()) + return; + diag(CommaLoc, "%select{initializer list|enum}0 should not have " + "a trailing comma") + << Kind << FixItHint::CreateRemoval(CommaLoc); + } +} + +} // namespace clang::tidy::readability diff --git a/clang-tools-extra/clang-tidy/readability/TrailingCommaCheck.h b/clang-tools-extra/clang-tidy/readability/TrailingCommaCheck.h new file mode 100644 index 0000000000000..f10b3fbdfb142 --- /dev/null +++ b/clang-tools-extra/clang-tidy/readability/TrailingCommaCheck.h @@ -0,0 +1,55 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_TRAILINGCOMMACHECK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_TRAILINGCOMMACHECK_H + +#include "../ClangTidyCheck.h" + +namespace clang::tidy::readability { + +/// Checks for presence or absence of trailing commas in enum definitions +/// and initializer lists. +/// +/// For the user-facing documentation see: +/// https://clang.llvm.org/extra/clang-tidy/checks/readability/trailing-comma.html +class TrailingCommaCheck : public ClangTidyCheck { +public: + TrailingCommaCheck(StringRef Name, ClangTidyContext *Context); + void storeOptions(ClangTidyOptions::OptionMap &Opts) override; + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; + bool isLanguageVersionSupported(const LangOptions &LangOpts) const override { + return LangOpts.CPlusPlus || LangOpts.C99; + } + std::optional<TraversalKind> getCheckTraversalKind() const override { + return TK_IgnoreUnlessSpelledInSource; + } + + enum class CommaPolicyKind { Append, Remove, Ignore }; + +private: + const CommaPolicyKind SingleLineCommaPolicy; + const CommaPolicyKind MultiLineCommaPolicy; + + void checkEnumDecl(const EnumDecl *Enum, + const ast_matchers::MatchFinder::MatchResult &Result); + void checkInitListExpr(const InitListExpr *InitList, + const ast_matchers::MatchFinder::MatchResult &Result); + + // Values correspond to %select{initializer list|enum}0 indices + enum DiagKind { InitList = 0, Enum = 1 }; + void emitDiag(SourceLocation LastLoc, std::optional<Token> Token, + DiagKind Kind, + const ast_matchers::MatchFinder::MatchResult &Result, + CommaPolicyKind Policy); +}; + +} // namespace clang::tidy::readability + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_TRAILINGCOMMACHECK_H diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst index 976223e5c8149..8cf2006172d3f 100644 --- a/clang-tools-extra/docs/ReleaseNotes.rst +++ b/clang-tools-extra/docs/ReleaseNotes.rst @@ -128,6 +128,12 @@ New checks Finds and removes redundant conversions from ``std::[w|u8|u16|u32]string_view`` to ``std::[...]string`` in call expressions expecting ``std::[...]string_view``. +- New :doc:`readability-trailing-comma + <clang-tidy/checks/readability/trailing-comma>` check. + + Checks for presence or absence of trailing commas in enum definitions and + initializer lists. + New check aliases ^^^^^^^^^^^^^^^^^ diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst index 2c9d5face0bee..34d1c2ce0a174 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/list.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst @@ -429,6 +429,7 @@ Clang-Tidy Checks :doc:`readability-static-definition-in-anonymous-namespace <readability/static-definition-in-anonymous-namespace>`, "Yes" :doc:`readability-string-compare <readability/string-compare>`, "Yes" :doc:`readability-suspicious-call-argument <readability/suspicious-call-argument>`, + :doc:`readability-trailing-comma <readability/trailing-comma>`, "Yes" :doc:`readability-uniqueptr-delete-release <readability/uniqueptr-delete-release>`, "Yes" :doc:`readability-uppercase-literal-suffix <readability/uppercase-literal-suffix>`, "Yes" :doc:`readability-use-anyofallof <readability/use-anyofallof>`, diff --git a/clang-tools-extra/docs/clang-tidy/checks/readability/trailing-comma.rst b/clang-tools-extra/docs/clang-tidy/checks/readability/trailing-comma.rst new file mode 100644 index 0000000000000..83e05fb1bead6 --- /dev/null +++ b/clang-tools-extra/docs/clang-tidy/checks/readability/trailing-comma.rst @@ -0,0 +1,68 @@ +.. title:: clang-tidy - readability-trailing-comma + +readability-trailing-comma +========================== + +Checks for presence or absence of trailing commas in enum definitions and +initializer lists. + +The check supports separate policies for single-line and multi-line constructs, +allowing diff erent styles for each. By default, the check enforces trailing +commas in multi-line constructs and removes them from single-line constructs. + +Trailing commas in multi-line constructs offer several benefits: + +- Adding or removing elements at the end only changes a single line, making + diff s smaller and easier to read. +- Formatters may change code to a more desired style. +- Code generators avoid the need for special handling of the last element. + +.. code-block:: c++ + + // Without trailing commas - adding "Yellow" requires modifying the "Blue" line + enum Color { + Red, + Green, + Blue + }; + + // With trailing commas - adding "Yellow" is a clean, single-line change + enum Color { + Red, + Green, + Blue, + }; + + +Limitations +----------- + +The check currently doesn't analyze code inside macros. + + +Options +------- + +.. option:: SingleLineCommaPolicy + + Controls whether to add, remove, or ignore trailing commas in single-line + enum definitions and initializer lists. + Valid values are: + + - `Append`: Add trailing commas where missing. + - `Remove`: Remove trailing commas where present. + - `Ignore`: Do not check single-line constructs. + + Default is `Remove`. + +.. option:: MultiLineCommaPolicy + + Controls whether to add, remove, or ignore trailing commas in multi-line + enum definitions and initializer lists. + Valid values are: + + - `Append`: Add trailing commas where missing. + - `Remove`: Remove trailing commas where present. + - `Ignore`: Do not check multi-line constructs. + + Default is `Append`. diff --git a/clang-tools-extra/test/clang-tidy/checkers/readability/trailing-comma-cxx11.cpp b/clang-tools-extra/test/clang-tidy/checkers/readability/trailing-comma-cxx11.cpp new file mode 100644 index 0000000000000..9f37db2c837c3 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/readability/trailing-comma-cxx11.cpp @@ -0,0 +1,56 @@ +// RUN: %check_clang_tidy -std=c++11-or-later %s readability-trailing-comma %t + +enum class SingleLine2 { X1, Y1 = 1, }; +// CHECK-MESSAGES: :[[@LINE-1]]:36: warning: enum should not have a trailing comma +// CHECK-FIXES: enum class SingleLine2 { X1, Y1 = 1 }; + +enum EnumWithAttrs { + E1 [[deprecated]] = 1, + E2 [[deprecated]] +}; +// CHECK-MESSAGES: :[[@LINE-2]]:20: warning: enum should have a trailing comma +// CHECK-FIXES: enum EnumWithAttrs { +// CHECK-FIXES-NEXT: E1 {{\[\[}}deprecated{{\]\]}} = 1, +// CHECK-FIXES-NEXT: E2 {{\[\[}}deprecated{{\]\]}}, +// CHECK-FIXES-NEXT: }; + +enum EnumWithAttrs2 { + E3 [[deprecated]] = 1, + E4 [[deprecated]] = 2 +}; +// CHECK-MESSAGES: :[[@LINE-2]]:24: warning: enum should have a trailing comma +// CHECK-FIXES: enum EnumWithAttrs2 { +// CHECK-FIXES-NEXT: E3 {{\[\[}}deprecated{{\]\]}} = 1, +// CHECK-FIXES-NEXT: E4 {{\[\[}}deprecated{{\]\]}} = 2, +// CHECK-FIXES-NEXT: }; + +enum EnumWithAttrsTrailing { + E5 [[deprecated]] = 1, + E6 [[deprecated]], +}; + +enum SingleLineAttrs { E7 [[deprecated]], E8 [[deprecated]] [[deprecated ]] , }; +// CHECK-MESSAGES: :[[@LINE-1]]:78: warning: enum should not have a trailing comma +// CHECK-FIXES: enum SingleLineAttrs { E7 {{\[\[}}deprecated{{\]\]}}, E8 {{\[\[}}deprecated{{\]\]}} {{\[\[}}deprecated {{\]\]}} }; + +enum SingleLineAttrs2 { E9 [[deprecated]], E10 [[deprecated]] = 1, }; +// CHECK-MESSAGES: :[[@LINE-1]]:66: warning: enum should not have a trailing comma +// CHECK-FIXES: enum SingleLineAttrs2 { E9 {{\[\[}}deprecated{{\]\]}}, E10 {{\[\[}}deprecated{{\]\]}} = 1 }; + +// Template pack expansions - no warnings +template <typename T, typename... Ts> +struct Pack { + int values[sizeof...(Ts) + 1] = {sizeof(T), sizeof(Ts)...}; +}; + +Pack<int> one; +Pack<int, double> two; +Pack<int, double, char> three; + +template <typename... Ts> +struct PackSingle { + int values[sizeof...(Ts)] = {sizeof(Ts)...}; +}; + +PackSingle<int> p1; +PackSingle<int, double, char> p3; diff --git a/clang-tools-extra/test/clang-tidy/checkers/readability/trailing-comma-cxx20-remove.cpp b/clang-tools-extra/test/clang-tidy/checkers/readability/trailing-comma-cxx20-remove.cpp new file mode 100644 index 0000000000000..6c18678f88fec --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/readability/trailing-comma-cxx20-remove.cpp @@ -0,0 +1,64 @@ +// RUN: %check_clang_tidy -std=c++20-or-later %s readability-trailing-comma %t -- \ +// RUN: -config='{CheckOptions: {readability-trailing-comma.SingleLineCommaPolicy: Remove, readability-trailing-comma.MultiLineCommaPolicy: Remove}}' + +struct S { int x, y; }; + +void f() { + S s1 = { + .x = 1, + .y = 2, + }; + // CHECK-MESSAGES: :[[@LINE-2]]:11: warning: initializer list should not have a trailing comma + // CHECK-FIXES: S s1 = { + // CHECK-FIXES-NEXT: .x = 1, + // CHECK-FIXES-NEXT: .y = 2 + // CHECK-FIXES-NEXT: }; + + int a[3] = { + [0] = 1, + }; + // CHECK-MESSAGES: :[[@LINE-2]]:12: warning: initializer list should not have a trailing comma + // CHECK-FIXES: int a[3] = { + // CHECK-FIXES-NEXT: [0] = 1 + // CHECK-FIXES-NEXT: }; + + S s2 = {.x = 1, .y = 2,}; + // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: initializer list should not have a trailing comma + // CHECK-FIXES: S s2 = {.x = 1, .y = 2}; + + S s3 = { + .x = 1, + .y = 2 + }; +} + +struct N { S a, b; }; + +void nested() { + N n1 = {.a = {.x = 1, .y = 2}, .b = {.x = 3, .y = 4,},}; + // CHECK-MESSAGES: :[[@LINE-1]]:54: warning: initializer list should not have a trailing comma + // CHECK-MESSAGES: :[[@LINE-2]]:56: warning: initializer list should not have a trailing comma + // CHECK-FIXES: N n1 = {.a = {.x = 1, .y = 2}, .b = {.x = 3, .y = 4}}; + + N n2 = { + .a = {.x = 1, .y = 2}, + .b = {.x = 3, .y = 4} + }; +} + +struct WithArray { + int values[3]; + int count; +}; + +void with_array() { + WithArray w2 = {.values = {1, 2, 3,}, .count = 3,}; + // CHECK-MESSAGES: :[[@LINE-1]]:37: warning: initializer list should not have a trailing comma + // CHECK-MESSAGES: :[[@LINE-2]]:51: warning: initializer list should not have a trailing comma + // CHECK-FIXES: WithArray w2 = {.values = {1, 2, 3}, .count = 3}; + + WithArray w3 = { + .values = {1, 2, 3}, + .count = 3 + }; +} diff --git a/clang-tools-extra/test/clang-tidy/checkers/readability/trailing-comma-cxx20.cpp b/clang-tools-extra/test/clang-tidy/checkers/readability/trailing-comma-cxx20.cpp new file mode 100644 index 0000000000000..b2f6ed072563f --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/readability/trailing-comma-cxx20.cpp @@ -0,0 +1,89 @@ +// RUN: %check_clang_tidy -std=c++20-or-later %s readability-trailing-comma %t + +struct S { int x, y; }; + +void f() { + S s1 = { + .x = 1, + .y = 2 + }; + // CHECK-MESSAGES: :[[@LINE-2]]:11: warning: initializer list should have a trailing comma + // CHECK-FIXES: S s1 = { + // CHECK-FIXES-NEXT: .x = 1, + // CHECK-FIXES-NEXT: .y = 2, + // CHECK-FIXES-NEXT: }; + + int a[3] = { + [0] = 1 + }; + // CHECK-MESSAGES: :[[@LINE-2]]:12: warning: initializer list should have a trailing comma + // CHECK-FIXES: int a[3] = { + // CHECK-FIXES-NEXT: [0] = 1, + // CHECK-FIXES-NEXT: }; + + S s2 = {.x = 1, .y = 2}; + S s3 = {.x = 1}; + + S s4 = { + .x = 1, + }; +} + +struct N { S a, b; }; + +void nested() { + N n = { + .a = {.x = 1, .y = 2}, + .b = { + .x = 3, + .y = 4 + } + }; + // CHECK-MESSAGES: :[[@LINE-3]]:13: warning: initializer list should have a trailing comma + // CHECK-MESSAGES: :[[@LINE-3]]:6: warning: initializer list should have a trailing comma + // CHECK-FIXES: N n = { + // CHECK-FIXES-NEXT: .a = {.x = 1, .y = 2}, + // CHECK-FIXES-NEXT: .b = { + // CHECK-FIXES-NEXT: .x = 3, + // CHECK-FIXES-NEXT: .y = 4, + // CHECK-FIXES-NEXT: }, + // CHECK-FIXES-NEXT: }; + + N n2 = {.a = {.x = 1, .y = 2}, .b = {.x = 3, .y = 4}}; + + N n3 = { + .a = {.x = 1, .y = 2}, + .b = { + .x = 3, + .y = 4, + }, + }; +} + +struct WithArray { + int values[3]; + int count; +}; + +void with_array() { + WithArray w1 = { + .values = {1, 2, + 3 + }, + .count = 3 + }; + // CHECK-MESSAGES: :[[@LINE-4]]:8: warning: initializer list should have a trailing comma + // CHECK-MESSAGES: :[[@LINE-3]]:15: warning: initializer list should have a trailing comma + // CHECK-FIXES: WithArray w1 = { + // CHECK-FIXES-NEXT: .values = {1, 2, + // CHECK-FIXES-NEXT: 3, + // CHECK-FIXES-NEXT: }, + // CHECK-FIXES-NEXT: .count = 3, + // CHECK-FIXES-NEXT: }; + + WithArray w2 = {.values = {1, 2, 3}, .count = 3}; + WithArray w3 = { + .values = {1, 2, 3}, + .count = 3, + }; +} diff --git a/clang-tools-extra/test/clang-tidy/checkers/readability/trailing-comma-remove.cpp b/clang-tools-extra/test/clang-tidy/checkers/readability/trailing-comma-remove.cpp new file mode 100644 index 0000000000000..fadce1808876f --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/readability/trailing-comma-remove.cpp @@ -0,0 +1,79 @@ +// RUN: %check_clang_tidy -std=c++98-or-later %s readability-trailing-comma %t -- \ +// RUN: -config='{CheckOptions: {readability-trailing-comma.SingleLineCommaPolicy: Remove, readability-trailing-comma.MultiLineCommaPolicy: Remove}}' + +struct S { int x, y; }; + +enum E1 { + A, + B, + C, +}; +// CHECK-MESSAGES: :[[@LINE-2]]:4: warning: enum should not have a trailing comma [readability-trailing-comma] +// CHECK-FIXES: enum E1 { +// CHECK-FIXES-NEXT: A, +// CHECK-FIXES-NEXT: B, +// CHECK-FIXES-NEXT: C +// CHECK-FIXES-NEXT: }; + +enum E2 { + V = 1, +}; +// CHECK-MESSAGES: :[[@LINE-2]]:8: warning: enum should not have a trailing comma +// CHECK-FIXES: enum E2 { +// CHECK-FIXES-NEXT: V = 1 +// CHECK-FIXES-NEXT: }; + +enum SingleLine { A1, B1, C1, }; +// CHECK-MESSAGES: :[[@LINE-1]]:29: warning: enum should not have a trailing comma +// CHECK-FIXES: enum SingleLine { A1, B1, C1 }; + +enum E3 { + P, + Q +}; +enum Empty {}; + +void f() { + // Multi-line init lists with trailing commas - should warn to remove + int a[] = { + 1, + 2, + }; + // CHECK-MESSAGES: :[[@LINE-2]]:6: warning: initializer list should not have a trailing comma + // CHECK-FIXES: int a[] = { + // CHECK-FIXES-NEXT: 1, + // CHECK-FIXES-NEXT: 2 + // CHECK-FIXES-NEXT: }; + + S s = { + 1, + 2, + }; + // CHECK-MESSAGES: :[[@LINE-2]]:6: warning: initializer list should not have a trailing comma + // CHECK-FIXES: S s = { + // CHECK-FIXES-NEXT: 1, + // CHECK-FIXES-NEXT: 2 + // CHECK-FIXES-NEXT: }; + + int b[] = {1, 2, 3,}; + // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: initializer list should not have a trailing comma + // CHECK-FIXES: int b[] = {1, 2, 3}; + S s2 = {1, 2,}; + // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: initializer list should not have a trailing comma + // CHECK-FIXES: S s2 = {1, 2}; + + int c[] = { + 1, + 2 + }; + int d[] = {}; +} + +struct N { S a, b; }; +void nested() { + N n = {{3, 4,},}; + // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: initializer list should not have a trailing comma + // CHECK-MESSAGES: :[[@LINE-2]]:17: warning: initializer list should not have a trailing comma + // CHECK-FIXES: N n = {{[{][{]3, 4[}][}]}}; + N n2 = {{3, 4}}; +} diff --git a/clang-tools-extra/test/clang-tidy/checkers/readability/trailing-comma-wrong-config.cpp b/clang-tools-extra/test/clang-tidy/checkers/readability/trailing-comma-wrong-config.cpp new file mode 100644 index 0000000000000..a3ce4e70d426a --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/readability/trailing-comma-wrong-config.cpp @@ -0,0 +1,11 @@ +// RUN: %check_clang_tidy -std=c++98-or-later %s readability-trailing-comma %t -- \ +// RUN: -config='{CheckOptions: {readability-trailing-comma.SingleLineCommaPolicy: Ignore, readability-trailing-comma.MultiLineCommaPolicy: Ignore}}' + + +// CHECK-MESSAGES: warning: The check 'readability-trailing-comma' will not perform any analysis because 'SingleLineCommaPolicy' and 'MultiLineCommaPolicy' are both set to 'Ignore'. [clang-tidy-config] + +enum E1 { + A +}; + +enum E2 { B, }; diff --git a/clang-tools-extra/test/clang-tidy/checkers/readability/trailing-comma.c b/clang-tools-extra/test/clang-tidy/checkers/readability/trailing-comma.c new file mode 100644 index 0000000000000..bdf9912e54155 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/readability/trailing-comma.c @@ -0,0 +1,117 @@ +// RUN: %check_clang_tidy %s readability-trailing-comma %t + +enum Color { + Red, + Green, + Blue +}; +// CHECK-MESSAGES: :[[@LINE-2]]:7: warning: enum should have a trailing comma +// CHECK-FIXES: enum Color { +// CHECK-FIXES-NEXT: Red, +// CHECK-FIXES-NEXT: Green, +// CHECK-FIXES-NEXT: Blue, +// CHECK-FIXES-NEXT: }; + +enum SingleLine { A, B, C }; +enum SingleLine2 { X1, Y1, }; +// CHECK-MESSAGES: :[[@LINE-1]]:26: warning: enum should not have a trailing comma +// CHECK-FIXES: enum SingleLine2 { X1, Y1 }; +enum SingleVal { VAL1 }; +enum SingleVal2 { VAL2, }; +// CHECK-MESSAGES: :[[@LINE-1]]:23: warning: enum should not have a trailing comma +// CHECK-FIXES: enum SingleVal2 { VAL2 }; +enum SingleVal3 { + VAL3 +}; +// CHECK-MESSAGES: :[[@LINE-2]]:7: warning: enum should have a trailing comma +// CHECK-FIXES: enum SingleVal3 { +// CHECK-FIXES-NEXT: VAL3, +// CHECK-FIXES-NEXT: }; + +struct Point { + int x; + int y; +}; + +struct Point p = { + 10, + 20 +}; +// CHECK-MESSAGES: :[[@LINE-2]]:5: warning: initializer list should have a trailing comma +// CHECK-FIXES: struct Point p = { +// CHECK-FIXES-NEXT: 10, +// CHECK-FIXES-NEXT: 20, +// CHECK-FIXES-NEXT: }; + +struct Point p2 = { + .x = 1, + .y = 2 +}; +// CHECK-MESSAGES: :[[@LINE-2]]:9: warning: initializer list should have a trailing comma +// CHECK-FIXES: struct Point p2 = { +// CHECK-FIXES-NEXT: .x = 1, +// CHECK-FIXES-NEXT: .y = 2, +// CHECK-FIXES-NEXT: }; + +int arr2[5] = { + [0] = 1 +}; +// CHECK-MESSAGES: :[[@LINE-2]]:10: warning: initializer list should have a trailing comma +// CHECK-FIXES: int arr2[5] = { +// CHECK-FIXES-NEXT: [0] = 1, +// CHECK-FIXES-NEXT: }; + +int multiArr[] = { + 1 +}; +// CHECK-MESSAGES: :[[@LINE-2]]:4: warning: initializer list should have a trailing comma +// CHECK-FIXES: int multiArr[] = { +// CHECK-FIXES-NEXT: 1, +// CHECK-FIXES-NEXT: }; + +int arr[] = {1, 2, 3}; +struct Point p3 = {10, 20}; +struct Point p4 = {.x = 1, .y = 2}; +int matrix[2][2] = {{1, 2}, {3, 4}}; +int single1[1] = {42}; +int single2[1] = {42,}; +// CHECK-MESSAGES: :[[@LINE-1]]:21: warning: initializer list should not have a trailing comma +// CHECK-FIXES: int single2[1] = {42}; +int single3[1] = { + 42 +}; +// CHECK-MESSAGES: :[[@LINE-2]]:5: warning: initializer list should have a trailing comma +// CHECK-FIXES: int single3[1] = { +// CHECK-FIXES-NEXT: 42, +// CHECK-FIXES-NEXT: }; +int single4[1] = { + 42, +}; +int empty1[] = {}; +struct Nested { int a[2]; int b; }; +struct Nested nest1 = {{1}, 2}; +struct Nested nest2 = {{1,}, 2}; +// CHECK-MESSAGES: :[[@LINE-1]]:26: warning: initializer list should not have a trailing comma +// CHECK-FIXES: struct Nested nest2 = {{[{][{]}}1}, 2}; +struct Nested nest3 = { + {1}, + 2 +}; +// CHECK-MESSAGES: :[[@LINE-2]]:4: warning: initializer list should have a trailing comma +// CHECK-FIXES: struct Nested nest3 = { +// CHECK-FIXES-NEXT: {1}, +// CHECK-FIXES-NEXT: 2, +// CHECK-FIXES-NEXT: }; + +struct Point singleDesig1 = {.x = 10}; +struct Point singleDesig2 = {.x = 10,}; +// CHECK-MESSAGES: :[[@LINE-1]]:37: warning: initializer list should not have a trailing comma +// CHECK-FIXES: struct Point singleDesig2 = {.x = 10}; +struct Point singleDesig3 = {}; +struct Point singleDesig4 = { + .x = 10 +}; +// CHECK-MESSAGES: :[[@LINE-2]]:10: warning: initializer list should have a trailing comma +// CHECK-FIXES: struct Point singleDesig4 = { +// CHECK-FIXES-NEXT: .x = 10, +// CHECK-FIXES-NEXT: }; diff --git a/clang-tools-extra/test/clang-tidy/checkers/readability/trailing-comma.cpp b/clang-tools-extra/test/clang-tidy/checkers/readability/trailing-comma.cpp new file mode 100644 index 0000000000000..76fb4bbf0c37d --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/readability/trailing-comma.cpp @@ -0,0 +1,161 @@ +// RUN: %check_clang_tidy -std=c++98-or-later %s readability-trailing-comma %t + +struct S { int x, y; }; + +enum E1 { + A, + B, + C +}; +// CHECK-MESSAGES: :[[@LINE-2]]:4: warning: enum should have a trailing comma [readability-trailing-comma] +// CHECK-FIXES: enum E1 { +// CHECK-FIXES-NEXT: A, +// CHECK-FIXES-NEXT: B, +// CHECK-FIXES-NEXT: C, +// CHECK-FIXES-NEXT: }; + +enum E2 { + V = 1 +}; +// CHECK-MESSAGES: :[[@LINE-2]]:8: warning: enum should have a trailing comma +// CHECK-FIXES: enum E2 { +// CHECK-FIXES-NEXT: V = 1, +// CHECK-FIXES-NEXT: }; + +enum SingleLine { A1, B1, C1 }; + +enum E3 { + P, + Q, +}; + +enum SingleEnum1 { ONE }; +enum SingleEnum2 { TWO, }; +// CHECK-MESSAGES: :[[@LINE-1]]:23: warning: enum should not have a trailing comma +// CHECK-FIXES: enum SingleEnum2 { TWO }; +enum SingleEnum3 { + THREE +}; +// CHECK-MESSAGES: :[[@LINE-2]]:8: warning: enum should have a trailing comma +// CHECK-FIXES: enum SingleEnum3 { +// CHECK-FIXES-NEXT: THREE, +// CHECK-FIXES-NEXT: }; +enum SingleEnum4 { + FOUR, +}; + +enum Empty {}; + +void f() { + int a[] = { + 1 + }; + // CHECK-MESSAGES: :[[@LINE-2]]:6: warning: initializer list should have a trailing comma + // CHECK-FIXES: int a[] = { + // CHECK-FIXES-NEXT: 1, + // CHECK-FIXES-NEXT: }; + + S s = { + 1, + 2 + }; + // CHECK-MESSAGES: :[[@LINE-2]]:6: warning: initializer list should have a trailing comma + // CHECK-FIXES: S s = { + // CHECK-FIXES-NEXT: 1, + // CHECK-FIXES-NEXT: 2, + // CHECK-FIXES-NEXT: }; + + int b[] = {1, 2, 3}; + S s2 = {1, 2}; + + int e[] = {1, 2, 3,}; + // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: initializer list should not have a trailing comma + // CHECK-FIXES: int e[] = {1, 2, 3}; + + int c[] = { + 1, + 2, + }; + int d[] = {}; + + int single1[] = {1}; + int single2[] = {1,}; + // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: initializer list should not have a trailing comma + // CHECK-FIXES: int single2[] = {1}; + int single3[] = { + 1 + }; + // CHECK-MESSAGES: :[[@LINE-2]]:6: warning: initializer list should have a trailing comma + // CHECK-FIXES: int single3[] = { + // CHECK-FIXES-NEXT: 1, + // CHECK-FIXES-NEXT: }; + int single4[] = { + 1, + }; + S singleS1 = {42}; + S singleS2 = {42,}; + // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: initializer list should not have a trailing comma + // CHECK-FIXES: S singleS2 = {42}; +} + +struct N { S a, b; }; +void nested() { + N n = {{1, 2}, {3, 4}}; + N n2 = {{3, 4,},}; + // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: initializer list should not have a trailing comma + // CHECK-MESSAGES: :[[@LINE-2]]:18: warning: initializer list should not have a trailing comma + // CHECK-FIXES: N n2 = {{[{][{]3, 4[}][}]}}; +} + +void nestedMultiLine() { + N n = { + {1, 2}, + {3, 4} + }; + // CHECK-MESSAGES: :[[@LINE-2]]:11: warning: initializer list should have a trailing comma + // CHECK-FIXES: N n = { + // CHECK-FIXES-NEXT: {1, 2}, + // CHECK-FIXES-NEXT: {3, 4}, + // CHECK-FIXES-NEXT: }; + + N n2 = { + {1, 2}, + {3, 4}, + }; + + struct Container { int arr[3]; }; + Container c1 = {{}}; + Container c2 = {{},}; + // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: initializer list should not have a trailing comma + // CHECK-FIXES: Container c2 = {{[{][{][}][}]}}; + + struct Wrapper { S s; }; + Wrapper w1 = {{1}}; + Wrapper w2 = {{1,}}; + // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: initializer list should not have a trailing comma + // CHECK-FIXES: Wrapper w2 = {{[{][{]1[}][}]}}; + + Wrapper w3 = { + {1} + }; + // CHECK-MESSAGES: :[[@LINE-2]]:8: warning: initializer list should have a trailing comma + // CHECK-FIXES: Wrapper w3 = { + // CHECK-FIXES-NEXT: {1}, + // CHECK-FIXES-NEXT: }; +} + +// Macros are ignored +#define ENUM(n, a, b) enum n { a, b } +#define INIT {1, 2} +#define ITEMS 1,2 + +ENUM(E1M, Xm, Ym); +int macroArr[] = INIT; +int a[] = { ITEMS }; + +// Comma from macro should not trigger false positive +#define COMMA , +int comma_from_macro[] = { + 1 + COMMA +}; _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
