[clang] 5409fb3 - [clang-format] Annotate lambdas with requires clauses.
Emilia Dreamer via cfe-commits
cfe-commits at lists.llvm.org
Sat Mar 25 18:42:45 PDT 2023
Author: Emilia Dreamer
Date: 2023-03-26T04:38:26+03:00
New Revision: 5409fb38372dbf65a94725ccefab2b993fbb7a9b
URL: https://github.com/llvm/llvm-project/commit/5409fb38372dbf65a94725ccefab2b993fbb7a9b
DIFF: https://github.com/llvm/llvm-project/commit/5409fb38372dbf65a94725ccefab2b993fbb7a9b.diff
LOG: [clang-format] Annotate lambdas with requires clauses.
The C++ grammar allows lambdas to have a *requires-clause* in two
places, either directly after the *template-parameter-list*, such as:
`[] <typename T> requires foo<T> (T t) { ... };`
Or, at the end of the *lambda-declarator* (before the lambda's body):
`[] <typename T> (T t) requires foo<T> { ... };`
Previously, these cases weren't handled at all, resulting in weird
results.
Note that this commit only handles token annotation, so the actual
formatting still ends up suboptimal. This is mostly because I do not yet
know how to approach making the requires clause formatting of lambdas
match the formatting for functions.
Fixes https://github.com/llvm/llvm-project/issues/61269
Reviewed By: HazardyKnusperkeks, owenpan
Differential Revision: https://reviews.llvm.org/D145642
Added:
Modified:
clang/lib/Format/UnwrappedLineParser.cpp
clang/unittests/Format/TokenAnnotatorTest.cpp
Removed:
################################################################################
diff --git a/clang/lib/Format/UnwrappedLineParser.cpp b/clang/lib/Format/UnwrappedLineParser.cpp
index f08a51f93e2f..9b17c28280f1 100644
--- a/clang/lib/Format/UnwrappedLineParser.cpp
+++ b/clang/lib/Format/UnwrappedLineParser.cpp
@@ -2134,7 +2134,7 @@ bool UnwrappedLineParser::tryToParseLambda() {
case tok::l_brace:
break;
case tok::l_paren:
- parseParens();
+ parseParens(/*AmpAmpTokenType=*/TT_PointerOrReference);
break;
case tok::l_square:
parseSquare();
@@ -2211,6 +2211,12 @@ bool UnwrappedLineParser::tryToParseLambda() {
SeenArrow = true;
nextToken();
break;
+ case tok::kw_requires: {
+ auto *RequiresToken = FormatTok;
+ nextToken();
+ parseRequiresClause(RequiresToken);
+ break;
+ }
default:
return true;
}
@@ -3371,6 +3377,17 @@ void UnwrappedLineParser::parseConstraintExpression() {
// lambda to be possible.
// template <typename T> requires requires { ... } [[nodiscard]] ...;
bool LambdaNextTimeAllowed = true;
+
+ // Within lambda declarations, it is permitted to put a requires clause after
+ // its template parameter list, which would place the requires clause right
+ // before the parentheses of the parameters of the lambda declaration. Thus,
+ // we track if we expect to see grouping parentheses at all.
+ // Without this check, `requires foo<T> (T t)` in the below example would be
+ // seen as the whole requires clause, accidentally eating the parameters of
+ // the lambda.
+ // [&]<typename T> requires foo<T> (T t) { ... };
+ bool TopLevelParensAllowed = true;
+
do {
bool LambdaThisTimeAllowed = std::exchange(LambdaNextTimeAllowed, false);
@@ -3383,7 +3400,10 @@ void UnwrappedLineParser::parseConstraintExpression() {
}
case tok::l_paren:
+ if (!TopLevelParensAllowed)
+ return;
parseParens(/*AmpAmpTokenType=*/TT_BinaryOperator);
+ TopLevelParensAllowed = false;
break;
case tok::l_square:
@@ -3407,6 +3427,7 @@ void UnwrappedLineParser::parseConstraintExpression() {
FormatTok->setFinalizedType(TT_BinaryOperator);
nextToken();
LambdaNextTimeAllowed = true;
+ TopLevelParensAllowed = true;
break;
case tok::comma:
@@ -3430,6 +3451,7 @@ void UnwrappedLineParser::parseConstraintExpression() {
case tok::star:
case tok::slash:
LambdaNextTimeAllowed = true;
+ TopLevelParensAllowed = true;
// Just eat them.
nextToken();
break;
@@ -3438,6 +3460,7 @@ void UnwrappedLineParser::parseConstraintExpression() {
case tok::coloncolon:
case tok::kw_true:
case tok::kw_false:
+ TopLevelParensAllowed = false;
// Just eat them.
nextToken();
break;
@@ -3486,6 +3509,7 @@ void UnwrappedLineParser::parseConstraintExpression() {
parseBracedList(/*ContinueOnSemicolons=*/false, /*IsEnum=*/false,
/*ClosingBraceKind=*/tok::greater);
}
+ TopLevelParensAllowed = false;
break;
}
} while (!eof());
diff --git a/clang/unittests/Format/TokenAnnotatorTest.cpp b/clang/unittests/Format/TokenAnnotatorTest.cpp
index 3a6fb0e9e4b3..997630d08e2e 100644
--- a/clang/unittests/Format/TokenAnnotatorTest.cpp
+++ b/clang/unittests/Format/TokenAnnotatorTest.cpp
@@ -1279,6 +1279,110 @@ TEST_F(TokenAnnotatorTest, UnderstandsLambdas) {
EXPECT_TOKEN(Tokens[0], tok::l_square, TT_LambdaLSquare);
EXPECT_TOKEN(Tokens[2], tok::less, TT_TemplateOpener);
EXPECT_TOKEN(Tokens[7], tok::l_brace, TT_LambdaLBrace);
+
+ // Lambdas with a requires-clause
+ Tokens = annotate("[] <typename T> (T t) requires Bar<T> {}");
+ ASSERT_EQ(Tokens.size(), 18u) << Tokens;
+ EXPECT_TOKEN(Tokens[0], tok::l_square, TT_LambdaLSquare);
+ EXPECT_TOKEN(Tokens[2], tok::less, TT_TemplateOpener);
+ EXPECT_TOKEN(Tokens[10], tok::kw_requires, TT_RequiresClause);
+ EXPECT_TRUE(Tokens[14]->ClosesRequiresClause);
+ EXPECT_TOKEN(Tokens[15], tok::l_brace, TT_LambdaLBrace);
+
+ Tokens = annotate("[] <typename T> (T &&t) requires Bar<T> {}");
+ ASSERT_EQ(Tokens.size(), 19u) << Tokens;
+ EXPECT_TOKEN(Tokens[0], tok::l_square, TT_LambdaLSquare);
+ EXPECT_TOKEN(Tokens[2], tok::less, TT_TemplateOpener);
+ EXPECT_TOKEN(Tokens[8], tok::ampamp, TT_PointerOrReference);
+ EXPECT_TOKEN(Tokens[11], tok::kw_requires, TT_RequiresClause);
+ EXPECT_TRUE(Tokens[15]->ClosesRequiresClause);
+ EXPECT_TOKEN(Tokens[16], tok::l_brace, TT_LambdaLBrace);
+
+ Tokens = annotate("[] <typename T> (T t) requires Foo<T> || Bar<T> {}");
+ ASSERT_EQ(Tokens.size(), 23u) << Tokens;
+ EXPECT_TOKEN(Tokens[0], tok::l_square, TT_LambdaLSquare);
+ EXPECT_TOKEN(Tokens[2], tok::less, TT_TemplateOpener);
+ EXPECT_TOKEN(Tokens[10], tok::kw_requires, TT_RequiresClause);
+ EXPECT_TRUE(Tokens[19]->ClosesRequiresClause);
+ EXPECT_TOKEN(Tokens[20], tok::l_brace, TT_LambdaLBrace);
+
+ Tokens = annotate("[] <typename T> (T t) -> T requires Bar<T> {}");
+ ASSERT_EQ(Tokens.size(), 20u) << Tokens;
+ EXPECT_TOKEN(Tokens[0], tok::l_square, TT_LambdaLSquare);
+ EXPECT_TOKEN(Tokens[2], tok::less, TT_TemplateOpener);
+ EXPECT_TOKEN(Tokens[10], tok::arrow, TT_LambdaArrow);
+ EXPECT_TOKEN(Tokens[12], tok::kw_requires, TT_RequiresClause);
+ EXPECT_TRUE(Tokens[16]->ClosesRequiresClause);
+ EXPECT_TOKEN(Tokens[17], tok::l_brace, TT_LambdaLBrace);
+
+ Tokens = annotate("[] <typename T> requires Bar<T> (T t) {}");
+ ASSERT_EQ(Tokens.size(), 18u) << Tokens;
+ EXPECT_TOKEN(Tokens[0], tok::l_square, TT_LambdaLSquare);
+ EXPECT_TOKEN(Tokens[2], tok::less, TT_TemplateOpener);
+ EXPECT_TOKEN(Tokens[6], tok::kw_requires, TT_RequiresClause);
+ EXPECT_TRUE(Tokens[10]->ClosesRequiresClause);
+ EXPECT_TOKEN(Tokens[15], tok::l_brace, TT_LambdaLBrace);
+
+ Tokens = annotate("[] <typename T> requires Bar<T> (T &&t) {}");
+ ASSERT_EQ(Tokens.size(), 19u) << Tokens;
+ EXPECT_TOKEN(Tokens[0], tok::l_square, TT_LambdaLSquare);
+ EXPECT_TOKEN(Tokens[2], tok::less, TT_TemplateOpener);
+ EXPECT_TOKEN(Tokens[6], tok::kw_requires, TT_RequiresClause);
+ EXPECT_TRUE(Tokens[10]->ClosesRequiresClause);
+ EXPECT_TOKEN(Tokens[13], tok::ampamp, TT_PointerOrReference);
+ EXPECT_TOKEN(Tokens[16], tok::l_brace, TT_LambdaLBrace);
+
+ Tokens = annotate("[] <typename T> requires Foo<T> || Bar<T> (T t) {}");
+ ASSERT_EQ(Tokens.size(), 23u) << Tokens;
+ EXPECT_TOKEN(Tokens[0], tok::l_square, TT_LambdaLSquare);
+ EXPECT_TOKEN(Tokens[2], tok::less, TT_TemplateOpener);
+ EXPECT_TOKEN(Tokens[6], tok::kw_requires, TT_RequiresClause);
+ EXPECT_TRUE(Tokens[15]->ClosesRequiresClause);
+ EXPECT_TOKEN(Tokens[20], tok::l_brace, TT_LambdaLBrace);
+
+ Tokens = annotate("[] <typename T> requires true (T&& t) {}");
+ ASSERT_EQ(Tokens.size(), 16u) << Tokens;
+ EXPECT_TOKEN(Tokens[0], tok::l_square, TT_LambdaLSquare);
+ EXPECT_TOKEN(Tokens[2], tok::less, TT_TemplateOpener);
+ EXPECT_TOKEN(Tokens[6], tok::kw_requires, TT_RequiresClause);
+ EXPECT_TRUE(Tokens[7]->ClosesRequiresClause);
+ EXPECT_TOKEN(Tokens[10], tok::ampamp, TT_PointerOrReference);
+ EXPECT_TOKEN(Tokens[13], tok::l_brace, TT_LambdaLBrace);
+
+ Tokens = annotate("[] <typename T> requires Bar<T> {}");
+ ASSERT_EQ(Tokens.size(), 14u) << Tokens;
+ EXPECT_TOKEN(Tokens[0], tok::l_square, TT_LambdaLSquare);
+ EXPECT_TOKEN(Tokens[2], tok::less, TT_TemplateOpener);
+ EXPECT_TOKEN(Tokens[6], tok::kw_requires, TT_RequiresClause);
+ EXPECT_TRUE(Tokens[10]->ClosesRequiresClause);
+ EXPECT_TOKEN(Tokens[11], tok::l_brace, TT_LambdaLBrace);
+
+ Tokens = annotate("[] <typename T> requires Bar<T> noexcept {}");
+ ASSERT_EQ(Tokens.size(), 15u) << Tokens;
+ EXPECT_TOKEN(Tokens[0], tok::l_square, TT_LambdaLSquare);
+ EXPECT_TOKEN(Tokens[2], tok::less, TT_TemplateOpener);
+ EXPECT_TOKEN(Tokens[6], tok::kw_requires, TT_RequiresClause);
+ EXPECT_TRUE(Tokens[10]->ClosesRequiresClause);
+ EXPECT_TOKEN(Tokens[12], tok::l_brace, TT_LambdaLBrace);
+
+ Tokens = annotate("[] <typename T> requires Bar<T> -> T {}");
+ ASSERT_EQ(Tokens.size(), 16u) << Tokens;
+ EXPECT_TOKEN(Tokens[0], tok::l_square, TT_LambdaLSquare);
+ EXPECT_TOKEN(Tokens[2], tok::less, TT_TemplateOpener);
+ EXPECT_TOKEN(Tokens[6], tok::kw_requires, TT_RequiresClause);
+ EXPECT_TRUE(Tokens[10]->ClosesRequiresClause);
+ EXPECT_TOKEN(Tokens[11], tok::arrow, TT_LambdaArrow);
+ EXPECT_TOKEN(Tokens[13], tok::l_brace, TT_LambdaLBrace);
+
+ Tokens = annotate("[] <typename T> requires Foo<T> (T t) requires Bar<T> {}");
+ ASSERT_EQ(Tokens.size(), 23u) << Tokens;
+ EXPECT_TOKEN(Tokens[0], tok::l_square, TT_LambdaLSquare);
+ EXPECT_TOKEN(Tokens[2], tok::less, TT_TemplateOpener);
+ EXPECT_TOKEN(Tokens[6], tok::kw_requires, TT_RequiresClause);
+ EXPECT_TRUE(Tokens[10]->ClosesRequiresClause);
+ EXPECT_TOKEN(Tokens[15], tok::kw_requires, TT_RequiresClause);
+ EXPECT_TRUE(Tokens[19]->ClosesRequiresClause);
+ EXPECT_TOKEN(Tokens[20], tok::l_brace, TT_LambdaLBrace);
}
TEST_F(TokenAnnotatorTest, UnderstandsFunctionAnnotations) {
More information about the cfe-commits
mailing list