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

Reply via email to