Author: XDeme Date: 2024-01-20T14:47:58-08:00 New Revision: a464e05109088f1f3a0ca4c83d6dd900e83bdb4b
URL: https://github.com/llvm/llvm-project/commit/a464e05109088f1f3a0ca4c83d6dd900e83bdb4b DIFF: https://github.com/llvm/llvm-project/commit/a464e05109088f1f3a0ca4c83d6dd900e83bdb4b.diff LOG: [clang-format] Handle templated elaborated type specifier in function… (#77013) … return type. The behavior now is consistent with the non template version. Enabled and updated old test. Added: Modified: clang/lib/Format/UnwrappedLineParser.cpp clang/unittests/Format/FormatTest.cpp clang/unittests/Format/TokenAnnotatorTest.cpp Removed: ################################################################################ diff --git a/clang/lib/Format/UnwrappedLineParser.cpp b/clang/lib/Format/UnwrappedLineParser.cpp index c08ce86449b6ea..7bc6410a78a495 100644 --- a/clang/lib/Format/UnwrappedLineParser.cpp +++ b/clang/lib/Format/UnwrappedLineParser.cpp @@ -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. @@ -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()) diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp index cab495125415aa..3fb55ae2c1f413 100644 --- a/clang/unittests/Format/FormatTest.cpp +++ b/clang/unittests/Format/FormatTest.cpp @@ -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. diff --git a/clang/unittests/Format/TokenAnnotatorTest.cpp b/clang/unittests/Format/TokenAnnotatorTest.cpp index 64b2abac5cce53..b1e1e70a1abbf0 100644 --- a/clang/unittests/Format/TokenAnnotatorTest.cpp +++ b/clang/unittests/Format/TokenAnnotatorTest.cpp @@ -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); _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits