Author: Katze719 Date: 2026-08-17T20:53:52Z New Revision: 3ec64dd5420f57abc780efa1bc935cfda02f6f8b
URL: https://github.com/llvm/llvm-project/commit/3ec64dd5420f57abc780efa1bc935cfda02f6f8b DIFF: https://github.com/llvm/llvm-project/commit/3ec64dd5420f57abc780efa1bc935cfda02f6f8b.diff LOG: [clang-format] Add support for additional C++ declaration specifiers in QualifierOrder (#160853) Fixes #60866 This PR extends clang-formats `QualifierOrder` option to support additional C++ declaration specifiers, addressing the limitation where many common qualifiers were not recognized. ## Problem Previously, `QualifierOrder` only supported a limited subset of C++ declaration specifiers: - `const`, `volatile`, `static`, `inline`, `constexpr`, `restrict`, `friend` This meant that many common C++ qualifiers like `extern`, `mutable`, `typedef`, `unsigned`, etc. were not recognized and could not be properly reordered, limiting the usefulness of the feature. ## Solution This PR adds support for 11 additional declaration specifiers: **C++98/C99 specifiers:** - `typedef` - Type definition specifier - `extern` - External linkage specifier - `mutable` - Mutable member specifier - `signed`, `unsigned` - Integer signedness specifiers - `long`, `short` - Integer size specifiers **C++11 specifiers:** - `thread_local` - Thread-local storage duration specifier - `explicit` - Explicit constructor/conversion specifier **C++20 specifiers:** - `consteval` - Immediate function specifier - `constinit` - Constant initialization specifier Added: Modified: clang/docs/ClangFormatStyleOptions.md clang/docs/ReleaseNotes.md clang/include/clang/Format/Format.h clang/lib/Format/QualifierAlignmentFixer.cpp clang/unittests/Format/QualifierFixerTest.cpp Removed: ################################################################################ diff --git a/clang/docs/ClangFormatStyleOptions.md b/clang/docs/ClangFormatStyleOptions.md index 9b962e6e1e083..8230e6f5139e7 100644 --- a/clang/docs/ClangFormatStyleOptions.md +++ b/clang/docs/ClangFormatStyleOptions.md @@ -6393,6 +6393,17 @@ the configuration (without a prefix: `Auto`). - `constexpr` - `volatile` - `restrict` + - `typedef` + - `consteval` + - `constinit` + - `thread_local` + - `extern` + - `mutable` + - `signed` + - `unsigned` + - `long` + - `short` + - `explicit` - `type` :::{note} @@ -6402,6 +6413,9 @@ the configuration (without a prefix: `Auto`). Items to the left of `type` will be placed to the left of the type and aligned in the order supplied. Items to the right of `type` will be placed to the right of the type and aligned in the order supplied. + If only one of `signed` and `unsigned` is specified, both are placed at + that position. The same applies to `long` and `short`. Specifying both + members of a pair allows them to be placed independently. ```yaml QualifierOrder: [inline, static, type, const, volatile] diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md index 0e40754efd30e..272227b1cdf1e 100644 --- a/clang/docs/ReleaseNotes.md +++ b/clang/docs/ReleaseNotes.md @@ -595,6 +595,10 @@ features cannot lower the translation-unit ABI level; - Add `SpacesInBlockComments` option to control spacing after `/*` and before `*/` in ordinary block comments. +- `QualifierOrder` now supports `typedef`, `consteval`, `constinit`, + `thread_local`, `extern`, `mutable`, `signed`, `unsigned`, `long`, `short`, + and `explicit` declaration specifiers. + ### libclang - visit identifier initializers in lambda capture as VarDecl instead of VariableRef. Warning: this changes behaviour. diff --git a/clang/include/clang/Format/Format.h b/clang/include/clang/Format/Format.h index 3948337d2fc3d..49e4666ae5e99 100644 --- a/clang/include/clang/Format/Format.h +++ b/clang/include/clang/Format/Format.h @@ -4565,6 +4565,17 @@ struct FormatStyle { /// * `constexpr` /// * `volatile` /// * `restrict` + /// * `typedef` + /// * `consteval` + /// * `constinit` + /// * `thread_local` + /// * `extern` + /// * `mutable` + /// * `signed` + /// * `unsigned` + /// * `long` + /// * `short` + /// * `explicit` /// * `type` /// /// \note @@ -4574,6 +4585,9 @@ struct FormatStyle { /// Items to the left of `type` will be placed to the left of the type and /// aligned in the order supplied. Items to the right of `type` will be /// placed to the right of the type and aligned in the order supplied. + /// If only one of `signed` and `unsigned` is specified, both are placed at + /// that position. The same applies to `long` and `short`. Specifying both + /// members of a pair allows them to be placed independently. /// /// \code{.yaml} /// QualifierOrder: [inline, static, type, const, volatile] diff --git a/clang/lib/Format/QualifierAlignmentFixer.cpp b/clang/lib/Format/QualifierAlignmentFixer.cpp index 3d1a9dc868208..29c583855e3f5 100644 --- a/clang/lib/Format/QualifierAlignmentFixer.cpp +++ b/clang/lib/Format/QualifierAlignmentFixer.cpp @@ -30,24 +30,28 @@ void addQualifierAlignmentFixerPasses(const FormatStyle &Style, prepareLeftRightOrderingForQualifierAlignmentFixer( Style.QualifierOrder, LeftOrder, RightOrder, ConfiguredQualifierTokens); + const auto AddPass = [&](const std::string &Qualifier, bool RightAlign) { + Passes.emplace_back([&, Qualifier, ConfiguredQualifierTokens, + RightAlign](const Environment &Env) { + return LeftRightQualifierAlignmentFixer( + Env, Style, Qualifier, ConfiguredQualifierTokens, RightAlign) + .process(); + }); + }; + // Handle the left and right alignment separately. for (const auto &Qualifier : LeftOrder) { - Passes.emplace_back( - [&, Qualifier, ConfiguredQualifierTokens](const Environment &Env) { - return LeftRightQualifierAlignmentFixer(Env, Style, Qualifier, - ConfiguredQualifierTokens, - /*RightAlign=*/false) - .process(); - }); + AddPass(Qualifier, /*RightAlign=*/false); + // Unlike the other declaration specifiers, `long` can legally occur twice + // in the same sequence. A pass moves one occurrence across the type, so a + // second pass is needed for `long long` and is otherwise a no-op. + if (Qualifier == "long") + AddPass(Qualifier, /*RightAlign=*/false); } for (const auto &Qualifier : RightOrder) { - Passes.emplace_back( - [&, Qualifier, ConfiguredQualifierTokens](const Environment &Env) { - return LeftRightQualifierAlignmentFixer(Env, Style, Qualifier, - ConfiguredQualifierTokens, - /*RightAlign=*/true) - .process(); - }); + AddPass(Qualifier, /*RightAlign=*/true); + if (Qualifier == "long") + AddPass(Qualifier, /*RightAlign=*/true); } } @@ -175,6 +179,13 @@ static bool isQualifier(const FormatToken *const Tok) { case tok::kw___ptr32: case tok::kw___ptr64: case tok::kw___funcref: + case tok::kw_typedef: + case tok::kw_consteval: + case tok::kw_constinit: + case tok::kw_thread_local: + case tok::kw_extern: + case tok::kw_mutable: + case tok::kw_explicit: return true; default: return false; @@ -544,6 +555,17 @@ tok::TokenKind LeftRightQualifierAlignmentFixer::getTokenFromQualifier( .Case("constexpr", tok::kw_constexpr) .Case("restrict", tok::kw_restrict) .Case("friend", tok::kw_friend) + .Case("typedef", tok::kw_typedef) + .Case("consteval", tok::kw_consteval) + .Case("constinit", tok::kw_constinit) + .Case("thread_local", tok::kw_thread_local) + .Case("extern", tok::kw_extern) + .Case("mutable", tok::kw_mutable) + .Case("signed", tok::kw_signed) + .Case("unsigned", tok::kw_unsigned) + .Case("long", tok::kw_long) + .Case("short", tok::kw_short) + .Case("explicit", tok::kw_explicit) .Default(tok::identifier); } @@ -623,9 +645,34 @@ void prepareLeftRightOrderingForQualifierAlignmentFixer( tok::TokenKind QualifierToken = LeftRightQualifierAlignmentFixer::getTokenFromQualifier(s); - if (QualifierToken != tok::kw_typeof && QualifierToken != tok::identifier) + if (QualifierToken != tok::kw_typeof && QualifierToken != tok::identifier) { Qualifiers.push_back(QualifierToken); + // Ensure signed/unsigned and long/short qualifier pairs are positioned + // together by default unless the user has explicitly specified both in + // the QualifierOrder. This allows users to override the default pairing + // by listing both qualifiers in the order. + auto AddPairedQualifier = [&](tok::TokenKind PairedToken, + const std::string &PairedName) { + if (!llvm::is_contained(Order, PairedName)) { + Qualifiers.push_back(PairedToken); + if (left) + LeftOrder.insert(LeftOrder.begin(), PairedName); + else + RightOrder.push_back(PairedName); + } + }; + + if (QualifierToken == tok::kw_unsigned) + AddPairedQualifier(tok::kw_signed, "signed"); + else if (QualifierToken == tok::kw_signed) + AddPairedQualifier(tok::kw_unsigned, "unsigned"); + else if (QualifierToken == tok::kw_long) + AddPairedQualifier(tok::kw_short, "short"); + else if (QualifierToken == tok::kw_short) + AddPairedQualifier(tok::kw_long, "long"); + } + if (left) { // Reverse the order for left aligned items. LeftOrder.insert(LeftOrder.begin(), s); diff --git a/clang/unittests/Format/QualifierFixerTest.cpp b/clang/unittests/Format/QualifierFixerTest.cpp index edde7bb3b5c97..87fca83a3d5be 100644 --- a/clang/unittests/Format/QualifierFixerTest.cpp +++ b/clang/unittests/Format/QualifierFixerTest.cpp @@ -50,6 +50,31 @@ TEST_F(QualifierFixerTest, RotateTokens) { tok::kw_restrict); EXPECT_EQ(LeftRightQualifierAlignmentFixer::getTokenFromQualifier("friend"), tok::kw_friend); + EXPECT_EQ(LeftRightQualifierAlignmentFixer::getTokenFromQualifier("typedef"), + tok::kw_typedef); + EXPECT_EQ( + LeftRightQualifierAlignmentFixer::getTokenFromQualifier("consteval"), + tok::kw_consteval); + EXPECT_EQ( + LeftRightQualifierAlignmentFixer::getTokenFromQualifier("constinit"), + tok::kw_constinit); + EXPECT_EQ( + LeftRightQualifierAlignmentFixer::getTokenFromQualifier("thread_local"), + tok::kw_thread_local); + EXPECT_EQ(LeftRightQualifierAlignmentFixer::getTokenFromQualifier("extern"), + tok::kw_extern); + EXPECT_EQ(LeftRightQualifierAlignmentFixer::getTokenFromQualifier("mutable"), + tok::kw_mutable); + EXPECT_EQ(LeftRightQualifierAlignmentFixer::getTokenFromQualifier("signed"), + tok::kw_signed); + EXPECT_EQ(LeftRightQualifierAlignmentFixer::getTokenFromQualifier("unsigned"), + tok::kw_unsigned); + EXPECT_EQ(LeftRightQualifierAlignmentFixer::getTokenFromQualifier("long"), + tok::kw_long); + EXPECT_EQ(LeftRightQualifierAlignmentFixer::getTokenFromQualifier("short"), + tok::kw_short); + EXPECT_EQ(LeftRightQualifierAlignmentFixer::getTokenFromQualifier("explicit"), + tok::kw_explicit); } TEST_F(QualifierFixerTest, FailQualifierInvalidConfiguration) { @@ -1059,48 +1084,44 @@ TEST_F(QualifierFixerTest, IsQualifierType) { ConfiguredTokens.push_back(tok::kw_restrict); ConfiguredTokens.push_back(tok::kw_constexpr); ConfiguredTokens.push_back(tok::kw_friend); + ConfiguredTokens.push_back(tok::kw_typedef); + ConfiguredTokens.push_back(tok::kw_consteval); + ConfiguredTokens.push_back(tok::kw_constinit); + ConfiguredTokens.push_back(tok::kw_thread_local); + ConfiguredTokens.push_back(tok::kw_extern); + ConfiguredTokens.push_back(tok::kw_mutable); + ConfiguredTokens.push_back(tok::kw_unsigned); + ConfiguredTokens.push_back(tok::kw_long); + ConfiguredTokens.push_back(tok::kw_explicit); + ConfiguredTokens.push_back(tok::kw_signed); + ConfiguredTokens.push_back(tok::kw_short); TestLexer lexer{Allocator, Buffers}; const auto LangOpts = getFormattingLangOpts(); auto Tokens = lexer.lex( - "const static inline auto restrict int double long constexpr friend"); - ASSERT_EQ(Tokens.size(), 11u) << Tokens; - - EXPECT_TRUE( - isConfiguredQualifierOrType(Tokens[0], ConfiguredTokens, LangOpts)); - EXPECT_TRUE( - isConfiguredQualifierOrType(Tokens[1], ConfiguredTokens, LangOpts)); - EXPECT_TRUE( - isConfiguredQualifierOrType(Tokens[2], ConfiguredTokens, LangOpts)); - EXPECT_TRUE( - isConfiguredQualifierOrType(Tokens[3], ConfiguredTokens, LangOpts)); - EXPECT_TRUE( - isConfiguredQualifierOrType(Tokens[4], ConfiguredTokens, LangOpts)); - EXPECT_TRUE( - isConfiguredQualifierOrType(Tokens[5], ConfiguredTokens, LangOpts)); - EXPECT_TRUE( - isConfiguredQualifierOrType(Tokens[6], ConfiguredTokens, LangOpts)); - EXPECT_TRUE( - isConfiguredQualifierOrType(Tokens[7], ConfiguredTokens, LangOpts)); - EXPECT_TRUE( - isConfiguredQualifierOrType(Tokens[8], ConfiguredTokens, LangOpts)); - EXPECT_TRUE( - isConfiguredQualifierOrType(Tokens[9], ConfiguredTokens, LangOpts)); - - EXPECT_TRUE(isQualifierOrType(Tokens[0], LangOpts)); - EXPECT_TRUE(isQualifierOrType(Tokens[1], LangOpts)); - EXPECT_TRUE(isQualifierOrType(Tokens[2], LangOpts)); - EXPECT_TRUE(isQualifierOrType(Tokens[3], LangOpts)); - EXPECT_TRUE(isQualifierOrType(Tokens[4], LangOpts)); - EXPECT_TRUE(isQualifierOrType(Tokens[5], LangOpts)); - EXPECT_TRUE(isQualifierOrType(Tokens[6], LangOpts)); - EXPECT_TRUE(isQualifierOrType(Tokens[7], LangOpts)); - EXPECT_TRUE(isQualifierOrType(Tokens[8], LangOpts)); - EXPECT_TRUE(isQualifierOrType(Tokens[9], LangOpts)); + "const static inline restrict constexpr friend typedef consteval " + "constinit thread_local extern mutable unsigned long explicit signed " + "short"); + ASSERT_EQ(Tokens.size(), ConfiguredTokens.size() + 1) << Tokens; + + for (size_t i = 0; i < ConfiguredTokens.size(); ++i) { + EXPECT_TRUE(isQualifierOrType(Tokens[i], LangOpts)) + << "Token " << i << " should be recognized by isQualifierOrType"; + EXPECT_TRUE( + isConfiguredQualifierOrType(Tokens[i], ConfiguredTokens, LangOpts)) + << "Token " << i << " should be recognized"; + } + + auto TypeTokens = lexer.lex("int double"); + for (size_t i = 0; i < TypeTokens.size() - 1; ++i) { + EXPECT_TRUE(isQualifierOrType(TypeTokens[i], LangOpts)); + EXPECT_TRUE( + isConfiguredQualifierOrType(TypeTokens[i], ConfiguredTokens, LangOpts)); + } auto NotTokens = lexer.lex("for while do Foo Bar "); - ASSERT_EQ(NotTokens.size(), 6u) << Tokens; + ASSERT_EQ(NotTokens.size(), 6u) << NotTokens; EXPECT_FALSE( isConfiguredQualifierOrType(NotTokens[0], ConfiguredTokens, LangOpts)); @@ -1440,6 +1461,108 @@ TEST_F(QualifierFixerTest, TemplatesLeft) { "TemplateType<Container const> t;", Style); } +TEST_F(QualifierFixerTest, NewQualifierSupport) { + FormatStyle Style = getLLVMStyle(); + Style.QualifierAlignment = FormatStyle::QAS_Custom; + + Style.QualifierOrder = {"typedef", "type"}; + verifyFormat("typedef int MyInt;", Style); + + Style.QualifierOrder = {"consteval", "type"}; + verifyFormat("consteval int func();", "int consteval func();", Style); + verifyFormat("consteval void func();", "void consteval func();", Style); + + Style.QualifierOrder = {"constinit", "static", "type"}; + verifyFormat("constinit static int var = 10;", + "static constinit int var = 10;", Style); + + Style.QualifierOrder = {"thread_local", "static", "type"}; + verifyFormat("thread_local static int counter;", + "static thread_local int counter;", Style); + + Style.QualifierOrder = {"extern", "type"}; + verifyFormat("extern int global_var;", "int extern global_var;", Style); + + Style.QualifierOrder = {"mutable", "type"}; + verifyFormat("mutable int cache;", "int mutable cache;", Style); + + Style.QualifierOrder = {"unsigned", "type"}; + verifyFormat("unsigned int num;", "int unsigned num;", Style); + verifyFormat("unsigned long long value;", "long long unsigned value;", Style); + verifyFormat("signed int num;", "int signed num;", Style); + verifyFormat("signed long long value;", "long long signed value;", Style); + + Style.QualifierOrder = {"long", "type"}; + verifyFormat("long int num;", "int long num;", Style); + verifyFormat("long unsigned int num;", "unsigned long int num;", Style); + verifyFormat("long long int num;", "int long long num;", Style); + verifyFormat("long long unsigned int num;", "unsigned long long int num;", + Style); + + Style.QualifierOrder = {"short", "type"}; + verifyFormat("short int num;", "int short num;", Style); + verifyFormat("short unsigned value;", "unsigned short value;", Style); + + Style.QualifierOrder = {"explicit", "type"}; + verifyFormat("explicit Foo(int x);", Style); + + Style.QualifierOrder = {"extern", "thread_local", "static", "constexpr", + "inline", "unsigned", "long", "type", + "const", "volatile"}; + + verifyFormat("extern thread_local static constexpr inline unsigned long int " + "const volatile var;", + "volatile const extern constexpr thread_local static inline " + "unsigned long int var;", + Style); +} + +TEST_F(QualifierFixerTest, PairedQualifiersSamePosition) { + FormatStyle Style = getLLVMStyle(); + Style.QualifierAlignment = FormatStyle::QAS_Custom; + + Style.QualifierOrder = {"unsigned", "type"}; + verifyFormat("unsigned int x;", "int unsigned x;", Style); + verifyFormat("signed int x;", "int signed x;", Style); + verifyFormat("unsigned long int y;", "long unsigned int y;", Style); + verifyFormat("signed long int y;", "long signed int y;", Style); + + Style.QualifierOrder = {"signed", "type"}; + verifyFormat("signed int x;", "int signed x;", Style); + verifyFormat("unsigned int x;", "int unsigned x;", Style); + + Style.QualifierOrder = {"long", "type"}; + verifyFormat("long int x;", "int long x;", Style); + verifyFormat("short int x;", "int short x;", Style); + verifyFormat("long unsigned int y;", "unsigned long int y;", Style); + verifyFormat("short unsigned int y;", "unsigned short int y;", Style); + + Style.QualifierOrder = {"short", "type"}; + verifyFormat("short int x;", "int short x;", Style); + verifyFormat("long int x;", "int long x;", Style); + + Style.QualifierOrder = {"type", "unsigned"}; + verifyFormat("int unsigned x;", "unsigned int x;", Style); + verifyFormat("int signed x;", "signed int x;", Style); + + Style.QualifierOrder = {"type", "long"}; + verifyFormat("int long x;", "long int x;", Style); + verifyFormat("int short x;", "short int x;", Style); + verifyFormat("int long long x;", "long long int x;", Style); + + Style.QualifierOrder = {"static", "unsigned", "long", "type"}; + verifyFormat("static unsigned long int x;", "long unsigned static int x;", + Style); + verifyFormat("static signed short int x;", "short signed static int x;", + Style); + + Style.QualifierOrder = {"unsigned", "signed", "type"}; + verifyFormat("unsigned signed int x;", "signed unsigned int x;", Style); + + Style.QualifierOrder = {"long", "short", "type"}; + verifyFormat("long short int x;", "short long int x;", Style); +} + TEST_F(QualifierFixerTest, Ranges) { FormatStyle Style = getLLVMStyle(); Style.QualifierAlignment = FormatStyle::QAS_Custom; _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
