[clang-format] fix nested angle brackets parse inside concept definition
authorSergey Semushin <predelnik@gmail.com>
Wed, 11 May 2022 11:38:35 +0000 (13:38 +0200)
committerMarek Kurdej <marek.kurdej+llvm.org@gmail.com>
Wed, 11 May 2022 12:02:51 +0000 (14:02 +0200)
Due to how parseBracedList always stopped on the first closing angle
bracket and was used in parsing angle bracketed expression inside concept
definition, nested brackets inside concepts were parsed incorrectly.

nextToken() call before calling parseBracedList is required because
we were processing opening angle bracket inside parseBracedList second
time leading to incorrect logic after my fix.

Fixes https://github.com/llvm/llvm-project/issues/54943
Fixes https://github.com/llvm/llvm-project/issues/54837

Reviewed By: HazardyKnusperkeks, curdeius

Differential Revision: https://reviews.llvm.org/D123896

clang/lib/Format/UnwrappedLineParser.cpp
clang/unittests/Format/FormatTest.cpp
clang/unittests/Format/TokenAnnotatorTest.cpp

index f13e3d7..f90e8fc 100644 (file)
@@ -2180,7 +2180,8 @@ bool UnwrappedLineParser::parseBracedList(bool ContinueOnSemicolons,
       parseBracedList();
       break;
     case tok::less:
-      if (Style.Language == FormatStyle::LK_Proto) {
+      if (Style.Language == FormatStyle::LK_Proto ||
+          ClosingBraceKind == tok::greater) {
         nextToken();
         parseBracedList(/*ContinueOnSemicolons=*/false, /*IsEnum=*/false,
                         /*ClosingBraceKind=*/tok::greater);
@@ -3220,6 +3221,7 @@ void UnwrappedLineParser::parseConstraintExpression() {
       if (!FormatTok->is(tok::less))
         return;
 
+      nextToken();
       parseBracedList(/*ContinueOnSemicolons=*/false, /*IsEnum=*/false,
                       /*ClosingBraceKind=*/tok::greater);
       break;
@@ -3260,9 +3262,11 @@ void UnwrappedLineParser::parseConstraintExpression() {
 
       // Read identifier with optional template declaration.
       nextToken();
-      if (FormatTok->is(tok::less))
+      if (FormatTok->is(tok::less)) {
+        nextToken();
         parseBracedList(/*ContinueOnSemicolons=*/false, /*IsEnum=*/false,
                         /*ClosingBraceKind=*/tok::greater);
+      }
       break;
     }
   } while (!eof());
index 78f7eee..8f9c721 100644 (file)
@@ -24114,6 +24114,12 @@ TEST_F(FormatTest, Concepts) {
   verifyFormat("template <class T, class T2>\n"
                "concept Same = __is_same_as<T, T2>;");
 
+  verifyFormat(
+      "template <class _InIt, class _OutIt>\n"
+      "concept _Can_reread_dest =\n"
+      "    std::forward_iterator<_OutIt> &&\n"
+      "    std::same_as<std::iter_value_t<_InIt>, std::iter_value_t<_OutIt>>;");
+
   auto Style = getLLVMStyle();
   Style.BreakBeforeConceptDeclarations = FormatStyle::BBCDS_Allowed;
 
index 3d2c3a4..138cab9 100644 (file)
@@ -390,6 +390,18 @@ TEST_F(TokenAnnotatorTest, UnderstandsRequiresClausesAndConcepts) {
   EXPECT_TOKEN(Tokens[21], tok::r_brace, TT_Unknown);
   EXPECT_EQ(Tokens[21]->MatchingParen, Tokens[15]);
   EXPECT_TRUE(Tokens[21]->ClosesRequiresClause);
+
+  Tokens =
+      annotate("template <class A, class B> concept C ="
+               "std::same_as<std::iter_value_t<A>, std::iter_value_t<B>>;");
+  ASSERT_EQ(Tokens.size(), 31u) << Tokens;
+  EXPECT_TOKEN(Tokens[8], tok::kw_concept, TT_Unknown);
+  EXPECT_TOKEN(Tokens[14], tok::less, TT_TemplateOpener);
+  EXPECT_TOKEN(Tokens[18], tok::less, TT_TemplateOpener);
+  EXPECT_TOKEN(Tokens[20], tok::greater, TT_TemplateCloser);
+  EXPECT_TOKEN(Tokens[25], tok::less, TT_TemplateOpener);
+  EXPECT_TOKEN(Tokens[27], tok::greater, TT_TemplateCloser);
+  EXPECT_TOKEN(Tokens[28], tok::greater, TT_TemplateCloser);
 }
 
 TEST_F(TokenAnnotatorTest, UnderstandsRequiresExpressions) {