[clang] [clang-format] Allow array alignment on non-rectangular arrays (PR #143781)
bdunkin wrote: I will gladly add a new option, if that is deemed necessary. I just want to make sure that's the direction I should go. My experience has been it is very hard to take back an option once it has been released, so they should be added deliberately, and with careful consideration. https://github.com/llvm/llvm-project/pull/143781 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang-format] Allow array alignment on non-rectangular arrays (PR #143781)
bdunkin wrote: > You can't change existing tests, you need an option if you want to pad with > spaces It is unclear to me if this should be considered a bug fix, or a new feature. Surely requiring new options are not required for every bug fix. https://github.com/llvm/llvm-project/pull/143781 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang-format] Handle templates in qualified typenames (PR #143194)
https://github.com/bdunkin created https://github.com/llvm/llvm-project/pull/143194 This fixes the `SpaceBeforeParensOptions.AfterFunctionDeclarationName` and `SpaceBeforeParensOptions.AfterFunctionDefinitionName` options not adding spaces when a template type's constructor or destructor is forward declared or defined outside of the type definition. Attribution Note - I have been authorized to contribute this change on behalf of my company: ArenaNet LLC >From 2f262ca8d5060bf58ac23241917bad6d7347c8d3 Mon Sep 17 00:00:00 2001 From: Ben Dunkin Date: Fri, 6 Jun 2025 12:29:13 -0700 Subject: [PATCH] Fix identifiers not being marked as constructor/destructor names if they are qualified by a template type, or have template typename declarations in front of them. --- clang/lib/Format/TokenAnnotator.cpp | 68 +-- clang/unittests/Format/TokenAnnotatorTest.cpp | 42 2 files changed, 106 insertions(+), 4 deletions(-) diff --git a/clang/lib/Format/TokenAnnotator.cpp b/clang/lib/Format/TokenAnnotator.cpp index 37ab40ca97bff..479a8743c5a65 100644 --- a/clang/lib/Format/TokenAnnotator.cpp +++ b/clang/lib/Format/TokenAnnotator.cpp @@ -3622,6 +3622,29 @@ static unsigned maxNestingDepth(const AnnotatedLine &Line) { return Result; } +static bool startsQualifiedName(const FormatToken *Tok) { + // Consider: A::B::B() + // Tok --^ + if (Tok->startsSequence(tok::identifier, tok::coloncolon)) +return true; + + // Consider: A::B::B() + // Tok --^ + if (Tok->startsSequence(tok::identifier, TT_TemplateOpener)) { +Tok = Tok->getNextNonComment(); +assert(Tok); +assert(Tok->is(TT_TemplateOpener)); + +if (!Tok->MatchingParen) + return false; + +return Tok->MatchingParen->startsSequence(TT_TemplateCloser, + tok::coloncolon); + } + + return false; +} + // Returns the name of a function with no return type, e.g. a constructor or // destructor. static FormatToken *getFunctionName(const AnnotatedLine &Line, @@ -3651,6 +3674,21 @@ static FormatToken *getFunctionName(const AnnotatedLine &Line, continue; } +// Skip past template typename declarations that may precede the +// constructor/destructor name +if (Tok->is(tok::kw_template)) { + Tok = Tok->getNextNonComment(); + if (!Tok) +return nullptr; + + assert(Tok->is(TT_TemplateOpener)); + Tok = Tok->MatchingParen; + if (!Tok) +return nullptr; + + continue; +} + // A qualified name may start from the global namespace. if (Tok->is(tok::coloncolon)) { Tok = Tok->Next; @@ -3659,9 +3697,23 @@ static FormatToken *getFunctionName(const AnnotatedLine &Line, } // Skip to the unqualified part of the name. -while (Tok->startsSequence(tok::identifier, tok::coloncolon)) { - assert(Tok->Next); - Tok = Tok->Next->Next; +while (startsQualifiedName(Tok)) { + Tok = Tok->getNextNonComment(); + if (!Tok) +return nullptr; + + // Skip template types if this is a templated type name + if (Tok->is(TT_TemplateOpener)) { +Tok = Tok->MatchingParen; +if (!Tok) + return nullptr; + +Tok = Tok->getNextNonComment(); +if (!Tok) + return nullptr; + } + + Tok = Tok->getNextNonComment(); if (!Tok) return nullptr; } @@ -3691,10 +3743,18 @@ static bool isCtorOrDtorName(const FormatToken *Tok) { if (Prev && Prev->is(tok::tilde)) Prev = Prev->Previous; - if (!Prev || !Prev->endsSequence(tok::coloncolon, tok::identifier)) + // Consider: A::A() and A::A() + if (!Prev || (!Prev->endsSequence(tok::coloncolon, tok::identifier) && +!Prev->endsSequence(tok::coloncolon, TT_TemplateCloser))) { return false; + } assert(Prev->Previous); + if (Prev->Previous->is(TT_TemplateCloser) && Prev->Previous->MatchingParen) { +Prev = Prev->Previous->MatchingParen; +assert(Prev->Previous); + } + return Prev->Previous->TokenText == Tok->TokenText; } diff --git a/clang/unittests/Format/TokenAnnotatorTest.cpp b/clang/unittests/Format/TokenAnnotatorTest.cpp index 9d62ff8d39a77..7e9b9c24f6a3a 100644 --- a/clang/unittests/Format/TokenAnnotatorTest.cpp +++ b/clang/unittests/Format/TokenAnnotatorTest.cpp @@ -2346,6 +2346,48 @@ TEST_F(TokenAnnotatorTest, UnderstandsCtorAndDtorDeclNames) { EXPECT_TOKEN(Tokens[4], tok::l_paren, TT_FunctionDeclarationLParen); EXPECT_TOKEN(Tokens[6], tok::l_brace, TT_FunctionLBrace); + Tokens = annotate("Foo::Foo() {}"); + ASSERT_EQ(Tokens.size(), 11u) << Tokens; + EXPECT_TOKEN(Tokens[5], tok::identifier, TT_CtorDtorDeclName); + EXPECT_TOKEN(Tokens[6], tok::l_paren, TT_FunctionDeclarationLParen); + EXPECT_TOKEN(Tokens[8], tok::l_brace, TT_FunctionLBrace); + + Tokens = annotate("Foo::~Foo() {}"); + ASSERT_EQ(Tokens.size(), 12u) << Tokens; + EXPECT_TOKEN(Tokens
[clang] [clang-format] Fix Microsoft calling convensions preventing function names from being marked TT_StartOfName (PR #143047)
https://github.com/bdunkin created https://github.com/llvm/llvm-project/pull/143047 This fixes the `SpaceBeforeParensOptions.AfterFunctionDeclarationName` and `SpaceBeforeParensOptions.AfterFunctionDefinitionName` options not adding spaces when the function has an explicit Microsoft calling convention. Attribution Note - I have been authorized to contribute this change on behalf of my company: ArenaNet LLC >From 7c3f0f8fb12eae866bc3812f103384a6cacb0dcf Mon Sep 17 00:00:00 2001 From: Ben Dunkin Date: Thu, 5 Jun 2025 16:12:49 -0700 Subject: [PATCH] Fix Microsoft calling convensions preventing function names from being marked TT_StartOfName --- clang/lib/Format/FormatToken.h| 1 + clang/lib/Format/TokenAnnotator.cpp | 15 +++ clang/unittests/Format/FormatTest.cpp | 12 3 files changed, 28 insertions(+) diff --git a/clang/lib/Format/FormatToken.h b/clang/lib/Format/FormatToken.h index 94014aee3221f..ef1c129b29b0c 100644 --- a/clang/lib/Format/FormatToken.h +++ b/clang/lib/Format/FormatToken.h @@ -114,6 +114,7 @@ namespace format { TYPE(LineComment) \ TYPE(MacroBlockBegin) \ TYPE(MacroBlockEnd) \ + TYPE(MicrosoftCallingConvention) \ TYPE(ModulePartitionColon) \ TYPE(NamespaceLBrace) \ TYPE(NamespaceMacro) \ diff --git a/clang/lib/Format/TokenAnnotator.cpp b/clang/lib/Format/TokenAnnotator.cpp index da279d07b5918..4e23478058499 100644 --- a/clang/lib/Format/TokenAnnotator.cpp +++ b/clang/lib/Format/TokenAnnotator.cpp @@ -1802,6 +1802,14 @@ class AnnotatingParser { if (Style.isTableGen() && !parseTableGenValue()) return false; break; +case tok::kw___cdecl: +case tok::kw___stdcall: +case tok::kw___fastcall: +case tok::kw___thiscall: +case tok::kw___regcall: +case tok::kw___vectorcall: + Tok->setType(TT_MicrosoftCallingConvention); + break; default: break; } @@ -2611,6 +2619,13 @@ class AnnotatingParser { // Skip "const" as it does not have an influence on whether this is a name. FormatToken *PreviousNotConst = Tok.getPreviousNonComment(); +// Skip Microsoft calling conventions, as they come before the function +// name, but after the return type +while (PreviousNotConst && + PreviousNotConst->is(TT_MicrosoftCallingConvention)) { + PreviousNotConst = PreviousNotConst->getPreviousNonComment(); +} + // For javascript const can be like "let" or "var" if (!Style.isJavaScript()) while (PreviousNotConst && PreviousNotConst->is(tok::kw_const)) diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp index c0633ba3c29b3..0f96b0f92cdb2 100644 --- a/clang/unittests/Format/FormatTest.cpp +++ b/clang/unittests/Format/FormatTest.cpp @@ -17504,6 +17504,12 @@ TEST_F(FormatTest, ConfigurableSpaceBeforeParens) { verifyFormat("A::A() : a(1) {}", SpaceFuncDecl); verifyFormat("void f () __attribute__((asdf));", SpaceFuncDecl); verifyFormat("void __attribute__((asdf)) f ();", SpaceFuncDecl); + verifyFormat("void __stdcall f ();", SpaceFuncDecl); + verifyFormat("void __cdecl f ();", SpaceFuncDecl); + verifyFormat("void __fastcall f ();", SpaceFuncDecl); + verifyFormat("void __stdcall f() {}", SpaceFuncDecl); + verifyFormat("void __cdecl f() {}", SpaceFuncDecl); + verifyFormat("void __fastcall f() {}", SpaceFuncDecl); verifyFormat("#define A(x) x", SpaceFuncDecl); verifyFormat("#define A (x) x", SpaceFuncDecl); verifyFormat("#if defined(x)\n" @@ -17540,6 +17546,12 @@ TEST_F(FormatTest, ConfigurableSpaceBeforeParens) { verifyFormat("A::A () : a(1) {}", SpaceFuncDef); verifyFormat("void f() __attribute__((asdf));", SpaceFuncDef); verifyFormat("void __attribute__((asdf)) f();", SpaceFuncDef); + verifyFormat("void __stdcall f();", SpaceFuncDef); + verifyFormat("void __cdecl f();", SpaceFuncDef); + verifyFormat("void __fastcall f();", SpaceFuncDef); + verifyFormat("void __stdcall f () {}", SpaceFuncDef); + verifyFormat("void __cdecl f () {}", SpaceFuncDef); + verifyFormat("void __fastcall f () {}", SpaceFuncDef); verifyFormat("#define A(x) x", SpaceFuncDef); verifyFormat("#define A (x) x", SpaceFuncDef); verifyFormat("#if defined(x)\n" ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang-format] Handle templates in qualified typenames (PR #143194)
https://github.com/bdunkin updated https://github.com/llvm/llvm-project/pull/143194 >From 0168891771b3cdfc6f6305b046005fb335cbff01 Mon Sep 17 00:00:00 2001 From: Ben Dunkin Date: Fri, 6 Jun 2025 12:29:13 -0700 Subject: [PATCH] Fix identifiers not being marked as constructor/destructor names if they are qualified by a template type, or have template typename declarations in front of them. --- clang/lib/Format/TokenAnnotator.cpp | 68 +-- clang/unittests/Format/TokenAnnotatorTest.cpp | 43 2 files changed, 107 insertions(+), 4 deletions(-) diff --git a/clang/lib/Format/TokenAnnotator.cpp b/clang/lib/Format/TokenAnnotator.cpp index 37ab40ca97bff..479a8743c5a65 100644 --- a/clang/lib/Format/TokenAnnotator.cpp +++ b/clang/lib/Format/TokenAnnotator.cpp @@ -3622,6 +3622,29 @@ static unsigned maxNestingDepth(const AnnotatedLine &Line) { return Result; } +static bool startsQualifiedName(const FormatToken *Tok) { + // Consider: A::B::B() + // Tok --^ + if (Tok->startsSequence(tok::identifier, tok::coloncolon)) +return true; + + // Consider: A::B::B() + // Tok --^ + if (Tok->startsSequence(tok::identifier, TT_TemplateOpener)) { +Tok = Tok->getNextNonComment(); +assert(Tok); +assert(Tok->is(TT_TemplateOpener)); + +if (!Tok->MatchingParen) + return false; + +return Tok->MatchingParen->startsSequence(TT_TemplateCloser, + tok::coloncolon); + } + + return false; +} + // Returns the name of a function with no return type, e.g. a constructor or // destructor. static FormatToken *getFunctionName(const AnnotatedLine &Line, @@ -3651,6 +3674,21 @@ static FormatToken *getFunctionName(const AnnotatedLine &Line, continue; } +// Skip past template typename declarations that may precede the +// constructor/destructor name +if (Tok->is(tok::kw_template)) { + Tok = Tok->getNextNonComment(); + if (!Tok) +return nullptr; + + assert(Tok->is(TT_TemplateOpener)); + Tok = Tok->MatchingParen; + if (!Tok) +return nullptr; + + continue; +} + // A qualified name may start from the global namespace. if (Tok->is(tok::coloncolon)) { Tok = Tok->Next; @@ -3659,9 +3697,23 @@ static FormatToken *getFunctionName(const AnnotatedLine &Line, } // Skip to the unqualified part of the name. -while (Tok->startsSequence(tok::identifier, tok::coloncolon)) { - assert(Tok->Next); - Tok = Tok->Next->Next; +while (startsQualifiedName(Tok)) { + Tok = Tok->getNextNonComment(); + if (!Tok) +return nullptr; + + // Skip template types if this is a templated type name + if (Tok->is(TT_TemplateOpener)) { +Tok = Tok->MatchingParen; +if (!Tok) + return nullptr; + +Tok = Tok->getNextNonComment(); +if (!Tok) + return nullptr; + } + + Tok = Tok->getNextNonComment(); if (!Tok) return nullptr; } @@ -3691,10 +3743,18 @@ static bool isCtorOrDtorName(const FormatToken *Tok) { if (Prev && Prev->is(tok::tilde)) Prev = Prev->Previous; - if (!Prev || !Prev->endsSequence(tok::coloncolon, tok::identifier)) + // Consider: A::A() and A::A() + if (!Prev || (!Prev->endsSequence(tok::coloncolon, tok::identifier) && +!Prev->endsSequence(tok::coloncolon, TT_TemplateCloser))) { return false; + } assert(Prev->Previous); + if (Prev->Previous->is(TT_TemplateCloser) && Prev->Previous->MatchingParen) { +Prev = Prev->Previous->MatchingParen; +assert(Prev->Previous); + } + return Prev->Previous->TokenText == Tok->TokenText; } diff --git a/clang/unittests/Format/TokenAnnotatorTest.cpp b/clang/unittests/Format/TokenAnnotatorTest.cpp index 9d62ff8d39a77..98a570a42ada3 100644 --- a/clang/unittests/Format/TokenAnnotatorTest.cpp +++ b/clang/unittests/Format/TokenAnnotatorTest.cpp @@ -2346,6 +2346,49 @@ TEST_F(TokenAnnotatorTest, UnderstandsCtorAndDtorDeclNames) { EXPECT_TOKEN(Tokens[4], tok::l_paren, TT_FunctionDeclarationLParen); EXPECT_TOKEN(Tokens[6], tok::l_brace, TT_FunctionLBrace); + Tokens = annotate("Foo::Foo() {}"); + ASSERT_EQ(Tokens.size(), 11u) << Tokens; + EXPECT_TOKEN(Tokens[5], tok::identifier, TT_CtorDtorDeclName); + EXPECT_TOKEN(Tokens[6], tok::l_paren, TT_FunctionDeclarationLParen); + EXPECT_TOKEN(Tokens[8], tok::l_brace, TT_FunctionLBrace); + + Tokens = annotate("Foo::~Foo() {}"); + ASSERT_EQ(Tokens.size(), 12u) << Tokens; + EXPECT_TOKEN(Tokens[6], tok::identifier, TT_CtorDtorDeclName); + EXPECT_TOKEN(Tokens[7], tok::l_paren, TT_FunctionDeclarationLParen); + EXPECT_TOKEN(Tokens[9], tok::l_brace, TT_FunctionLBrace); + + Tokens = annotate("template Foo::Foo() {}"); + ASSERT_EQ(Tokens.size(), 16u) << Tokens; + EXPECT_TOKEN(Tokens[10], tok::identifier, TT_CtorDtorDeclName); + EXPECT_TOKEN(Tokens[11], tok::l_paren,
[clang] [clang-format] Fix Microsoft calling convensions preventing function names from being marked TT_StartOfName (PR #143047)
https://github.com/bdunkin closed https://github.com/llvm/llvm-project/pull/143047 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang-format] Fix Microsoft calling convensions preventing function names from being marked TT_StartOfName (PR #143047)
bdunkin wrote: Ah ok, I understand how your change is a better fix. I will close this PR as yours also fixes things. I have some more fixes coming for this same option, so I will follow your lead on getting the token type of the opening parenthesis correct. https://github.com/llvm/llvm-project/pull/143047 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang-format] Allow array alignment on non-rectangular arrays (PR #143781)
https://github.com/bdunkin created https://github.com/llvm/llvm-project/pull/143781 This change implements `AlignArrayOfStructures` for non-rectangular arrays (arrays of arrays where the inner arrays do not all have the same number of elements). It is largely backwards compatible with existing rectangular arrays, with one exception (see). Obviously, this is _not_ backwards compatible with existing non-rectangular arrays which would have been skipped, but will now be formatted. The tests for non-rectangular have been updated because they did not place the end brace in the same column for all rows like is done for rectangular arrays. Some new tests were also added to cover some missing, but interesting cases. >From d15c79f12ff54d570d24677327b9efb3d6773f81 Mon Sep 17 00:00:00 2001 From: Ben Dunkin Date: Fri, 6 Jun 2025 14:23:29 -0700 Subject: [PATCH] Allow array alignment to happen on non-rectangular arrays --- clang/lib/Format/TokenAnnotator.cpp| 23 +- clang/lib/Format/WhitespaceManager.cpp | 354 - clang/lib/Format/WhitespaceManager.h | 82 +- clang/unittests/Format/FormatTest.cpp | 76 -- 4 files changed, 320 insertions(+), 215 deletions(-) diff --git a/clang/lib/Format/TokenAnnotator.cpp b/clang/lib/Format/TokenAnnotator.cpp index aed1672afac66..636bf2efbb81d 100644 --- a/clang/lib/Format/TokenAnnotator.cpp +++ b/clang/lib/Format/TokenAnnotator.cpp @@ -4251,7 +4251,28 @@ FormatToken *TokenAnnotator::calculateInitializerColumnList( CurrentToken = CurrentToken->Next; if (!CurrentToken) break; - CurrentToken->StartsColumn = true; + + // Right (closing) braces should not count as starting a column because + // they are aligned using separate logic. + + // Note: This uses startsSequence() so that trailing comments are skipped + // when checking if the token after a comma/l-brace is a r_brace. We can't + // just ignore comments in general, because an inline comment with + // something else after it should still count as starting a column. + // IE: + // + //{ // a + // 4 + //} + // + // vs. + // + //{ /* a */ 4 } + // + // In the first case, the comment does not start a column, but in the + // second it does + CurrentToken->StartsColumn = !CurrentToken->startsSequence(tok::r_brace); + CurrentToken = CurrentToken->Previous; } CurrentToken = CurrentToken->Next; diff --git a/clang/lib/Format/WhitespaceManager.cpp b/clang/lib/Format/WhitespaceManager.cpp index cfaadf07edfd0..a458e3610db66 100644 --- a/clang/lib/Format/WhitespaceManager.cpp +++ b/clang/lib/Format/WhitespaceManager.cpp @@ -1318,75 +1318,141 @@ void WhitespaceManager::alignArrayInitializers(unsigned Start, unsigned End) { void WhitespaceManager::alignArrayInitializersRightJustified( CellDescriptions &&CellDescs) { - if (!CellDescs.isRectangular()) + + const int ColumnCount = CellDescs.ColumnStartingCellIndices.size(); + if (ColumnCount < 2) return; const int BracePadding = Style.Cpp11BracedListStyle ? 0 : 1; + auto &ColumnStartingIndices = CellDescs.ColumnStartingCellIndices; auto &Cells = CellDescs.Cells; - // Now go through and fixup the spaces. - auto *CellIter = Cells.begin(); - for (auto i = 0U; i < CellDescs.CellCounts[0]; ++i, ++CellIter) { -unsigned NetWidth = 0U; -if (isSplitCell(*CellIter)) - NetWidth = getNetWidth(Cells.begin(), CellIter, CellDescs.InitialSpaces); -auto CellWidth = getMaximumCellWidth(CellIter, NetWidth); - -if (Changes[CellIter->Index].Tok->is(tok::r_brace)) { - // So in here we want to see if there is a brace that falls - // on a line that was split. If so on that line we make sure that - // the spaces in front of the brace are enough. - const auto *Next = CellIter; - do { -const FormatToken *Previous = Changes[Next->Index].Tok->Previous; -if (Previous && Previous->isNot(TT_LineComment)) { - Changes[Next->Index].Spaces = BracePadding; - Changes[Next->Index].NewlinesBefore = 0; -} -Next = Next->NextColumnElement; - } while (Next); - // Unless the array is empty, we need the position of all the - // immediately adjacent cells - if (CellIter != Cells.begin()) { -auto ThisNetWidth = -getNetWidth(Cells.begin(), CellIter, CellDescs.InitialSpaces); -auto MaxNetWidth = getMaximumNetWidth( -Cells.begin(), CellIter, CellDescs.InitialSpaces, -CellDescs.CellCounts[0], CellDescs.CellCounts.size()); -if (ThisNetWidth < MaxNetWidth) - Changes[CellIter->Index].Spaces = (MaxNetWidth - ThisNetWidth); -auto RowCount = 1U; -auto Offset = std::distance(Cells.begin(), CellIter); -for (const auto *Next = CellIter->NextColumnElement; Next; - Next = Next->NextColumnEle
[clang] [clang-format] Allow array alignment on non-rectangular arrays (PR #143781)
https://github.com/bdunkin edited https://github.com/llvm/llvm-project/pull/143781 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang-format] Handle templates in qualified typenames (PR #143194)
https://github.com/bdunkin updated https://github.com/llvm/llvm-project/pull/143194 >From 37c7cde072e25eae0409e162c5080830d182f2dd Mon Sep 17 00:00:00 2001 From: Ben Dunkin Date: Fri, 6 Jun 2025 12:29:13 -0700 Subject: [PATCH] Fix identifiers not being marked as constructor/destructor names if they are qualified by a template type, or have template typename declarations in front of them. --- clang/lib/Format/TokenAnnotator.cpp | 65 +-- clang/unittests/Format/TokenAnnotatorTest.cpp | 55 2 files changed, 114 insertions(+), 6 deletions(-) diff --git a/clang/lib/Format/TokenAnnotator.cpp b/clang/lib/Format/TokenAnnotator.cpp index aed1672afac66..307231b3b 100644 --- a/clang/lib/Format/TokenAnnotator.cpp +++ b/clang/lib/Format/TokenAnnotator.cpp @@ -3630,6 +3630,36 @@ static unsigned maxNestingDepth(const AnnotatedLine &Line) { return Result; } +// Returns the token after the first qualifier of the name, or nullptr if there +// is no qualifier. +static FormatToken* skipNameQualifier(const FormatToken *Tok) { + // Qualified names must start with an identifier. + if (!Tok->is(tok::identifier)) +return nullptr; + + Tok = Tok->getNextNonComment(); + if (Tok == nullptr) +return nullptr; + + // Consider: A::B::B() + //Tok --^ + if (Tok->is(tok::coloncolon)) +return Tok->getNextNonComment(); + + // Consider: A::B::B() + //Tok --^ + if (Tok->is(TT_TemplateOpener)) { +if (!Tok->MatchingParen) + return nullptr; + +Tok = Tok->MatchingParen; +if (Tok->startsSequence(TT_TemplateCloser, tok::coloncolon)) + return Tok->getNextNonComment()->getNextNonComment(); + } + + return nullptr; +} + // Returns the name of a function with no return type, e.g. a constructor or // destructor. static FormatToken *getFunctionName(const AnnotatedLine &Line, @@ -3659,6 +3689,21 @@ static FormatToken *getFunctionName(const AnnotatedLine &Line, continue; } +// Skip past template typename declarations that may precede the +// constructor/destructor name +if (Tok->is(tok::kw_template)) { + Tok = Tok->getNextNonComment(); + if (!Tok) +return nullptr; + + assert(Tok->is(TT_TemplateOpener)); + Tok = Tok->MatchingParen; + if (!Tok) +return nullptr; + + continue; +} + // A qualified name may start from the global namespace. if (Tok->is(tok::coloncolon)) { Tok = Tok->Next; @@ -3667,13 +3712,13 @@ static FormatToken *getFunctionName(const AnnotatedLine &Line, } // Skip to the unqualified part of the name. -while (Tok->startsSequence(tok::identifier, tok::coloncolon)) { - assert(Tok->Next); - Tok = Tok->Next->Next; - if (!Tok) -return nullptr; +while (FormatToken* Next = skipNameQualifier(Tok)) { + Tok = Next; } +if (!Tok) + return nullptr; + // Skip the `~` if a destructor name. if (Tok->is(tok::tilde)) { Tok = Tok->Next; @@ -3699,10 +3744,18 @@ static bool isCtorOrDtorName(const FormatToken *Tok) { if (Prev && Prev->is(tok::tilde)) Prev = Prev->Previous; - if (!Prev || !Prev->endsSequence(tok::coloncolon, tok::identifier)) + // Consider: A::A() and A::A() + if (!Prev || (!Prev->endsSequence(tok::coloncolon, tok::identifier) && +!Prev->endsSequence(tok::coloncolon, TT_TemplateCloser))) { return false; + } assert(Prev->Previous); + if (Prev->Previous->is(TT_TemplateCloser) && Prev->Previous->MatchingParen) { +Prev = Prev->Previous->MatchingParen; +assert(Prev->Previous); + } + return Prev->Previous->TokenText == Tok->TokenText; } diff --git a/clang/unittests/Format/TokenAnnotatorTest.cpp b/clang/unittests/Format/TokenAnnotatorTest.cpp index 873c6c492d18c..c3538b2a8edfa 100644 --- a/clang/unittests/Format/TokenAnnotatorTest.cpp +++ b/clang/unittests/Format/TokenAnnotatorTest.cpp @@ -2351,6 +2351,61 @@ TEST_F(TokenAnnotatorTest, UnderstandsCtorAndDtorDeclNames) { EXPECT_TOKEN(Tokens[4], tok::l_paren, TT_FunctionDeclarationLParen); EXPECT_TOKEN(Tokens[6], tok::l_brace, TT_FunctionLBrace); + Tokens = annotate("Foo::Foo() {}"); + ASSERT_EQ(Tokens.size(), 11u) << Tokens; + EXPECT_TOKEN(Tokens[5], tok::identifier, TT_CtorDtorDeclName); + EXPECT_TOKEN(Tokens[6], tok::l_paren, TT_FunctionDeclarationLParen); + EXPECT_TOKEN(Tokens[8], tok::l_brace, TT_FunctionLBrace); + + Tokens = annotate("Foo::~Foo() {}"); + ASSERT_EQ(Tokens.size(), 12u) << Tokens; + EXPECT_TOKEN(Tokens[6], tok::identifier, TT_CtorDtorDeclName); + EXPECT_TOKEN(Tokens[7], tok::l_paren, TT_FunctionDeclarationLParen); + EXPECT_TOKEN(Tokens[9], tok::l_brace, TT_FunctionLBrace); + + Tokens = annotate("template Foo::Foo() {}"); + ASSERT_EQ(Tokens.size(), 16u) << Tokens; + EXPECT_TOKEN(Tokens[10], tok::identifier, TT_CtorDtorDeclName); + EXPECT_TOKEN(Tokens[11], tok::l_paren, TT_FunctionDeclarationLParen
[clang] [clang-format] Handle templates in qualified typenames (PR #143194)
@@ -3622,6 +3622,29 @@ static unsigned maxNestingDepth(const AnnotatedLine &Line) { return Result; } +static bool startsQualifiedName(const FormatToken *Tok) { bdunkin wrote: Done. https://github.com/llvm/llvm-project/pull/143194 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang-format] Handle templates in qualified typenames (PR #143194)
@@ -3622,6 +3622,29 @@ static unsigned maxNestingDepth(const AnnotatedLine &Line) { return Result; } +static bool startsQualifiedName(const FormatToken *Tok) { + // Consider: A::B::B() + // Tok --^ + if (Tok->startsSequence(tok::identifier, tok::coloncolon)) +return true; + + // Consider: A::B::B() + // Tok --^ + if (Tok->startsSequence(tok::identifier, TT_TemplateOpener)) { +Tok = Tok->getNextNonComment(); +assert(Tok); bdunkin wrote: Done. https://github.com/llvm/llvm-project/pull/143194 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang-format] Handle templates in qualified typenames (PR #143194)
@@ -3659,9 +3697,23 @@ static FormatToken *getFunctionName(const AnnotatedLine &Line, } // Skip to the unqualified part of the name. -while (Tok->startsSequence(tok::identifier, tok::coloncolon)) { - assert(Tok->Next); - Tok = Tok->Next->Next; +while (startsQualifiedName(Tok)) { bdunkin wrote: I have done this, and renamed it `skipNameQualifier` to reflect the new behaviour. https://github.com/llvm/llvm-project/pull/143194 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang-format] Handle templates in qualified typenames (PR #143194)
https://github.com/bdunkin updated https://github.com/llvm/llvm-project/pull/143194 >From 839d068df748189470f2c69eb47714425de9524b Mon Sep 17 00:00:00 2001 From: Ben Dunkin Date: Fri, 6 Jun 2025 12:29:13 -0700 Subject: [PATCH] Fix identifiers not being marked as constructor/destructor names if they are qualified by a template type, or have template typename declarations in front of them. --- clang/lib/Format/TokenAnnotator.cpp | 65 +-- clang/unittests/Format/TokenAnnotatorTest.cpp | 55 2 files changed, 114 insertions(+), 6 deletions(-) diff --git a/clang/lib/Format/TokenAnnotator.cpp b/clang/lib/Format/TokenAnnotator.cpp index aed1672afac66..0a76b00cc6c5b 100644 --- a/clang/lib/Format/TokenAnnotator.cpp +++ b/clang/lib/Format/TokenAnnotator.cpp @@ -3630,6 +3630,36 @@ static unsigned maxNestingDepth(const AnnotatedLine &Line) { return Result; } +// Returns the token after the first qualifier of the name, or nullptr if there +// is no qualifier. +static FormatToken* skipNameQualifier(const FormatToken *Tok) { + // Qualified names must start with an identifier. + if (!Tok->is(tok::identifier)) +return nullptr; + + Tok = Tok->getNextNonComment(); + if (Tok == nullptr) +return nullptr; + + // Consider: A::B::B() + //Tok --^ + if (Tok->is(tok::coloncolon)) +return Tok->getNextNonComment(); + + // Consider: A::B::B() + //Tok --^ + if (Tok->is(TT_TemplateOpener)) { +if (!Tok->MatchingParen) + return nullptr; + +Tok = Tok->MatchingParen; +if (Tok->startsSequence(TT_TemplateCloser, tok::coloncolon)) + return Tok->getNextNonComment()->getNextNonComment(); + } + + return nullptr; +} + // Returns the name of a function with no return type, e.g. a constructor or // destructor. static FormatToken *getFunctionName(const AnnotatedLine &Line, @@ -3659,6 +3689,21 @@ static FormatToken *getFunctionName(const AnnotatedLine &Line, continue; } +// Skip past template typename declarations that may precede the +// constructor/destructor name. +if (Tok->is(tok::kw_template)) { + Tok = Tok->getNextNonComment(); + if (!Tok) +return nullptr; + + assert(Tok->is(TT_TemplateOpener)); + Tok = Tok->MatchingParen; + if (!Tok) +return nullptr; + + continue; +} + // A qualified name may start from the global namespace. if (Tok->is(tok::coloncolon)) { Tok = Tok->Next; @@ -3667,13 +3712,13 @@ static FormatToken *getFunctionName(const AnnotatedLine &Line, } // Skip to the unqualified part of the name. -while (Tok->startsSequence(tok::identifier, tok::coloncolon)) { - assert(Tok->Next); - Tok = Tok->Next->Next; - if (!Tok) -return nullptr; +while (FormatToken* Next = skipNameQualifier(Tok)) { + Tok = Next; } +if (!Tok) + return nullptr; + // Skip the `~` if a destructor name. if (Tok->is(tok::tilde)) { Tok = Tok->Next; @@ -3699,10 +3744,18 @@ static bool isCtorOrDtorName(const FormatToken *Tok) { if (Prev && Prev->is(tok::tilde)) Prev = Prev->Previous; - if (!Prev || !Prev->endsSequence(tok::coloncolon, tok::identifier)) + // Consider: A::A() and A::A() + if (!Prev || (!Prev->endsSequence(tok::coloncolon, tok::identifier) && +!Prev->endsSequence(tok::coloncolon, TT_TemplateCloser))) { return false; + } assert(Prev->Previous); + if (Prev->Previous->is(TT_TemplateCloser) && Prev->Previous->MatchingParen) { +Prev = Prev->Previous->MatchingParen; +assert(Prev->Previous); + } + return Prev->Previous->TokenText == Tok->TokenText; } diff --git a/clang/unittests/Format/TokenAnnotatorTest.cpp b/clang/unittests/Format/TokenAnnotatorTest.cpp index 873c6c492d18c..c3538b2a8edfa 100644 --- a/clang/unittests/Format/TokenAnnotatorTest.cpp +++ b/clang/unittests/Format/TokenAnnotatorTest.cpp @@ -2351,6 +2351,61 @@ TEST_F(TokenAnnotatorTest, UnderstandsCtorAndDtorDeclNames) { EXPECT_TOKEN(Tokens[4], tok::l_paren, TT_FunctionDeclarationLParen); EXPECT_TOKEN(Tokens[6], tok::l_brace, TT_FunctionLBrace); + Tokens = annotate("Foo::Foo() {}"); + ASSERT_EQ(Tokens.size(), 11u) << Tokens; + EXPECT_TOKEN(Tokens[5], tok::identifier, TT_CtorDtorDeclName); + EXPECT_TOKEN(Tokens[6], tok::l_paren, TT_FunctionDeclarationLParen); + EXPECT_TOKEN(Tokens[8], tok::l_brace, TT_FunctionLBrace); + + Tokens = annotate("Foo::~Foo() {}"); + ASSERT_EQ(Tokens.size(), 12u) << Tokens; + EXPECT_TOKEN(Tokens[6], tok::identifier, TT_CtorDtorDeclName); + EXPECT_TOKEN(Tokens[7], tok::l_paren, TT_FunctionDeclarationLParen); + EXPECT_TOKEN(Tokens[9], tok::l_brace, TT_FunctionLBrace); + + Tokens = annotate("template Foo::Foo() {}"); + ASSERT_EQ(Tokens.size(), 16u) << Tokens; + EXPECT_TOKEN(Tokens[10], tok::identifier, TT_CtorDtorDeclName); + EXPECT_TOKEN(Tokens[11], tok::l_paren, TT_FunctionDeclarationLPare
[clang] [clang-format] Handle templates in qualified typenames (PR #143194)
@@ -3651,6 +3674,21 @@ static FormatToken *getFunctionName(const AnnotatedLine &Line, continue; } +// Skip past template typename declarations that may precede the +// constructor/destructor name bdunkin wrote: Done. https://github.com/llvm/llvm-project/pull/143194 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang] [clang-format] Handle templates in qualified typenames (PR #143194)
@@ -3651,6 +3674,21 @@ static FormatToken *getFunctionName(const AnnotatedLine &Line, continue; } +// Skip past template typename declarations that may precede the +// constructor/destructor name +if (Tok->is(tok::kw_template)) { bdunkin wrote: This is already inside the loop. Do you perhaps mean that it should come earlier? https://github.com/llvm/llvm-project/pull/143194 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits