diff --git a/clang/lib/Format/FormatToken.h b/clang/lib/Format/FormatToken.h --- a/clang/lib/Format/FormatToken.h +++ b/clang/lib/Format/FormatToken.h @@ -95,6 +95,8 @@ TYPE(PureVirtualSpecifier) \ TYPE(RangeBasedForLoopColon) \ TYPE(RegexLiteral) \ + TYPE(RequiresClause) \ + TYPE(RequiresExpression) \ TYPE(SelectorName) \ TYPE(StartOfName) \ TYPE(StatementAttributeLikeMacro) \ diff --git a/clang/lib/Format/TokenAnnotator.cpp b/clang/lib/Format/TokenAnnotator.cpp --- a/clang/lib/Format/TokenAnnotator.cpp +++ b/clang/lib/Format/TokenAnnotator.cpp @@ -251,7 +251,8 @@ Contexts.back().IsExpression = false; } else if (Left->Previous && (Left->Previous->isOneOf(tok::kw_static_assert, tok::kw_while, - tok::l_paren, tok::comma) || + tok::l_paren, tok::comma, + TT_RequiresClause) || Left->Previous->isIf() || Left->Previous->is(TT_BinaryOperator))) { // static_assert, if and while usually contain expressions. @@ -1415,7 +1416,8 @@ TT_LambdaArrow, TT_NamespaceMacro, TT_OverloadedOperator, TT_RegexLiteral, TT_TemplateString, TT_ObjCStringLiteral, TT_UntouchableMacroFunc, TT_ConstraintJunctions, - TT_StatementAttributeLikeMacro)) + TT_StatementAttributeLikeMacro, TT_RequiresClause, + TT_RequiresExpression)) CurrentToken->setType(TT_Unknown); CurrentToken->Role.reset(); CurrentToken->MatchingParen = nullptr; diff --git a/clang/lib/Format/UnwrappedLineParser.h b/clang/lib/Format/UnwrappedLineParser.h --- a/clang/lib/Format/UnwrappedLineParser.h +++ b/clang/lib/Format/UnwrappedLineParser.h @@ -117,7 +117,7 @@ bool parseStructLike(); void parseConcept(); void parseRequires(); - void parseRequiresExpression(unsigned int OriginalLevel); + void parseRequiresClauseOrExpression(unsigned int OriginalLevel); void parseConstraintExpression(unsigned int OriginalLevel); void parseJavaEnumBody(); // Parses a record (aka class) as a top level element. If ParseAsExpr is true, diff --git a/clang/lib/Format/UnwrappedLineParser.cpp b/clang/lib/Format/UnwrappedLineParser.cpp --- a/clang/lib/Format/UnwrappedLineParser.cpp +++ b/clang/lib/Format/UnwrappedLineParser.cpp @@ -2436,17 +2436,27 @@ return; nextToken(); if (FormatTok->Tok.is(tok::kw_requires)) { + FormatTok->setType(TT_RequiresExpression); nextToken(); - parseRequiresExpression(Line->Level); + parseRequiresClauseOrExpression(Line->Level); } else { parseConstraintExpression(Line->Level); } } -void UnwrappedLineParser::parseRequiresExpression(unsigned int OriginalLevel) { - // requires (R range) +void UnwrappedLineParser::parseRequiresClauseOrExpression( + unsigned int OriginalLevel) { + // requires (R range) or requires (trait1_v && trait2_v) + assert(FormatTok->Previous && FormatTok->Previous->is(tok::kw_requires)); if (FormatTok->Tok.is(tok::l_paren)) { + bool ParsingClause = FormatTok->Previous->is(TT_RequiresClause); parseParens(); + if (ParsingClause && + !FormatTok->Tok.isOneOf(tok::comment, tok::kw_struct, tok::kw_class, + tok::kw_union, tok::l_brace, tok::semi)) { + // Only break if we start a function. + addUnwrappedLine(); + } if (Style.IndentRequires && OriginalLevel != Line->Level) { addUnwrappedLine(); --Line->Level; @@ -2480,7 +2490,8 @@ nextToken(); } if (FormatTok->Tok.is(tok::kw_requires)) { - parseRequiresExpression(OriginalLevel); + FormatTok->setType(TT_RequiresExpression); + parseRequiresClauseOrExpression(OriginalLevel); } if (FormatTok->Tok.is(tok::less)) { parseBracedList(/*ContinueOnSemicolons=*/false, /*IsEnum=*/false, @@ -2526,15 +2537,26 @@ assert(FormatTok->Tok.is(tok::kw_requires) && "'requires' expected"); unsigned OriginalLevel = Line->Level; - if (FormatTok->Previous && FormatTok->Previous->is(tok::greater)) { - addUnwrappedLine(); - if (Style.IndentRequires) { - Line->Level++; + bool SetClause = false; + if (FormatTok->Previous) { + if (FormatTok->Previous->is(tok::greater)) { + FormatTok->setType(TT_RequiresClause); + SetClause = true; + addUnwrappedLine(); + if (Style.IndentRequires) { + Line->Level++; + } + } else if (FormatTok->Previous->is(tok::r_paren)) { + FormatTok->setType(TT_RequiresClause); + SetClause = true; } } + if (!SetClause) + FormatTok->setType(TT_RequiresExpression); + nextToken(); - parseRequiresExpression(OriginalLevel); + parseRequiresClauseOrExpression(OriginalLevel); } bool UnwrappedLineParser::parseEnum() { diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp --- a/clang/unittests/Format/FormatTest.cpp +++ b/clang/unittests/Format/FormatTest.cpp @@ -22288,6 +22288,114 @@ "requires (std::invocable...>) " "struct constant;", Style); + + verifyFormat("template void func(T);"); + + verifyFormat("template \n" + "requires std::signed_integral && std::signed_integral\n" + "void func(T);"); + verifyFormat("template \n" + "requires(std::is_integral_v && std::is_signed_v)\n" + "void func(T);"); + verifyFormat("template \n" + "requires requires(T &&t) {\n" + " typename T::size_type;\n" + " { t.size() } -> std::same_as;\n" + "}\n" + "func(T);"); + + verifyFormat("template \n" + "requires std::signed_integral && std::signed_integral\n" + "void func(T) {}"); + verifyFormat("template \n" + "requires(std::is_integral_v && std::is_signed_v)\n" + "void func(T) {}"); + verifyFormat("template \n" + "requires requires(T &&t) {\n" + " typename T::size_type;\n" + " { t.size() } -> std::same_as;\n" + "}\n" + "func(T) {}"); + + verifyFormat( + "template void func(T) requires std::signed_integral;"); + verifyFormat("template \n" + "void func(T) requires std::signed_integral && " + "std::signed_integral;"); + verifyFormat( + "template \n" + "void func(T) requires(std::is_integral_v && std::is_signed_v);"); + verifyFormat("template void func(T) requires requires(T &&t) {\n" + " typename T::size_type;\n" + " { t.size() } -> std::same_as;\n" + "};"); + + verifyFormat( + "template void func(T) requires std::signed_integral {}"); + verifyFormat("template \n" + "void func(T) requires std::signed_integral && " + "std::signed_integral {}"); + verifyFormat( + "template \n" + "void func(T) requires(std::is_integral_v && std::is_signed_v) {}"); + verifyFormat("template void func(T) requires requires(T &&t) {\n" + " typename T::size_type;\n" + " { t.size() } -> std::same_as;\n" + "}\n" + "{}"); + + Style = getLLVMStyle(); + Style.IndentRequires = true; + + verifyFormat("template \n" + " requires std::signed_integral && std::signed_integral\n" + "void func(T);", + Style); + verifyFormat("template \n" + " requires(std::is_integral_v && std::is_signed_v)\n" + "void func(T);", + Style); + verifyFormat("template \n" + " requires requires(T &&t) {\n" + " typename T::size_type;\n" + " { t.size() } -> std::same_as;\n" + " }\n" + "func(T);", + Style); + + verifyFormat("template \n" + " requires std::signed_integral && std::signed_integral\n" + "void func(T) {}", + Style); + verifyFormat("template \n" + " requires(std::is_integral_v && std::is_signed_v)\n" + "void func(T) {}", + Style); + verifyFormat("template \n" + " requires requires(T &&t) {\n" + " typename T::size_type;\n" + " { t.size() } -> std::same_as;\n" + " }\n" + "func(T) {}", + Style); + + verifyFormat( + "template void func(T) requires std::signed_integral {}", + Style); + verifyFormat("template \n" + "void func(T) requires std::signed_integral && " + "std::signed_integral {}", + Style); + verifyFormat( + "template \n" + "void func(T) requires(std::is_integral_v && std::is_signed_v) {}", + Style); + verifyFormat("template void func(T) requires requires(T &&t) {\n" + " typename T::size_type;\n" + " { t.size() } -> std::same_as;\n" + "}\n" + "{}", + Style); } TEST_F(FormatTest, StatementAttributeLikeMacros) {