diff --git a/clang/docs/ClangFormatStyleOptions.rst b/clang/docs/ClangFormatStyleOptions.rst --- a/clang/docs/ClangFormatStyleOptions.rst +++ b/clang/docs/ClangFormatStyleOptions.rst @@ -3861,6 +3861,35 @@ +**RequiresExpressionIndentation** (``RequiresExpressionIndentationKind``) :versionbadge:`clang-format 16` + The indentation used for requires expression bodies. + + Possible values: + + * ``REI_OuterScope`` (in configuration: ``OuterScope``) + Align requires expression body relative to the indentation level of the + outer scope the requires expression resides in. + This is the default. + + .. code-block:: c++ + + template + concept C = requires(T t) { + ... + } + + * ``REI_Keyword`` (in configuration: ``Keyword``) + Align requires expression body relative to the `requires` keyword. + + .. code-block:: c++ + + template + concept C = requires(T t) { + ... + } + + + **SeparateDefinitionBlocks** (``SeparateDefinitionStyle``) :versionbadge:`clang-format 14` Specifies the use of empty lines to separate definition blocks, including classes, structs, enums, and functions. diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -537,7 +537,10 @@ clang-format ------------ -- Add `RemoveSemicolon` option for removing `;` after a non-empty function definition. +- Add ``RemoveSemicolon`` option for removing ``;`` after a non-empty function definition. +- Add ``RequiresExpressionIndentation`` option for configuring the alignment of requires-expressions. + The default value of this option is ``OuterScope``, which differs in behavior from clang-format 15. + To match the default behavior of clang-format 15, use the ``Keyword`` value. clang-extdef-mapping -------------------- 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 @@ -3153,6 +3153,32 @@ /// \version 15 RequiresClausePositionStyle RequiresClausePosition; + /// Indentation logic for requires expression bodies. + enum RequiresExpressionIndentationKind : int8_t { + /// Align requires expression body relative to the indentation level of the + /// outer scope the requires expression resides in. + /// This is the default. + /// \code + /// template + /// concept C = requires(T t) { + /// ... + /// } + /// \endcode + REI_OuterScope, + /// Align requires expression body relative to the `requires` keyword. + /// \code + /// template + /// concept C = requires(T t) { + /// ... + /// } + /// \endcode + REI_Keyword, + }; + + /// The indentation used for requires expression bodies. + /// \version 16 + RequiresExpressionIndentationKind RequiresExpressionIndentation; + /// \brief The style if definition blocks should be separated. enum SeparateDefinitionStyle : int8_t { /// Leave definition blocks as they are. @@ -3988,6 +4014,7 @@ RemoveBracesLLVM == R.RemoveBracesLLVM && RemoveSemicolon == R.RemoveSemicolon && RequiresClausePosition == R.RequiresClausePosition && + RequiresExpressionIndentation == R.RequiresExpressionIndentation && SeparateDefinitionBlocks == R.SeparateDefinitionBlocks && ShortNamespaceLines == R.ShortNamespaceLines && SortIncludes == R.SortIncludes && diff --git a/clang/lib/Format/ContinuationIndenter.cpp b/clang/lib/Format/ContinuationIndenter.cpp --- a/clang/lib/Format/ContinuationIndenter.cpp +++ b/clang/lib/Format/ContinuationIndenter.cpp @@ -1403,8 +1403,10 @@ CurrentState.NestedBlockIndent = State.Column + Current.ColumnWidth + 1; if (Current.isOneOf(TT_LambdaLSquare, TT_LambdaArrow)) CurrentState.LastSpace = State.Column; - if (Current.is(TT_RequiresExpression)) + if (Current.is(TT_RequiresExpression) && + Style.RequiresExpressionIndentation == FormatStyle::REI_Keyword) { CurrentState.NestedBlockIndent = State.Column; + } // Insert scopes created by fake parenthesis. const FormatToken *Previous = Current.getPreviousNonComment(); 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 @@ -99,6 +99,15 @@ } }; +template <> +struct ScalarEnumerationTraits { + static void + enumeration(IO &IO, FormatStyle::RequiresExpressionIndentationKind &Value) { + IO.enumCase(Value, "Keyword", FormatStyle::REI_Keyword); + IO.enumCase(Value, "OuterScope", FormatStyle::REI_OuterScope); + } +}; + template <> struct ScalarEnumerationTraits { static void enumeration(IO &IO, FormatStyle::UseTabStyle &Value) { IO.enumCase(Value, "Never", FormatStyle::UT_Never); @@ -854,6 +863,8 @@ IO.mapOptional("RemoveBracesLLVM", Style.RemoveBracesLLVM); IO.mapOptional("RemoveSemicolon", Style.RemoveSemicolon); IO.mapOptional("RequiresClausePosition", Style.RequiresClausePosition); + IO.mapOptional("RequiresExpressionIndentation", + Style.RequiresExpressionIndentation); IO.mapOptional("SeparateDefinitionBlocks", Style.SeparateDefinitionBlocks); IO.mapOptional("ShortNamespaceLines", Style.ShortNamespaceLines); IO.mapOptional("SortIncludes", Style.SortIncludes); @@ -1290,6 +1301,7 @@ LLVMStyle.PointerAlignment = FormatStyle::PAS_Right; LLVMStyle.ReferenceAlignment = FormatStyle::RAS_Pointer; LLVMStyle.RequiresClausePosition = FormatStyle::RCPS_OwnLine; + LLVMStyle.RequiresExpressionIndentation = FormatStyle::REI_Keyword; LLVMStyle.SeparateDefinitionBlocks = FormatStyle::SDS_Leave; LLVMStyle.ShortNamespaceLines = 1; LLVMStyle.SpacesBeforeTrailingComments = 1; 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 @@ -24995,6 +24995,123 @@ "bar(requires);"); } +TEST_F(FormatTest, RequiresExpressionIndentation) { + auto Style = getLLVMStyle(); + EXPECT_EQ(Style.RequiresExpressionIndentation, FormatStyle::REI_Keyword); + + verifyFormat("template \n" + "concept C = requires(T t) {\n" + " typename T::value;\n" + " requires requires(typename T::value v) {\n" + " { t == v } -> std::same_as;\n" + " };\n" + " };", + Style); + + verifyFormat( + "template \n" + "void bar(T)\n" + " requires Foo && requires(T t) {\n" + " { t.foo() } -> std::same_as;\n" + " } && requires(T t) {\n" + " { t.bar() } -> std::same_as;\n" + " --t;\n" + " };", + Style); + + verifyFormat("template \n" + " requires Foo &&\n" + " requires(T t) {\n" + " { t.foo() } -> std::same_as;\n" + " } && requires(T t) {\n" + " { t.bar() } -> std::same_as;\n" + " --t;\n" + " }\n" + "void bar(T);", + Style); + + verifyFormat("template void f() {\n" + " if constexpr (requires(T t) {\n" + " { t.bar() } -> std::same_as;\n" + " }) {\n" + " }\n" + "}", + Style); + + verifyFormat( + "template void f() {\n" + " if constexpr (condition && requires(T t) {\n" + " { t.bar() } -> std::same_as;\n" + " }) {\n" + " }\n" + "}", + Style); + + verifyFormat("template struct C {\n" + " void f()\n" + " requires requires(T t) {\n" + " { t.bar() } -> std::same_as;\n" + " };\n" + "};", + Style); + + Style.RequiresExpressionIndentation = FormatStyle::REI_OuterScope; + + verifyFormat("template \n" + "concept C = requires(T t) {\n" + " typename T::value;\n" + " requires requires(typename T::value v) {\n" + " { t == v } -> std::same_as;\n" + " };\n" + "};", + Style); + + verifyFormat("template \n" + "void bar(T)\n" + " requires Foo && requires(T t) {\n" + " { t.foo() } -> std::same_as;\n" + " } && requires(T t) {\n" + " { t.bar() } -> std::same_as;\n" + " --t;\n" + " };", + Style); + + verifyFormat("template \n" + " requires Foo &&\n" + " requires(T t) {\n" + " { t.foo() } -> std::same_as;\n" + " } && requires(T t) {\n" + " { t.bar() } -> std::same_as;\n" + " --t;\n" + " }\n" + "void bar(T);", + Style); + + verifyFormat("template void f() {\n" + " if constexpr (requires(T t) {\n" + " { t.bar() } -> std::same_as;\n" + " }) {\n" + " }\n" + "}", + Style); + + verifyFormat("template void f() {\n" + " if constexpr (condition && requires(T t) {\n" + " { t.bar() } -> std::same_as;\n" + " }) {\n" + " }\n" + "}", + Style); + + verifyFormat("template struct C {\n" + " void f()\n" + " requires requires(T t) {\n" + " { t.bar() } -> std::same_as;\n" + " };\n" + "};", + Style); +} + TEST_F(FormatTest, StatementAttributeLikeMacros) { FormatStyle Style = getLLVMStyle(); StringRef Source = "void Foo::slot() {\n"