[clang-format] Change heuristic for locating lambda template arguments

Previously, the heuristic was simply to look for template argument-
specific keywords, such as typename, class, template and auto
that are preceded by a left angle bracket <.

This changes the heuristic to instead look for a left angle bracket <
preceded by a right square bracket ], since according to the C++
grammar, the template arguments must *directly* follow the introducer.
(This sort of check might just end up being *too* aggressive)

This patch also adds a bunch more token annotator tests for lambdas,
specifically for some of the stranger forms of lambdas now allowed as
of C++20 or soon-to-be-allowed as part of C++23.

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

This does NOT resolve the FIXME regarding explicit template lists, but
perhaps it gets closer

Differential Revision: https://reviews.llvm.org/D132295
This commit is contained in:
Emilia Dreamer 2022-09-06 12:22:34 +02:00 committed by Björn Schäpers
parent 2adf241592
commit 247613548b
3 changed files with 85 additions and 6 deletions

View File

@ -2210,21 +2210,21 @@ bool UnwrappedLineParser::tryToParseLambda() {
case tok::l_square:
parseSquare();
break;
case tok::less:
assert(FormatTok->Previous);
if (FormatTok->Previous->is(tok::r_square))
InTemplateParameterList = true;
nextToken();
break;
case tok::kw_auto:
case tok::kw_class:
case tok::kw_template:
case tok::kw_typename:
assert(FormatTok->Previous);
if (FormatTok->Previous->is(tok::less))
InTemplateParameterList = true;
nextToken();
break;
case tok::amp:
case tok::star:
case tok::kw_const:
case tok::kw_constexpr:
case tok::comma:
case tok::less:
case tok::greater:
case tok::identifier:
case tok::numeric_constant:

View File

@ -21730,6 +21730,18 @@ TEST_F(FormatTest, FormatsLambdas) {
" g();\n"
" }\n"
"};\n");
verifyFormat("auto L = []<int... T>(T...) {\n"
" {\n"
" f();\n"
" g();\n"
" }\n"
"};");
verifyFormat("auto L = []<Foo... T>(T...) {\n"
" {\n"
" f();\n"
" g();\n"
" }\n"
"};");
// Multiple lambdas in the same parentheses change indentation rules. These
// lambdas are forced to start on new lines.

View File

@ -809,18 +809,85 @@ TEST_F(TokenAnnotatorTest, UnderstandsLambdas) {
Tokens = annotate("[]() -> auto {}");
ASSERT_EQ(Tokens.size(), 9u) << Tokens;
EXPECT_TOKEN(Tokens[0], tok::l_square, TT_LambdaLSquare);
EXPECT_TOKEN(Tokens[4], tok::arrow, TT_LambdaArrow);
EXPECT_TOKEN(Tokens[6], tok::l_brace, TT_LambdaLBrace);
Tokens = annotate("[]() -> auto & {}");
ASSERT_EQ(Tokens.size(), 10u) << Tokens;
EXPECT_TOKEN(Tokens[0], tok::l_square, TT_LambdaLSquare);
EXPECT_TOKEN(Tokens[4], tok::arrow, TT_LambdaArrow);
EXPECT_TOKEN(Tokens[7], tok::l_brace, TT_LambdaLBrace);
Tokens = annotate("[]() -> auto * {}");
ASSERT_EQ(Tokens.size(), 10u) << Tokens;
EXPECT_TOKEN(Tokens[0], tok::l_square, TT_LambdaLSquare);
EXPECT_TOKEN(Tokens[4], tok::arrow, TT_LambdaArrow);
EXPECT_TOKEN(Tokens[7], tok::l_brace, TT_LambdaLBrace);
Tokens = annotate("[] {}");
ASSERT_EQ(Tokens.size(), 5u) << Tokens;
EXPECT_TOKEN(Tokens[0], tok::l_square, TT_LambdaLSquare);
EXPECT_TOKEN(Tokens[2], tok::l_brace, TT_LambdaLBrace);
Tokens = annotate("[] noexcept {}");
ASSERT_EQ(Tokens.size(), 6u) << Tokens;
EXPECT_TOKEN(Tokens[0], tok::l_square, TT_LambdaLSquare);
EXPECT_TOKEN(Tokens[3], tok::l_brace, TT_LambdaLBrace);
Tokens = annotate("[] -> auto {}");
ASSERT_EQ(Tokens.size(), 7u) << Tokens;
EXPECT_TOKEN(Tokens[0], tok::l_square, TT_LambdaLSquare);
EXPECT_TOKEN(Tokens[2], tok::arrow, TT_LambdaArrow);
EXPECT_TOKEN(Tokens[4], tok::l_brace, TT_LambdaLBrace);
Tokens = annotate("[] <typename T> () {}");
ASSERT_EQ(Tokens.size(), 11u) << Tokens;
EXPECT_TOKEN(Tokens[0], tok::l_square, TT_LambdaLSquare);
EXPECT_TOKEN(Tokens[2], tok::less, TT_TemplateOpener);
EXPECT_TOKEN(Tokens[8], tok::l_brace, TT_LambdaLBrace);
Tokens = annotate("[] <typename T> {}");
ASSERT_EQ(Tokens.size(), 9u) << Tokens;
EXPECT_TOKEN(Tokens[0], tok::l_square, TT_LambdaLSquare);
EXPECT_TOKEN(Tokens[2], tok::less, TT_TemplateOpener);
EXPECT_TOKEN(Tokens[6], tok::l_brace, TT_LambdaLBrace);
Tokens = annotate("[] <typename... T> () {}");
ASSERT_EQ(Tokens.size(), 12u) << Tokens;
EXPECT_TOKEN(Tokens[0], tok::l_square, TT_LambdaLSquare);
EXPECT_TOKEN(Tokens[2], tok::less, TT_TemplateOpener);
EXPECT_TOKEN(Tokens[9], tok::l_brace, TT_LambdaLBrace);
Tokens = annotate("[] <typename... T> {}");
ASSERT_EQ(Tokens.size(), 10u) << Tokens;
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);
Tokens = annotate("[] <int... T> () {}");
ASSERT_EQ(Tokens.size(), 12u) << Tokens;
EXPECT_TOKEN(Tokens[0], tok::l_square, TT_LambdaLSquare);
EXPECT_TOKEN(Tokens[2], tok::less, TT_TemplateOpener);
EXPECT_TOKEN(Tokens[9], tok::l_brace, TT_LambdaLBrace);
Tokens = annotate("[] <int... T> {}");
ASSERT_EQ(Tokens.size(), 10u) << Tokens;
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);
Tokens = annotate("[] <Foo... T> () {}");
ASSERT_EQ(Tokens.size(), 12u) << Tokens;
EXPECT_TOKEN(Tokens[0], tok::l_square, TT_LambdaLSquare);
EXPECT_TOKEN(Tokens[2], tok::less, TT_TemplateOpener);
EXPECT_TOKEN(Tokens[9], tok::l_brace, TT_LambdaLBrace);
Tokens = annotate("[] <Foo... T> {}");
ASSERT_EQ(Tokens.size(), 10u) << Tokens;
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);
}
TEST_F(TokenAnnotatorTest, UnderstandsFunctionAnnotations) {