diff --git a/clang/docs/ClangFormatStyleOptions.rst b/clang/docs/ClangFormatStyleOptions.rst --- a/clang/docs/ClangFormatStyleOptions.rst +++ b/clang/docs/ClangFormatStyleOptions.rst @@ -3752,6 +3752,27 @@ IF (...) vs. IF(...) + * ``bool AfterRequiresClause`` If ``true``, put space between requires keyword in a requires clause and + opening parentheses, if is are one. + + .. code-block:: c++ + + true: false: + template vs. template + requires (A && B) requires(A && B) + ... ... + + * ``bool AfterRequiresExpression`` If ``true``, put space between requires keyword in a requires expression + and opening parentheses. + + .. code-block:: c++ + + true: false: + template vs. template + requires (T t) { requires(T t) { + ... ... + } } + * ``bool BeforeNonEmptyParentheses`` If ``true``, put a space before opening parentheses only if the parentheses are not empty. diff --git a/clang/include/clang/Format/Format.h b/clang/include/clang/Format/Format.h --- a/clang/include/clang/Format/Format.h +++ b/clang/include/clang/Format/Format.h @@ -3367,6 +3367,25 @@ /// /// \endcode bool AfterIfMacros; + /// If ``true``, put space between requires keyword in a requires clause and + /// opening parentheses, if is are one. + /// \code + /// true: false: + /// template vs. template + /// requires (A && B) requires(A && B) + /// ... ... + /// \endcode + bool AfterRequiresClause; + /// If ``true``, put space between requires keyword in a requires expression + /// and opening parentheses. + /// \code + /// true: false: + /// template vs. template + /// requires (T t) { requires(T t) { + /// ... ... + /// } } + /// \endcode + bool AfterRequiresExpression; /// If ``true``, put a space before opening parentheses only if the /// parentheses are not empty. /// \code @@ -3380,6 +3399,7 @@ : AfterControlStatements(false), AfterForeachMacros(false), AfterFunctionDeclarationName(false), AfterFunctionDefinitionName(false), AfterIfMacros(false), + AfterRequiresClause(false), AfterRequiresExpression(false), BeforeNonEmptyParentheses(false) {} bool operator==(const SpaceBeforeParensCustom &Other) const { @@ -3389,6 +3409,8 @@ Other.AfterFunctionDeclarationName && AfterFunctionDefinitionName == Other.AfterFunctionDefinitionName && AfterIfMacros == Other.AfterIfMacros && + AfterRequiresClause == Other.AfterRequiresClause && + AfterRequiresExpression == Other.AfterRequiresExpression && BeforeNonEmptyParentheses == Other.BeforeNonEmptyParentheses; } }; diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp --- a/clang/lib/Format/Format.cpp +++ b/clang/lib/Format/Format.cpp @@ -855,6 +855,8 @@ IO.mapOptional("AfterFunctionDeclarationName", Spacing.AfterFunctionDeclarationName); IO.mapOptional("AfterIfMacros", Spacing.AfterIfMacros); + IO.mapOptional("AfterRequiresClause", Spacing.AfterRequiresClause); + IO.mapOptional("AfterRequiresExpression", Spacing.AfterRequiresExpression); IO.mapOptional("BeforeNonEmptyParentheses", Spacing.BeforeNonEmptyParentheses); } @@ -1214,6 +1216,7 @@ LLVMStyle.SpaceBeforeCtorInitializerColon = true; LLVMStyle.SpaceBeforeInheritanceColon = true; LLVMStyle.SpaceBeforeParens = FormatStyle::SBPO_ControlStatements; + LLVMStyle.SpaceBeforeParensOptions = {}; LLVMStyle.SpaceBeforeParensOptions.AfterControlStatements = true; LLVMStyle.SpaceBeforeParensOptions.AfterForeachMacros = true; LLVMStyle.SpaceBeforeParensOptions.AfterIfMacros = true; 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 @@ -3137,8 +3137,16 @@ if (Right.is(tok::l_paren)) { if (Left.is(TT_TemplateCloser) && Right.isNot(TT_FunctionTypeLParen)) return spaceRequiredBeforeParens(Right); - if (Left.is(tok::kw_requires)) - return spaceRequiredBeforeParens(Right); + if (Left.is(tok::kw_requires)) { + if (spaceRequiredBeforeParens(Right)) + return true; + if (Left.is(TT_RequiresClause)) + return Style.SpaceBeforeParensOptions.AfterRequiresClause; + else { + assert(Left.is(TT_RequiresExpression)); + return Style.SpaceBeforeParensOptions.AfterRequiresExpression; + } + } if ((Left.is(tok::r_paren) && Left.is(TT_AttributeParen)) || (Left.is(tok::r_square) && Left.is(TT_AttributeSquare))) return 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 @@ -2477,6 +2477,13 @@ void UnwrappedLineParser::parseConstraintExpression( unsigned int OriginalLevel) { // requires Id && Id || Id + + if (FormatTok->Tok.is(tok::kw_requires)) { + assert(FormatTok->Previous); + assert(FormatTok->Previous->is(TT_RequiresClause)); + FormatTok->setType(TT_RequiresExpression); + } + while ( FormatTok->isOneOf(tok::identifier, tok::kw_requires, tok::coloncolon)) { nextToken(); 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 @@ -14300,6 +14300,36 @@ verifyFormat("X A::operator++ (T);", SomeSpace2); verifyFormat("int x = int (y);", SomeSpace2); verifyFormat("auto lambda = []() { return 0; };", SomeSpace2); + + auto SpaceAfterRequires = getLLVMStyle(); + SpaceAfterRequires.SpaceBeforeParens = FormatStyle::SBPO_Custom; + SpaceAfterRequires.SpaceBeforeParensOptions.AfterRequiresClause = true; + SpaceAfterRequires.SpaceBeforeParensOptions.AfterRequiresExpression = true; + verifyFormat( + "template void func(T) requires(trait && trait) {\n" + " typename T::size_type;\n" + " { t.size() } -> std::same_as;\n" + "}\n" + "{}"); + verifyFormat("template void func(T) requires requires(T &&t) {\n" + " typename T::size_type;\n" + " { t.size() } -> std::same_as;\n" + "}\n" + "{}"); + verifyFormat( + "template void func(T) requires (trait && trait) {\n" + " typename T::size_type;\n" + " { t.size() } -> std::same_as;\n" + "}\n" + "{}", + SpaceAfterRequires); + verifyFormat( + "template void func(T) requires requires (T &&t) {\n" + " typename T::size_type;\n" + " { t.size() } -> std::same_as;\n" + "}\n" + "{}", + SpaceAfterRequires); } TEST_F(FormatTest, SpaceAfterLogicalNot) { @@ -22565,6 +22595,21 @@ "}\n" "{}", Style); + + Style.SpaceBeforeParens = FormatStyle::SBPO_Custom; + Style.SpaceBeforeParensOptions.AfterRequiresClause = true; + Style.SpaceBeforeParensOptions.AfterRequiresExpression = true; + verifyFormat("template \n" + " requires (std::is_integral_v && std::is_signed_v)\n" + "void func(T) {}", + 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) {