https://github.com/bdunkin updated https://github.com/llvm/llvm-project/pull/143194
>From bbbf42e25cd9cc93b188cf378e95938e5f6a4dc3 Mon Sep 17 00:00:00 2001 From: Ben Dunkin <[email protected]> Date: Fri, 6 Jun 2025 12:29:13 -0700 Subject: [PATCH 1/3] 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 | 66 +++++++++++++++++-- clang/unittests/Format/TokenAnnotatorTest.cpp | 55 ++++++++++++++++ 2 files changed, 114 insertions(+), 7 deletions(-) diff --git a/clang/lib/Format/TokenAnnotator.cpp b/clang/lib/Format/TokenAnnotator.cpp index 79cfa73001e54..972382cbfaa4b 100644 --- a/clang/lib/Format/TokenAnnotator.cpp +++ b/clang/lib/Format/TokenAnnotator.cpp @@ -3668,6 +3668,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<float>::B<int>::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, @@ -3697,6 +3727,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; @@ -3705,12 +3750,11 @@ 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)) { @@ -3737,10 +3781,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<int>::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 065d53f42f02b..5b1d3ceab25bc 100644 --- a/clang/unittests/Format/TokenAnnotatorTest.cpp +++ b/clang/unittests/Format/TokenAnnotatorTest.cpp @@ -2455,6 +2455,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<int>::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<int>::~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 <typename V> Foo<V>::Foo() {}"); + ASSERT_EQ(Tokens.size(), 16u) << Tokens; + EXPECT_TOKEN(Tokens[10], tok::identifier, TT_CtorDtorDeclName); + EXPECT_TOKEN(Tokens[11], tok::l_paren, TT_FunctionDeclarationLParen); + EXPECT_TOKEN(Tokens[13], tok::l_brace, TT_FunctionLBrace); + + Tokens = annotate("template <typename V> Foo<V>::~Foo() {}"); + ASSERT_EQ(Tokens.size(), 17u) << Tokens; + EXPECT_TOKEN(Tokens[11], tok::identifier, TT_CtorDtorDeclName); + EXPECT_TOKEN(Tokens[12], tok::l_paren, TT_FunctionDeclarationLParen); + EXPECT_TOKEN(Tokens[14], tok::l_brace, TT_FunctionLBrace); + + Tokens = annotate("template <typename V> [[nodiscard]] Foo<V>::Foo() {}"); + ASSERT_EQ(Tokens.size(), 21u) << Tokens; + EXPECT_TOKEN(Tokens[15], tok::identifier, TT_CtorDtorDeclName); + EXPECT_TOKEN(Tokens[16], tok::l_paren, TT_FunctionDeclarationLParen); + EXPECT_TOKEN(Tokens[18], tok::l_brace, TT_FunctionLBrace); + + Tokens = annotate("template <typename V> Foo<V>::Foo() [[nodiscard]] {}"); + ASSERT_EQ(Tokens.size(), 21u) << Tokens; + EXPECT_TOKEN(Tokens[10], tok::identifier, TT_CtorDtorDeclName); + EXPECT_TOKEN(Tokens[11], tok::l_paren, TT_FunctionDeclarationLParen); + EXPECT_TOKEN(Tokens[18], tok::l_brace, TT_FunctionLBrace); + + Tokens = annotate("template <typename V, typename U> Foo<V, U>::Foo() {}"); + ASSERT_EQ(Tokens.size(), 21u) << Tokens; + EXPECT_TOKEN(Tokens[15], tok::identifier, TT_CtorDtorDeclName); + EXPECT_TOKEN(Tokens[16], tok::l_paren, TT_FunctionDeclarationLParen); + EXPECT_TOKEN(Tokens[18], tok::l_brace, TT_FunctionLBrace); + + Tokens = annotate("template <typename V, typename U> Foo<V, U>::~Foo() {}"); + ASSERT_EQ(Tokens.size(), 22u) << Tokens; + EXPECT_TOKEN(Tokens[16], tok::identifier, TT_CtorDtorDeclName); + EXPECT_TOKEN(Tokens[17], tok::l_paren, TT_FunctionDeclarationLParen); + EXPECT_TOKEN(Tokens[19], tok::l_brace, TT_FunctionLBrace); + + Tokens = annotate( + "template <typename V> template<typename W> Foo<V>::Foo(W x) {}"); + ASSERT_EQ(Tokens.size(), 23u) << Tokens; + EXPECT_TOKEN(Tokens[15], tok::identifier, TT_CtorDtorDeclName); + EXPECT_TOKEN(Tokens[16], tok::l_paren, TT_FunctionDeclarationLParen); + EXPECT_TOKEN(Tokens[20], tok::l_brace, TT_FunctionLBrace); + Tokens = annotate("struct Test {\n" " Test()\n" " : l([] {\n" >From a1c9de505e45fabd13454ff07f9a0bf5b9f383ea Mon Sep 17 00:00:00 2001 From: Ben Dunkin <[email protected]> Date: Thu, 17 Jul 2025 12:29:18 -0700 Subject: [PATCH 2/3] Fix formatting --- clang/lib/Format/TokenAnnotator.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/clang/lib/Format/TokenAnnotator.cpp b/clang/lib/Format/TokenAnnotator.cpp index 972382cbfaa4b..2ac21f759b4d8 100644 --- a/clang/lib/Format/TokenAnnotator.cpp +++ b/clang/lib/Format/TokenAnnotator.cpp @@ -3670,7 +3670,7 @@ static unsigned maxNestingDepth(const AnnotatedLine &Line) { // Returns the token after the first qualifier of the name, or nullptr if there // is no qualifier. -static FormatToken* skipNameQualifier(const FormatToken *Tok) { +static FormatToken *skipNameQualifier(const FormatToken *Tok) { // Qualified names must start with an identifier. if (!Tok->is(tok::identifier)) return nullptr; @@ -3734,7 +3734,11 @@ static FormatToken *getFunctionName(const AnnotatedLine &Line, if (!Tok) return nullptr; - assert(Tok->is(TT_TemplateOpener)); + // If the next token after the template keyword is not an opening bracket, + // it is a template instantiation, and not a function. + if (Tok->isNot(TT_TemplateOpener)) + return nullptr; + Tok = Tok->MatchingParen; if (!Tok) return nullptr; >From 013926c9a3f12afc60837352df9c089d8b9638e6 Mon Sep 17 00:00:00 2001 From: Ben Dunkin <[email protected]> Date: Fri, 5 Dec 2025 15:54:32 -0800 Subject: [PATCH 3/3] Make suggested changes from reviewers. --- clang/lib/Format/TokenAnnotator.cpp | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/clang/lib/Format/TokenAnnotator.cpp b/clang/lib/Format/TokenAnnotator.cpp index 2ac21f759b4d8..f6341ff0305a9 100644 --- a/clang/lib/Format/TokenAnnotator.cpp +++ b/clang/lib/Format/TokenAnnotator.cpp @@ -3671,12 +3671,14 @@ static unsigned maxNestingDepth(const AnnotatedLine &Line) { // Returns the token after the first qualifier of the name, or nullptr if there // is no qualifier. static FormatToken *skipNameQualifier(const FormatToken *Tok) { + assert(Tok); + // Qualified names must start with an identifier. - if (!Tok->is(tok::identifier)) + if (Tok->isNot(tok::identifier)) return nullptr; Tok = Tok->getNextNonComment(); - if (Tok == nullptr) + if (!Tok) return nullptr; // Consider: A::B::B() @@ -3687,15 +3689,16 @@ static FormatToken *skipNameQualifier(const FormatToken *Tok) { // Consider: A<float>::B<int>::B() // Tok --^ if (Tok->is(TT_TemplateOpener)) { - if (!Tok->MatchingParen) + Tok = Tok->MatchingParen; + if (!Tok) return nullptr; - Tok = Tok->MatchingParen; - if (Tok->startsSequence(TT_TemplateCloser, tok::coloncolon)) - return Tok->getNextNonComment()->getNextNonComment(); + Tok = Tok->getNextNonComment(); + if (!Tok) + return nullptr; } - return nullptr; + return Tok->is(tok::coloncolon) ? Tok->getNextNonComment() : nullptr; } // Returns the name of a function with no return type, e.g. a constructor or @@ -3740,8 +3743,6 @@ static FormatToken *getFunctionName(const AnnotatedLine &Line, return nullptr; Tok = Tok->MatchingParen; - if (!Tok) - return nullptr; continue; } @@ -3754,7 +3755,7 @@ static FormatToken *getFunctionName(const AnnotatedLine &Line, } // Skip to the unqualified part of the name. - while (FormatToken *Next = skipNameQualifier(Tok)) + while (auto *Next = skipNameQualifier(Tok)) Tok = Next; if (!Tok) _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
