[clang] [clang-format] Handle templates in qualified typenames (PR #143194)
via cfe-commits
cfe-commits at lists.llvm.org
Fri Jun 6 12:38:16 PDT 2025
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang-format
Author: Ben Dunkin (bdunkin)
<details>
<summary>Changes</summary>
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
---
Full diff: https://github.com/llvm/llvm-project/pull/143194.diff
2 Files Affected:
- (modified) clang/lib/Format/TokenAnnotator.cpp (+64-4)
- (modified) clang/unittests/Format/TokenAnnotatorTest.cpp (+42)
``````````diff
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<float>::B<int>::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<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 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<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, 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"
``````````
</details>
https://github.com/llvm/llvm-project/pull/143194
More information about the cfe-commits
mailing list