[clang-format] Preserve AmpAmpTokenType in nested parentheses
authorEmilia Kond <emilia@rymiel.space>
Mon, 26 Jun 2023 09:35:47 +0000 (12:35 +0300)
committerEmilia Kond <emilia@rymiel.space>
Mon, 26 Jun 2023 09:39:16 +0000 (12:39 +0300)
When parsing a requires clause, the UnwrappedLineParser would delegate to
parseParens with an AmpAmpTokenType set to BinaryOperator. However,
parseParens would not carry this over into any nested parens, meaning it
could assign a different token type to an && in a requires clause.

This patch makes sure parseParens inherits its parameter when performing
a recursive call.

Fixes https://github.com/llvm/llvm-project/issues/63251

Reviewed By: HazardyKnusperkeks, owenpan, MyDeveloperDay

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

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

index 66c1205..e9da145 100644 (file)
@@ -2431,14 +2431,14 @@ bool UnwrappedLineParser::parseBracedList(bool ContinueOnSemicolons,
 
 /// \brief Parses a pair of parentheses (and everything between them).
 /// \param AmpAmpTokenType If different than TT_Unknown sets this type for all
-/// double ampersands. This only counts for the current parens scope.
+/// double ampersands. This applies for all nested scopes as well.
 void UnwrappedLineParser::parseParens(TokenType AmpAmpTokenType) {
   assert(FormatTok->is(tok::l_paren) && "'(' expected.");
   nextToken();
   do {
     switch (FormatTok->Tok.getKind()) {
     case tok::l_paren:
-      parseParens();
+      parseParens(AmpAmpTokenType);
       if (Style.Language == FormatStyle::LK_Java && FormatTok->is(tok::l_brace))
         parseChildBlock();
       break;
index 6a3ecc6..59b1421 100644 (file)
@@ -906,6 +906,26 @@ TEST_F(TokenAnnotatorTest, UnderstandsRequiresClausesAndConcepts) {
       annotate("auto bar() -> Template<type> requires(is_integral_v<T>) {}");
   ASSERT_EQ(Tokens.size(), 19u) << Tokens;
   EXPECT_TOKEN(Tokens[9], tok::kw_requires, TT_RequiresClause);
+
+  Tokens = annotate("void foo() requires((A<T>) && C) {}");
+  ASSERT_EQ(Tokens.size(), 18u) << Tokens;
+  EXPECT_TOKEN(Tokens[4], tok::kw_requires, TT_RequiresClause);
+  EXPECT_TOKEN(Tokens[12], tok::ampamp, TT_BinaryOperator);
+
+  Tokens = annotate("void foo() requires(((A<T>) && C)) {}");
+  ASSERT_EQ(Tokens.size(), 20u) << Tokens;
+  EXPECT_TOKEN(Tokens[4], tok::kw_requires, TT_RequiresClause);
+  EXPECT_TOKEN(Tokens[13], tok::ampamp, TT_BinaryOperator);
+
+  Tokens = annotate("void foo() requires([](T&&){}(t)) {}");
+  ASSERT_EQ(Tokens.size(), 21u) << Tokens;
+  EXPECT_TOKEN(Tokens[4], tok::kw_requires, TT_RequiresClause);
+  EXPECT_TOKEN(Tokens[10], tok::ampamp, TT_PointerOrReference);
+
+  Tokens = annotate("void foo() requires([](T&& u){}(t)) {}");
+  ASSERT_EQ(Tokens.size(), 22u) << Tokens;
+  EXPECT_TOKEN(Tokens[4], tok::kw_requires, TT_RequiresClause);
+  EXPECT_TOKEN(Tokens[10], tok::ampamp, TT_PointerOrReference);
 }
 
 TEST_F(TokenAnnotatorTest, UnderstandsRequiresExpressions) {