Skip to content

Commit

Permalink
[clang-format] Handle templated elaborated type specifier in function… (
Browse files Browse the repository at this point in the history
#77013)

… return type.

The behavior now is consistent with the non template version.
Enabled and updated old test.
  • Loading branch information
XDeme1 committed Jan 20, 2024
1 parent 11b3b10 commit a464e05
Show file tree
Hide file tree
Showing 3 changed files with 37 additions and 17 deletions.
30 changes: 16 additions & 14 deletions clang/lib/Format/UnwrappedLineParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3882,6 +3882,9 @@ void UnwrappedLineParser::parseRecord(bool ParseAsExpr) {
const FormatToken &InitialToken = *FormatTok;
nextToken();

auto IsNonMacroIdentifier = [](const FormatToken *Tok) {
return Tok->is(tok::identifier) && Tok->TokenText != Tok->TokenText.upper();
};
// The actual identifier can be a nested name specifier, and in macros
// it is often token-pasted.
// An [[attribute]] can be before the identifier.
Expand All @@ -3903,27 +3906,26 @@ void UnwrappedLineParser::parseRecord(bool ParseAsExpr) {
}
if (FormatTok->is(tok::l_square) && handleCppAttributes())
continue;
bool IsNonMacroIdentifier =
FormatTok->is(tok::identifier) &&
FormatTok->TokenText != FormatTok->TokenText.upper();
nextToken();
// We can have macros in between 'class' and the class name.
if (!IsNonMacroIdentifier && FormatTok->is(tok::l_paren))
if (!IsNonMacroIdentifier(FormatTok->Previous) &&
FormatTok->is(tok::l_paren)) {
parseParens();
}
}

// Note that parsing away template declarations here leads to incorrectly
// accepting function declarations as record declarations.
// In general, we cannot solve this problem. Consider:
// class A<int> B() {}
// which can be a function definition or a class definition when B() is a
// macro. If we find enough real-world cases where this is a problem, we
// can parse for the 'template' keyword in the beginning of the statement,
// and thus rule out the record production in case there is no template
// (this would still leave us with an ambiguity between template function
// and class declarations).
if (FormatTok->isOneOf(tok::colon, tok::less)) {
int AngleNestingLevel = 0;
do {
if (FormatTok->is(tok::less))
++AngleNestingLevel;
else if (FormatTok->is(tok::greater))
--AngleNestingLevel;

if (AngleNestingLevel == 0 && FormatTok->is(tok::l_paren) &&
IsNonMacroIdentifier(FormatTok->Previous)) {
break;
}
if (FormatTok->is(tok::l_brace)) {
calculateBraceTypes(/*ExpectClassBody=*/true);
if (!tryToParseBracedList())
Expand Down
10 changes: 7 additions & 3 deletions clang/unittests/Format/FormatTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14659,9 +14659,13 @@ TEST_F(FormatTest, UnderstandContextOfRecordTypeKeywords) {
verifyFormat("template <> struct X < 15, i<3 && 42 < 50 && 33 < 28> {};");
verifyFormat("int i = SomeFunction(a<b, a> b);");

// FIXME:
// This now gets parsed incorrectly as class definition.
// verifyFormat("class A<int> f() {\n}\nint n;");
verifyFormat("class A<int> f() {}\n"
"int n;");
verifyFormat("template <typename T> class A<T> f() {}\n"
"int n;");

verifyFormat("template <> class Foo<int> F() {\n"
"} n;");

// Elaborate types where incorrectly parsing the structural element would
// break the indent.
Expand Down
14 changes: 14 additions & 0 deletions clang/unittests/Format/TokenAnnotatorTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2520,6 +2520,20 @@ TEST_F(TokenAnnotatorTest, BraceKind) {
EXPECT_BRACE_KIND(Tokens[4], BK_Block);
EXPECT_BRACE_KIND(Tokens[5], BK_Block);

Tokens = annotate("class Foo<int> f() {}");
ASSERT_EQ(Tokens.size(), 11u) << Tokens;
EXPECT_TOKEN(Tokens[5], tok::identifier, TT_FunctionDeclarationName);
EXPECT_TOKEN(Tokens[8], tok::l_brace, TT_FunctionLBrace);
EXPECT_BRACE_KIND(Tokens[8], BK_Block);
EXPECT_BRACE_KIND(Tokens[9], BK_Block);

Tokens = annotate("template <typename T> class Foo<T> f() {}");
ASSERT_EQ(Tokens.size(), 16u) << Tokens;
EXPECT_TOKEN(Tokens[10], tok::identifier, TT_FunctionDeclarationName);
EXPECT_TOKEN(Tokens[13], tok::l_brace, TT_FunctionLBrace);
EXPECT_BRACE_KIND(Tokens[13], BK_Block);
EXPECT_BRACE_KIND(Tokens[14], BK_Block);

Tokens = annotate("void f() override {};");
ASSERT_EQ(Tokens.size(), 9u) << Tokens;
EXPECT_TOKEN(Tokens[1], tok::identifier, TT_FunctionDeclarationName);
Expand Down

0 comments on commit a464e05

Please sign in to comment.