[clang] 9c422ab - [clang-format] Add option for aligning requires clause body
Emilia Dreamer via cfe-commits
cfe-commits at lists.llvm.org
Fri Oct 21 00:53:31 PDT 2022
Author: Danil Sidoruk
Date: 2022-10-21T10:42:45+03:00
New Revision: 9c422ab7ce82ec13155629f1e5e4e11d608fe1de
URL: https://github.com/llvm/llvm-project/commit/9c422ab7ce82ec13155629f1e5e4e11d608fe1de
DIFF: https://github.com/llvm/llvm-project/commit/9c422ab7ce82ec13155629f1e5e4e11d608fe1de.diff
LOG: [clang-format] Add option for aligning requires clause body
Adds an option whether requires clause body should be aligned with
the `requires` keyword.
This option is now the default, both without configuration and in LLVM
style.
Fixes https://github.com/llvm/llvm-project/issues/56283
Differential Revision: https://reviews.llvm.org/D129443
Co-authored-by: Emilia Dreamer <emilia at rymiel.space>
Added:
Modified:
clang/docs/ClangFormatStyleOptions.rst
clang/docs/ReleaseNotes.rst
clang/include/clang/Format/Format.h
clang/lib/Format/ContinuationIndenter.cpp
clang/lib/Format/Format.cpp
clang/unittests/Format/FormatTest.cpp
Removed:
################################################################################
diff --git a/clang/docs/ClangFormatStyleOptions.rst b/clang/docs/ClangFormatStyleOptions.rst
index cdbc23ae43eb8..b52cf49ef5636 100644
--- a/clang/docs/ClangFormatStyleOptions.rst
+++ b/clang/docs/ClangFormatStyleOptions.rst
@@ -3861,6 +3861,35 @@ the configuration (without a prefix: ``Auto``).
+**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 <typename T>
+ concept C = requires(T t) {
+ ...
+ }
+
+ * ``REI_Keyword`` (in configuration: ``Keyword``)
+ Align requires expression body relative to the `requires` keyword.
+
+ .. code-block:: c++
+
+ template <typename T>
+ 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
index 8198d62bc8d5f..e87a4eea552df 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -629,7 +629,10 @@ AST Matchers
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
diff ers 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
index 7c1721064727c..3205b502ac195 100644
--- a/clang/include/clang/Format/Format.h
+++ b/clang/include/clang/Format/Format.h
@@ -3153,6 +3153,32 @@ struct FormatStyle {
/// \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 <typename T>
+ /// concept C = requires(T t) {
+ /// ...
+ /// }
+ /// \endcode
+ REI_OuterScope,
+ /// Align requires expression body relative to the `requires` keyword.
+ /// \code
+ /// template <typename T>
+ /// 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 @@ struct FormatStyle {
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
index 00a9ae1063d18..79530de34138f 100644
--- a/clang/lib/Format/ContinuationIndenter.cpp
+++ b/clang/lib/Format/ContinuationIndenter.cpp
@@ -1406,8 +1406,10 @@ unsigned ContinuationIndenter::moveStateToNextToken(LineState &State,
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
index 37f171aa1f35b..1bab870ee52ab 100644
--- a/clang/lib/Format/Format.cpp
+++ b/clang/lib/Format/Format.cpp
@@ -99,6 +99,15 @@ struct ScalarEnumerationTraits<FormatStyle::LambdaBodyIndentationKind> {
}
};
+template <>
+struct ScalarEnumerationTraits<FormatStyle::RequiresExpressionIndentationKind> {
+ 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<FormatStyle::UseTabStyle> {
static void enumeration(IO &IO, FormatStyle::UseTabStyle &Value) {
IO.enumCase(Value, "Never", FormatStyle::UT_Never);
@@ -854,6 +863,8 @@ template <> struct MappingTraits<FormatStyle> {
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 @@ FormatStyle getLLVMStyle(FormatStyle::LanguageKind Language) {
LLVMStyle.PointerAlignment = FormatStyle::PAS_Right;
LLVMStyle.ReferenceAlignment = FormatStyle::RAS_Pointer;
LLVMStyle.RequiresClausePosition = FormatStyle::RCPS_OwnLine;
+ LLVMStyle.RequiresExpressionIndentation = FormatStyle::REI_OuterScope;
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
index 79e0847e02629..d7aca687ab40d 100644
--- a/clang/unittests/Format/FormatTest.cpp
+++ b/clang/unittests/Format/FormatTest.cpp
@@ -24334,6 +24334,12 @@ TEST_F(FormatTest, WebKitDefaultStyle) {
TEST_F(FormatTest, Concepts) {
EXPECT_EQ(getLLVMStyle().BreakBeforeConceptDeclarations,
FormatStyle::BBCDS_Always);
+
+ // The default in LLVM style is REI_OuterScope, but these tests were written
+ // when the default was REI_Keyword.
+ FormatStyle Style = getLLVMStyle();
+ Style.RequiresExpressionIndentation = FormatStyle::REI_Keyword;
+
verifyFormat("template <typename T>\n"
"concept True = true;");
@@ -24350,13 +24356,15 @@ TEST_F(FormatTest, Concepts) {
"concept DelayedCheck = true && requires(T t) {\n"
" t.bar();\n"
" t.baz();\n"
- " } && sizeof(T) <= 8;");
+ " } && sizeof(T) <= 8;",
+ Style);
verifyFormat("template <typename T>\n"
"concept DelayedCheck = true && requires(T t) { // Comment\n"
" t.bar();\n"
" t.baz();\n"
- " } && sizeof(T) <= 8;");
+ " } && sizeof(T) <= 8;",
+ Style);
verifyFormat("template <typename T>\n"
"concept DelayedCheck = false || requires(T t) { t.bar(); } && "
@@ -24458,26 +24466,30 @@ TEST_F(FormatTest, Concepts) {
"concept Hashable = requires(T a) {\n"
" { std::hash<T>{}(a) } -> "
"std::convertible_to<std::size_t>;\n"
- " };");
+ " };",
+ Style);
verifyFormat(
"template <typename T>\n"
"concept EqualityComparable = requires(T a, T b) {\n"
" { a == b } -> std::same_as<bool>;\n"
- " };");
+ " };",
+ Style);
verifyFormat(
"template <typename T>\n"
"concept EqualityComparable = requires(T a, T b) {\n"
" { a == b } -> std::same_as<bool>;\n"
" { a != b } -> std::same_as<bool>;\n"
- " };");
+ " };",
+ Style);
verifyFormat("template <typename T>\n"
"concept WeakEqualityComparable = requires(T a, T b) {\n"
" { a == b };\n"
" { a != b };\n"
- " };");
+ " };",
+ Style);
verifyFormat("template <typename T>\n"
"concept HasSizeT = requires { typename T::size_t; };");
@@ -24493,7 +24505,8 @@ TEST_F(FormatTest, Concepts) {
" requires Same<T *, decltype(new T[n])>;\n"
" { delete new T; };\n"
" { delete new T[n]; };\n"
- " };");
+ " };",
+ Style);
verifyFormat("template <typename T>\n"
"concept Semiregular =\n"
@@ -24506,7 +24519,8 @@ TEST_F(FormatTest, Concepts) {
" { delete new T[n]; };\n"
" { new T } -> std::same_as<T *>;\n"
" } && DefaultConstructible<T> && CopyConstructible<T> && "
- "CopyAssignable<T>;");
+ "CopyAssignable<T>;",
+ Style);
verifyFormat(
"template <typename T>\n"
@@ -24520,14 +24534,16 @@ TEST_F(FormatTest, Concepts) {
" { delete new T; };\n"
" { delete new T[n]; };\n"
" } && CopyConstructible<T> && "
- "CopyAssignable<T>;");
+ "CopyAssignable<T>;",
+ Style);
verifyFormat("template <typename T>\n"
"concept Two = requires(T t) {\n"
" { t.foo() } -> std::same_as<Bar>;\n"
" } && requires(T &&t) {\n"
" { t.foo() } -> std::same_as<Bar &&>;\n"
- " };");
+ " };",
+ Style);
verifyFormat(
"template <typename T>\n"
@@ -24535,7 +24551,8 @@ TEST_F(FormatTest, Concepts) {
" { *x } -> std::convertible_to<typename T::inner>;\n"
" { x + 1 } noexcept -> std::same_as<int>;\n"
" { x * 1 } -> std::convertible_to<T>;\n"
- " };");
+ " };",
+ Style);
verifyFormat("template <typename T>\n"
"concept C = requires(T x) {\n"
@@ -24545,33 +24562,38 @@ TEST_F(FormatTest, Concepts) {
" {\n"
" long_long_long_function_call(1, 2, 3, 4, 5)\n"
" } noexcept -> long_long_concept_name<T>;\n"
- " };");
+ " };",
+ Style);
verifyFormat(
"template <typename T, typename U = T>\n"
"concept Swappable = requires(T &&t, U &&u) {\n"
" swap(std::forward<T>(t), std::forward<U>(u));\n"
" swap(std::forward<U>(u), std::forward<T>(t));\n"
- " };");
+ " };",
+ Style);
verifyFormat("template <typename T, typename U>\n"
"concept Common = requires(T &&t, U &&u) {\n"
" typename CommonType<T, U>;\n"
" { CommonType<T, U>(std::forward<T>(t)) };\n"
- " };");
+ " };",
+ Style);
verifyFormat("template <typename T, typename U>\n"
"concept Common = requires(T &&t, U &&u) {\n"
" typename CommonType<T, U>;\n"
" { CommonType<T, U>{std::forward<T>(t)} };\n"
- " };");
+ " };",
+ Style);
verifyFormat(
"template <typename T>\n"
"concept C = requires(T t) {\n"
" requires Bar<T> && Foo<T>;\n"
" requires((trait<T> && Baz) || (T2<T> && Foo<T>));\n"
- " };");
+ " };",
+ Style);
verifyFormat("template <typename T>\n"
"concept HasFoo = requires(T t) {\n"
@@ -24582,7 +24604,8 @@ TEST_F(FormatTest, Concepts) {
"concept HasBar = requires(T t) {\n"
" { t.bar() };\n"
" t.bar();\n"
- " };");
+ " };",
+ Style);
verifyFormat("template <typename T>\n"
"concept Large = sizeof(T) > 10;");
@@ -24593,7 +24616,8 @@ TEST_F(FormatTest, Concepts) {
" { t.foo(u) } -> typename T::foo_type;\n"
" t++;\n"
" };\n"
- "void doFoo(FooableWith<int> auto t) { t.foo(3); }");
+ "void doFoo(FooableWith<int> auto t) { t.foo(3); }",
+ Style);
verifyFormat("template <typename T>\n"
"concept Context = is_specialization_of_v<context, T>;");
@@ -24619,7 +24643,6 @@ TEST_F(FormatTest, Concepts) {
" std::forward_iterator<_OutIt> &&\n"
" std::same_as<std::iter_value_t<_InIt>, std::iter_value_t<_OutIt>>;");
- auto Style = getLLVMStyle();
Style.BreakBeforeConceptDeclarations = FormatStyle::BBCDS_Allowed;
verifyFormat(
@@ -24730,6 +24753,10 @@ TEST_F(FormatTest, RequiresClausesPositions) {
EXPECT_EQ(Style.RequiresClausePosition, FormatStyle::RCPS_OwnLine);
EXPECT_EQ(Style.IndentRequiresClause, true);
+ // The default in LLVM style is REI_OuterScope, but these tests were written
+ // when the default was REI_Keyword.
+ Style.RequiresExpressionIndentation = FormatStyle::REI_Keyword;
+
verifyFormat("template <typename T>\n"
" requires(Foo<T> && std::trait<T>)\n"
"struct Bar;",
@@ -25021,6 +25048,123 @@ TEST_F(FormatTest, RequiresClauses) {
"bar(requires);");
}
+TEST_F(FormatTest, RequiresExpressionIndentation) {
+ auto Style = getLLVMStyle();
+ EXPECT_EQ(Style.RequiresExpressionIndentation, FormatStyle::REI_OuterScope);
+
+ verifyFormat("template <typename T>\n"
+ "concept C = requires(T t) {\n"
+ " typename T::value;\n"
+ " requires requires(typename T::value v) {\n"
+ " { t == v } -> std::same_as<bool>;\n"
+ " };\n"
+ "};",
+ Style);
+
+ verifyFormat("template <typename T>\n"
+ "void bar(T)\n"
+ " requires Foo<T> && requires(T t) {\n"
+ " { t.foo() } -> std::same_as<int>;\n"
+ " } && requires(T t) {\n"
+ " { t.bar() } -> std::same_as<bool>;\n"
+ " --t;\n"
+ " };",
+ Style);
+
+ verifyFormat("template <typename T>\n"
+ " requires Foo<T> &&\n"
+ " requires(T t) {\n"
+ " { t.foo() } -> std::same_as<int>;\n"
+ " } && requires(T t) {\n"
+ " { t.bar() } -> std::same_as<bool>;\n"
+ " --t;\n"
+ " }\n"
+ "void bar(T);",
+ Style);
+
+ verifyFormat("template <typename T> void f() {\n"
+ " if constexpr (requires(T t) {\n"
+ " { t.bar() } -> std::same_as<bool>;\n"
+ " }) {\n"
+ " }\n"
+ "}",
+ Style);
+
+ verifyFormat("template <typename T> void f() {\n"
+ " if constexpr (condition && requires(T t) {\n"
+ " { t.bar() } -> std::same_as<bool>;\n"
+ " }) {\n"
+ " }\n"
+ "}",
+ Style);
+
+ verifyFormat("template <typename T> struct C {\n"
+ " void f()\n"
+ " requires requires(T t) {\n"
+ " { t.bar() } -> std::same_as<bool>;\n"
+ " };\n"
+ "};",
+ Style);
+
+ Style.RequiresExpressionIndentation = FormatStyle::REI_Keyword;
+
+ verifyFormat("template <typename T>\n"
+ "concept C = requires(T t) {\n"
+ " typename T::value;\n"
+ " requires requires(typename T::value v) {\n"
+ " { t == v } -> std::same_as<bool>;\n"
+ " };\n"
+ " };",
+ Style);
+
+ verifyFormat(
+ "template <typename T>\n"
+ "void bar(T)\n"
+ " requires Foo<T> && requires(T t) {\n"
+ " { t.foo() } -> std::same_as<int>;\n"
+ " } && requires(T t) {\n"
+ " { t.bar() } -> std::same_as<bool>;\n"
+ " --t;\n"
+ " };",
+ Style);
+
+ verifyFormat("template <typename T>\n"
+ " requires Foo<T> &&\n"
+ " requires(T t) {\n"
+ " { t.foo() } -> std::same_as<int>;\n"
+ " } && requires(T t) {\n"
+ " { t.bar() } -> std::same_as<bool>;\n"
+ " --t;\n"
+ " }\n"
+ "void bar(T);",
+ Style);
+
+ verifyFormat("template <typename T> void f() {\n"
+ " if constexpr (requires(T t) {\n"
+ " { t.bar() } -> std::same_as<bool>;\n"
+ " }) {\n"
+ " }\n"
+ "}",
+ Style);
+
+ verifyFormat(
+ "template <typename T> void f() {\n"
+ " if constexpr (condition && requires(T t) {\n"
+ " { t.bar() } -> std::same_as<bool>;\n"
+ " }) {\n"
+ " }\n"
+ "}",
+ Style);
+
+ verifyFormat("template <typename T> struct C {\n"
+ " void f()\n"
+ " requires requires(T t) {\n"
+ " { t.bar() } -> std::same_as<bool>;\n"
+ " };\n"
+ "};",
+ Style);
+}
+
TEST_F(FormatTest, StatementAttributeLikeMacros) {
FormatStyle Style = getLLVMStyle();
StringRef Source = "void Foo::slot() {\n"
More information about the cfe-commits
mailing list