Skip to content

Commit 0bac8f1

Browse files
authored
[clang-format] Handle templates in qualified typenames (#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
1 parent e7015c9 commit 0bac8f1

File tree

2 files changed

+119
-7
lines changed

2 files changed

+119
-7
lines changed

clang/lib/Format/TokenAnnotator.cpp

Lines changed: 64 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3668,6 +3668,39 @@ static unsigned maxNestingDepth(const AnnotatedLine &Line) {
36683668
return Result;
36693669
}
36703670

3671+
// Returns the token after the first qualifier of the name, or nullptr if there
3672+
// is no qualifier.
3673+
static FormatToken *skipNameQualifier(const FormatToken *Tok) {
3674+
assert(Tok);
3675+
3676+
// Qualified names must start with an identifier.
3677+
if (Tok->isNot(tok::identifier))
3678+
return nullptr;
3679+
3680+
Tok = Tok->getNextNonComment();
3681+
if (!Tok)
3682+
return nullptr;
3683+
3684+
// Consider: A::B::B()
3685+
// Tok --^
3686+
if (Tok->is(tok::coloncolon))
3687+
return Tok->getNextNonComment();
3688+
3689+
// Consider: A<float>::B<int>::B()
3690+
// Tok --^
3691+
if (Tok->is(TT_TemplateOpener)) {
3692+
Tok = Tok->MatchingParen;
3693+
if (!Tok)
3694+
return nullptr;
3695+
3696+
Tok = Tok->getNextNonComment();
3697+
if (!Tok)
3698+
return nullptr;
3699+
}
3700+
3701+
return Tok->is(tok::coloncolon) ? Tok->getNextNonComment() : nullptr;
3702+
}
3703+
36713704
// Returns the name of a function with no return type, e.g. a constructor or
36723705
// destructor.
36733706
static FormatToken *getFunctionName(const AnnotatedLine &Line,
@@ -3697,6 +3730,23 @@ static FormatToken *getFunctionName(const AnnotatedLine &Line,
36973730
continue;
36983731
}
36993732

3733+
// Skip past template typename declarations that may precede the
3734+
// constructor/destructor name.
3735+
if (Tok->is(tok::kw_template)) {
3736+
Tok = Tok->getNextNonComment();
3737+
if (!Tok)
3738+
return nullptr;
3739+
3740+
// If the next token after the template keyword is not an opening bracket,
3741+
// it is a template instantiation, and not a function.
3742+
if (Tok->isNot(TT_TemplateOpener))
3743+
return nullptr;
3744+
3745+
Tok = Tok->MatchingParen;
3746+
3747+
continue;
3748+
}
3749+
37003750
// A qualified name may start from the global namespace.
37013751
if (Tok->is(tok::coloncolon)) {
37023752
Tok = Tok->Next;
@@ -3705,12 +3755,11 @@ static FormatToken *getFunctionName(const AnnotatedLine &Line,
37053755
}
37063756

37073757
// Skip to the unqualified part of the name.
3708-
while (Tok->startsSequence(tok::identifier, tok::coloncolon)) {
3709-
assert(Tok->Next);
3710-
Tok = Tok->Next->Next;
3711-
if (!Tok)
3712-
return nullptr;
3713-
}
3758+
while (auto *Next = skipNameQualifier(Tok))
3759+
Tok = Next;
3760+
3761+
if (!Tok)
3762+
return nullptr;
37143763

37153764
// Skip the `~` if a destructor name.
37163765
if (Tok->is(tok::tilde)) {
@@ -3737,10 +3786,18 @@ static bool isCtorOrDtorName(const FormatToken *Tok) {
37373786
if (Prev && Prev->is(tok::tilde))
37383787
Prev = Prev->Previous;
37393788

3740-
if (!Prev || !Prev->endsSequence(tok::coloncolon, tok::identifier))
3789+
// Consider: A::A() and A<int>::A()
3790+
if (!Prev || (!Prev->endsSequence(tok::coloncolon, tok::identifier) &&
3791+
!Prev->endsSequence(tok::coloncolon, TT_TemplateCloser))) {
37413792
return false;
3793+
}
37423794

37433795
assert(Prev->Previous);
3796+
if (Prev->Previous->is(TT_TemplateCloser) && Prev->Previous->MatchingParen) {
3797+
Prev = Prev->Previous->MatchingParen;
3798+
assert(Prev->Previous);
3799+
}
3800+
37443801
return Prev->Previous->TokenText == Tok->TokenText;
37453802
}
37463803

clang/unittests/Format/TokenAnnotatorTest.cpp

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2461,6 +2461,61 @@ TEST_F(TokenAnnotatorTest, UnderstandsCtorAndDtorDeclNames) {
24612461
EXPECT_TOKEN(Tokens[4], tok::l_paren, TT_FunctionDeclarationLParen);
24622462
EXPECT_TOKEN(Tokens[6], tok::l_brace, TT_FunctionLBrace);
24632463

2464+
Tokens = annotate("Foo<int>::Foo() {}");
2465+
ASSERT_EQ(Tokens.size(), 11u) << Tokens;
2466+
EXPECT_TOKEN(Tokens[5], tok::identifier, TT_CtorDtorDeclName);
2467+
EXPECT_TOKEN(Tokens[6], tok::l_paren, TT_FunctionDeclarationLParen);
2468+
EXPECT_TOKEN(Tokens[8], tok::l_brace, TT_FunctionLBrace);
2469+
2470+
Tokens = annotate("Foo<int>::~Foo() {}");
2471+
ASSERT_EQ(Tokens.size(), 12u) << Tokens;
2472+
EXPECT_TOKEN(Tokens[6], tok::identifier, TT_CtorDtorDeclName);
2473+
EXPECT_TOKEN(Tokens[7], tok::l_paren, TT_FunctionDeclarationLParen);
2474+
EXPECT_TOKEN(Tokens[9], tok::l_brace, TT_FunctionLBrace);
2475+
2476+
Tokens = annotate("template <typename V> Foo<V>::Foo() {}");
2477+
ASSERT_EQ(Tokens.size(), 16u) << Tokens;
2478+
EXPECT_TOKEN(Tokens[10], tok::identifier, TT_CtorDtorDeclName);
2479+
EXPECT_TOKEN(Tokens[11], tok::l_paren, TT_FunctionDeclarationLParen);
2480+
EXPECT_TOKEN(Tokens[13], tok::l_brace, TT_FunctionLBrace);
2481+
2482+
Tokens = annotate("template <typename V> Foo<V>::~Foo() {}");
2483+
ASSERT_EQ(Tokens.size(), 17u) << Tokens;
2484+
EXPECT_TOKEN(Tokens[11], tok::identifier, TT_CtorDtorDeclName);
2485+
EXPECT_TOKEN(Tokens[12], tok::l_paren, TT_FunctionDeclarationLParen);
2486+
EXPECT_TOKEN(Tokens[14], tok::l_brace, TT_FunctionLBrace);
2487+
2488+
Tokens = annotate("template <typename V> [[nodiscard]] Foo<V>::Foo() {}");
2489+
ASSERT_EQ(Tokens.size(), 21u) << Tokens;
2490+
EXPECT_TOKEN(Tokens[15], tok::identifier, TT_CtorDtorDeclName);
2491+
EXPECT_TOKEN(Tokens[16], tok::l_paren, TT_FunctionDeclarationLParen);
2492+
EXPECT_TOKEN(Tokens[18], tok::l_brace, TT_FunctionLBrace);
2493+
2494+
Tokens = annotate("template <typename V> Foo<V>::Foo() [[nodiscard]] {}");
2495+
ASSERT_EQ(Tokens.size(), 21u) << Tokens;
2496+
EXPECT_TOKEN(Tokens[10], tok::identifier, TT_CtorDtorDeclName);
2497+
EXPECT_TOKEN(Tokens[11], tok::l_paren, TT_FunctionDeclarationLParen);
2498+
EXPECT_TOKEN(Tokens[18], tok::l_brace, TT_FunctionLBrace);
2499+
2500+
Tokens = annotate("template <typename V, typename U> Foo<V, U>::Foo() {}");
2501+
ASSERT_EQ(Tokens.size(), 21u) << Tokens;
2502+
EXPECT_TOKEN(Tokens[15], tok::identifier, TT_CtorDtorDeclName);
2503+
EXPECT_TOKEN(Tokens[16], tok::l_paren, TT_FunctionDeclarationLParen);
2504+
EXPECT_TOKEN(Tokens[18], tok::l_brace, TT_FunctionLBrace);
2505+
2506+
Tokens = annotate("template <typename V, typename U> Foo<V, U>::~Foo() {}");
2507+
ASSERT_EQ(Tokens.size(), 22u) << Tokens;
2508+
EXPECT_TOKEN(Tokens[16], tok::identifier, TT_CtorDtorDeclName);
2509+
EXPECT_TOKEN(Tokens[17], tok::l_paren, TT_FunctionDeclarationLParen);
2510+
EXPECT_TOKEN(Tokens[19], tok::l_brace, TT_FunctionLBrace);
2511+
2512+
Tokens = annotate(
2513+
"template <typename V> template<typename W> Foo<V>::Foo(W x) {}");
2514+
ASSERT_EQ(Tokens.size(), 23u) << Tokens;
2515+
EXPECT_TOKEN(Tokens[15], tok::identifier, TT_CtorDtorDeclName);
2516+
EXPECT_TOKEN(Tokens[16], tok::l_paren, TT_FunctionDeclarationLParen);
2517+
EXPECT_TOKEN(Tokens[20], tok::l_brace, TT_FunctionLBrace);
2518+
24642519
Tokens = annotate("struct Test {\n"
24652520
" Test()\n"
24662521
" : l([] {\n"

0 commit comments

Comments
 (0)