Index: clang/lib/Format/UnwrappedLineParser.h =================================================================== --- clang/lib/Format/UnwrappedLineParser.h +++ clang/lib/Format/UnwrappedLineParser.h @@ -133,6 +133,7 @@ bool parseEnum(); bool parseStructLike(); void parseConcept(); + bool parseRequires(); void parseRequiresClause(); void parseRequiresExpression(); void parseConstraintExpression(); Index: clang/lib/Format/UnwrappedLineParser.cpp =================================================================== --- clang/lib/Format/UnwrappedLineParser.cpp +++ clang/lib/Format/UnwrappedLineParser.cpp @@ -1537,9 +1537,12 @@ case tok::kw_concept: parseConcept(); return; - case tok::kw_requires: - parseRequiresClause(); - return; + case tok::kw_requires: { + bool Return = parseRequires(); + if (Return) + return; + break; + } case tok::kw_enum: // Ignore if this is part of "template is(tok::less)) { @@ -2783,6 +2786,120 @@ addUnwrappedLine(); } +/// \brief Parses a requires, decides if it is a clause or an expression. +/// \pre The current token has to be the requires keyword. +/// \returns If it parsed a clause. +bool clang::format::UnwrappedLineParser::parseRequires() { + assert(FormatTok->Tok.is(tok::kw_requires) && "'requires' expected"); + auto *PreviousNonComment = FormatTok->getPreviousNonComment(); + + auto ProbablyExpressionAfterAmpOrAmpAmp = [PreviousNonComment] { + assert(PreviousNonComment->isOneOf(tok::amp, tok::ampamp)); + // This one is really tricky, since most tokens doesn't have a type yet. + // The & or && can be binary operators, then we have a requires + // expression. But they also can be r-value overload indicators, then + // we have a trailing requires clause. We try to detect the latter and + // default to the expressions. + + auto PrevPrev = PreviousNonComment->getPreviousNonComment(); + if (!PrevPrev) { + // No Token is invalid code, just do whatever you want. + return true; + } + + switch (PrevPrev->Tok.getKind()) { + case tok::kw_const: + // void foo() const & requires... + return false; + case tok::r_paren: + // We differentiate after the switch. + break; + default: + // Is most definitly an expression. + return true; + } + + auto RParen = PrevPrev; + auto LastParenContent = RParen->getPreviousNonComment(); + + if (!LastParenContent) { + // No Token is invalid code, just do whatever you want. + return true; + } + + switch (LastParenContent->Tok.getKind()) { + case tok::r_paren: + // Nested parens, probably not a clause. + return true; + case tok::l_paren: + // FIXME: We need an annotation on the paren to really know if it is a + // function call: + // ... foo() && requires ... + // or a declaration: + // void foo() && requires ... + // there is no distinction possible right now. We go for the latter, + // because it's more likely to appear in code. + return false; + case tok::amp: + case tok::ampamp: + case tok::star: + // A function declaration where the parameter name is omitted: + // void foo(Type&&) && requires ... + return false; + case tok::identifier: + // We have to look further... + break; + default: + if (LastParenContent->isSimpleTypeSpecifier()) { + // Definetly function delcaration. + return false; + } + return true; + } + + auto BeforeLastParenContent = LastParenContent->getPreviousNonComment(); + if (!BeforeLastParenContent) { + // No Token is invalid code, just do whatever you want. + return true; + } + + switch (BeforeLastParenContent->Tok.getKind()) { + case tok::l_paren: + // FIXME: We need an annotation on the paren to really know if it is a + // function call: + // ... foo(variable) && requires ... + // or a declaration: + // void foo(Type /*with omitted*/) && requires ... + // there is no distinction possible right now. We go for the former, + // because it's more likely to appear in code. + return true; + case tok::identifier: + // Two identifiers is, without macros, a parameter type and value, thus a + // clause. + return false; + default: + if (BeforeLastParenContent->isSimpleTypeSpecifier()) { + // Definetly function delcaration. + return false; + } + break; + } + return true; + }; + + if (PreviousNonComment && + !PreviousNonComment->isOneOf(tok::greater, tok::r_paren, tok::kw_noexcept, + tok::kw_const, + TT_RequiresExpressionLBrace) && + (!PreviousNonComment->isOneOf(tok::amp, tok::ampamp) || + ProbablyExpressionAfterAmpOrAmpAmp())) { + parseRequiresExpression(); + return false; + } + parseRequiresClause(); + return true; +} + /// \brief Parses a requires clause. /// \pre The current token needs to be the requires keyword. /// \sa parseRequiresExpression Index: clang/unittests/Format/TokenAnnotatorTest.cpp =================================================================== --- clang/unittests/Format/TokenAnnotatorTest.cpp +++ clang/unittests/Format/TokenAnnotatorTest.cpp @@ -119,6 +119,9 @@ " { t.foo() };\n" "} && Bar && Baz;"); ASSERT_EQ(Tokens.size(), 35u) << Tokens; + EXPECT_TOKEN(Tokens[8], tok::kw_requires, TT_RequiresExpression); + EXPECT_TOKEN(Tokens[9], tok::l_paren, TT_RequiresExpressionLParen); + EXPECT_TOKEN(Tokens[13], tok::l_brace, TT_RequiresExpressionLBrace); EXPECT_TOKEN(Tokens[23], tok::ampamp, TT_BinaryOperator); EXPECT_TOKEN(Tokens[28], tok::ampamp, TT_BinaryOperator); @@ -126,6 +129,7 @@ "requires C1 && (C21 || C22 && C2e) && C3\n" "struct Foo;"); ASSERT_EQ(Tokens.size(), 36u) << Tokens; + EXPECT_TOKEN(Tokens[5], tok::kw_requires, TT_RequiresClause); EXPECT_TOKEN(Tokens[6], tok::identifier, TT_Unknown); EXPECT_EQ(Tokens[6]->FakeLParens.size(), 1u); EXPECT_TOKEN(Tokens[10], tok::ampamp, TT_BinaryOperator); @@ -141,6 +145,7 @@ "requires (C1 && (C21 || C22 && C2e) && C3)\n" "struct Foo;"); ASSERT_EQ(Tokens.size(), 38u) << Tokens; + EXPECT_TOKEN(Tokens[5], tok::kw_requires, TT_RequiresClause); EXPECT_TOKEN(Tokens[7], tok::identifier, TT_Unknown); EXPECT_EQ(Tokens[7]->FakeLParens.size(), 1u); EXPECT_TOKEN(Tokens[11], tok::ampamp, TT_BinaryOperator); @@ -151,6 +156,72 @@ EXPECT_EQ(Tokens[32]->FakeRParens, 1u); EXPECT_TOKEN(Tokens[33], tok::r_paren, TT_Unknown); EXPECT_TRUE(Tokens[33]->ClosesRequiresClause); + + Tokens = annotate("template \n" + "void foo(T) noexcept requires Bar;"); + ASSERT_EQ(Tokens.size(), 18u) << Tokens; + EXPECT_TOKEN(Tokens[11], tok::kw_requires, TT_RequiresClause); + + Tokens = annotate("template \n" + "struct S {\n" + " void foo() const requires Bar;\n" + " void bar() const & requires Baz;\n" + " void bar() && requires Baz2;\n" + " void baz() const & noexcept requires Baz;\n" + " void baz() && noexcept requires Baz2;\n" + "};\n" + "\n" + "void S::bar() const & requires Baz { }"); + ASSERT_EQ(Tokens.size(), 85u) << Tokens; + EXPECT_TOKEN(Tokens[13], tok::kw_requires, TT_RequiresClause); + EXPECT_TOKEN(Tokens[25], tok::kw_requires, TT_RequiresClause); + EXPECT_TOKEN(Tokens[36], tok::kw_requires, TT_RequiresClause); + EXPECT_TOKEN(Tokens[49], tok::kw_requires, TT_RequiresClause); + EXPECT_TOKEN(Tokens[61], tok::kw_requires, TT_RequiresClause); + EXPECT_TOKEN(Tokens[77], tok::kw_requires, TT_RequiresClause); +} + +TEST_F(TokenAnnotatorTest, UnderstandsRequiresExpressions) { + auto Tokens = annotate("bool b = requires(int i) { i + 5; };"); + ASSERT_EQ(Tokens.size(), 16u) << Tokens; + EXPECT_TOKEN(Tokens[3], tok::kw_requires, TT_RequiresExpression); + EXPECT_TOKEN(Tokens[4], tok::l_paren, TT_RequiresExpressionLParen); + EXPECT_TOKEN(Tokens[8], tok::l_brace, TT_RequiresExpressionLBrace); + + Tokens = annotate("if (requires(int i) { i + 5; }) return;"); + ASSERT_EQ(Tokens.size(), 17u) << Tokens; + EXPECT_TOKEN(Tokens[2], tok::kw_requires, TT_RequiresExpression); + EXPECT_TOKEN(Tokens[3], tok::l_paren, TT_RequiresExpressionLParen); + EXPECT_TOKEN(Tokens[7], tok::l_brace, TT_RequiresExpressionLBrace); + + Tokens = annotate("if (func() && requires(int i) { i + 5; }) return;"); + ASSERT_EQ(Tokens.size(), 21u) << Tokens; + EXPECT_TOKEN(Tokens[6], tok::kw_requires, TT_RequiresExpression); + EXPECT_TOKEN(Tokens[7], tok::l_paren, TT_RequiresExpressionLParen); + EXPECT_TOKEN(Tokens[11], tok::l_brace, TT_RequiresExpressionLBrace); + + Tokens = annotate("template \n" + "concept C = requires(T T) {\n" + " requires Bar && Foo;\n" + "};"); + ASSERT_EQ(Tokens.size(), 28u) << Tokens; + EXPECT_TOKEN(Tokens[8], tok::kw_requires, TT_RequiresExpression); + EXPECT_TOKEN(Tokens[9], tok::l_paren, TT_RequiresExpressionLParen); + EXPECT_TOKEN(Tokens[13], tok::l_brace, TT_RequiresExpressionLBrace); + EXPECT_TOKEN(Tokens[14], tok::kw_requires, + TT_RequiresClauseInARequiresExpression); + + Tokens = annotate("template \n" + "concept C = requires(T T) {\n" + " { t.func() } -> std::same_as;" + " requires Bar && Foo;\n" + "};"); + ASSERT_EQ(Tokens.size(), 43u) << Tokens; + EXPECT_TOKEN(Tokens[8], tok::kw_requires, TT_RequiresExpression); + EXPECT_TOKEN(Tokens[9], tok::l_paren, TT_RequiresExpressionLParen); + EXPECT_TOKEN(Tokens[13], tok::l_brace, TT_RequiresExpressionLBrace); + EXPECT_TOKEN(Tokens[29], tok::kw_requires, + TT_RequiresClauseInARequiresExpression); } TEST_F(TokenAnnotatorTest, RequiresDoesNotChangeParsingOfTheRest) {