[clang] [clang-format] Add BraceWrapping.AfterRequiresExpression option (PR #216465)

via cfe-commits cfe-commits at lists.llvm.org
Sat Aug 15 00:48:01 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clang-format

Author: Aditya Goyal (goyaladitya05)

<details>
<summary>Changes</summary>

Thir PR adds a `BraceWrapping.AfterRequiresExpression` sub-option that wraps the opening brace of requires expressions, and enable it in the `Allman`, `Whitesmiths`, and `GNU` presets of `BreakBeforeBraces`.

The brace is only wrapped if the requires expression doesn't fit on a single line, matching the behavior of `BeforeLambdaBody`. The wrapped brace is aligned with its closing brace:

```cpp
template <typename T>
concept Uart = requires(T a)
{
    { a.write() } -> std::convertible_to<std::size_t>;
    a.flush();
};
```
Closes: #<!-- -->202901

---
Full diff: https://github.com/llvm/llvm-project/pull/216465.diff


8 Files Affected:

- (modified) clang/docs/ClangFormatStyleOptions.md (+17) 
- (modified) clang/docs/ReleaseNotes.md (+3) 
- (modified) clang/include/clang/Format/Format.h (+16) 
- (modified) clang/lib/Format/ContinuationIndenter.cpp (+15) 
- (modified) clang/lib/Format/Format.cpp (+6) 
- (modified) clang/lib/Format/TokenAnnotator.cpp (+5) 
- (modified) clang/unittests/Format/ConfigParseTest.cpp (+1) 
- (modified) clang/unittests/Format/FormatTest.cpp (+92) 


``````````diff
diff --git a/clang/docs/ClangFormatStyleOptions.md b/clang/docs/ClangFormatStyleOptions.md
index 9b962e6e1e083..f51bf691ec6a4 100644
--- a/clang/docs/ClangFormatStyleOptions.md
+++ b/clang/docs/ClangFormatStyleOptions.md
@@ -2552,6 +2552,23 @@ the configuration (without a prefix: `Auto`).
     according to `AfterControlStatement` flag.
     :::
 
+  - `bool AfterRequiresExpression` Wrap requires expression body.
+
+    ```c++
+    true:
+    template <typename T>
+    concept C = requires(T t)
+    {
+      foo(t);
+    };
+
+    false:
+    template <typename T>
+    concept C = requires(T t) {
+      foo(t);
+    };
+    ```
+
   - `bool AfterStruct` Wrap struct definitions.
 
     ```c++
diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index a7a31946610ae..768abc317f9a7 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -583,6 +583,9 @@ features cannot lower the translation-unit ABI level;
 
 - Add `SpacesInBlockComments` option to control spacing after `/*` and
   before `*/` in ordinary block comments.
+- Add `AfterRequiresExpression` sub-option of `BraceWrapping` to wrap the
+  body of requires expressions. It is enabled by the `Allman`, `Whitesmiths`,
+  and `GNU` styles of `BreakBeforeBraces`.
 
 ### libclang
 
diff --git a/clang/include/clang/Format/Format.h b/clang/include/clang/Format/Format.h
index 3948337d2fc3d..7241864fff50e 100644
--- a/clang/include/clang/Format/Format.h
+++ b/clang/include/clang/Format/Format.h
@@ -1507,6 +1507,22 @@ struct FormatStyle {
     ///  according to `AfterControlStatement` flag.
     /// \endnote
     bool AfterObjCDeclaration;
+    /// Wrap requires expression body.
+    /// \code
+    ///   true:
+    ///   template <typename T>
+    ///   concept C = requires(T t)
+    ///   {
+    ///     foo(t);
+    ///   };
+    ///
+    ///   false:
+    ///   template <typename T>
+    ///   concept C = requires(T t) {
+    ///     foo(t);
+    ///   };
+    /// \endcode
+    bool AfterRequiresExpression;
     /// Wrap struct definitions.
     /// \code
     ///   true:
diff --git a/clang/lib/Format/ContinuationIndenter.cpp b/clang/lib/Format/ContinuationIndenter.cpp
index 085b48fd1ed78..aa264c1487bce 100644
--- a/clang/lib/Format/ContinuationIndenter.cpp
+++ b/clang/lib/Format/ContinuationIndenter.cpp
@@ -449,6 +449,9 @@ bool ContinuationIndenter::canBreak(const LineState &State) {
       // enabled.
       (!Style.BraceWrapping.BeforeLambdaBody ||
        Current.isNot(TT_LambdaLBrace)) &&
+      // Same for the opening brace of requires expressions.
+      (!Style.BraceWrapping.AfterRequiresExpression ||
+       Current.isNot(TT_RequiresExpressionLBrace)) &&
       CurrentState.NoLineBreakInOperand) {
     return false;
   }
@@ -477,6 +480,11 @@ bool ContinuationIndenter::mustBreak(const LineState &State) {
     auto LambdaBodyLength = getLengthToMatchingParen(Current, State.Stack);
     return LambdaBodyLength > getColumnLimit(State);
   }
+  if (Style.BraceWrapping.AfterRequiresExpression && Current.CanBreakBefore &&
+      Current.is(TT_RequiresExpressionLBrace) &&
+      getLengthToMatchingParen(Current, State.Stack) > getColumnLimit(State)) {
+    return true;
+  }
   if (Current.MustBreakBefore ||
       (Current.is(TT_InlineASMColon) &&
        (Style.BreakBeforeInlineASMColon == FormatStyle::BBIAS_Always ||
@@ -1508,6 +1516,13 @@ ContinuationIndenter::getNewLineColumn(const LineState &State) {
     return From + Style.IndentWidth;
   }
 
+  // Align the wrapped opening brace of a requires expression with its
+  // closing brace.
+  if (Style.BraceWrapping.AfterRequiresExpression &&
+      Current.is(TT_RequiresExpressionLBrace)) {
+    return CurrentState.NestedBlockIndent;
+  }
+
   if ((NextNonComment->is(tok::l_brace) && NextNonComment->is(BK_Block)) ||
       (Style.isVerilog() && Keywords.isVerilogBegin(*NextNonComment))) {
     if (Current.NestingLevel == 0 ||
diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp
index 2b6e65efbf026..1e4c2c6080912 100644
--- a/clang/lib/Format/Format.cpp
+++ b/clang/lib/Format/Format.cpp
@@ -223,6 +223,7 @@ template <> struct MappingTraits<FormatStyle::BraceWrappingFlags> {
     IO.mapOptional("AfterFunction", Wrapping.AfterFunction);
     IO.mapOptional("AfterNamespace", Wrapping.AfterNamespace);
     IO.mapOptional("AfterObjCDeclaration", Wrapping.AfterObjCDeclaration);
+    IO.mapOptional("AfterRequiresExpression", Wrapping.AfterRequiresExpression);
     IO.mapOptional("AfterStruct", Wrapping.AfterStruct);
     IO.mapOptional("AfterUnion", Wrapping.AfterUnion);
     IO.mapOptional("BeforeCatch", Wrapping.BeforeCatch);
@@ -1722,6 +1723,7 @@ static void expandPresetsBraceWrapping(FormatStyle &Expanded) {
                             /*AfterFunction=*/false,
                             /*AfterNamespace=*/false,
                             /*AfterObjCDeclaration=*/false,
+                            /*AfterRequiresExpression=*/false,
                             /*AfterStruct=*/false,
                             /*AfterUnion=*/false,
                             /*AfterExternBlock=*/false,
@@ -1762,6 +1764,7 @@ static void expandPresetsBraceWrapping(FormatStyle &Expanded) {
     Expanded.BraceWrapping.AfterFunction = true;
     Expanded.BraceWrapping.AfterNamespace = true;
     Expanded.BraceWrapping.AfterObjCDeclaration = true;
+    Expanded.BraceWrapping.AfterRequiresExpression = true;
     Expanded.BraceWrapping.AfterStruct = true;
     Expanded.BraceWrapping.AfterUnion = true;
     Expanded.BraceWrapping.AfterExternBlock = true;
@@ -1777,6 +1780,7 @@ static void expandPresetsBraceWrapping(FormatStyle &Expanded) {
     Expanded.BraceWrapping.AfterFunction = true;
     Expanded.BraceWrapping.AfterNamespace = true;
     Expanded.BraceWrapping.AfterObjCDeclaration = true;
+    Expanded.BraceWrapping.AfterRequiresExpression = true;
     Expanded.BraceWrapping.AfterStruct = true;
     Expanded.BraceWrapping.AfterExternBlock = true;
     Expanded.BraceWrapping.BeforeCatch = true;
@@ -1792,6 +1796,7 @@ static void expandPresetsBraceWrapping(FormatStyle &Expanded) {
         /*AfterFunction=*/true,
         /*AfterNamespace=*/true,
         /*AfterObjCDeclaration=*/true,
+        /*AfterRequiresExpression=*/true,
         /*AfterStruct=*/true,
         /*AfterUnion=*/true,
         /*AfterExternBlock=*/true,
@@ -1894,6 +1899,7 @@ FormatStyle getLLVMStyle(FormatStyle::LanguageKind Language) {
                              /*AfterFunction=*/false,
                              /*AfterNamespace=*/false,
                              /*AfterObjCDeclaration=*/false,
+                             /*AfterRequiresExpression=*/false,
                              /*AfterStruct=*/false,
                              /*AfterUnion=*/false,
                              /*AfterExternBlock=*/false,
diff --git a/clang/lib/Format/TokenAnnotator.cpp b/clang/lib/Format/TokenAnnotator.cpp
index b6c33279b0aca..105c98e704c3b 100644
--- a/clang/lib/Format/TokenAnnotator.cpp
+++ b/clang/lib/Format/TokenAnnotator.cpp
@@ -6724,6 +6724,11 @@ bool TokenAnnotator::canBreakBefore(const AnnotatedLine &Line,
     return false;
   }
 
+  if (Style.BraceWrapping.AfterRequiresExpression &&
+      Right.is(TT_RequiresExpressionLBrace)) {
+    return true;
+  }
+
   auto ShortLambdaOption = Style.AllowShortLambdasOnASingleLine;
   if (Style.BraceWrapping.BeforeLambdaBody && Right.is(TT_LambdaLBrace)) {
     if (isAllmanLambdaBrace(Left))
diff --git a/clang/unittests/Format/ConfigParseTest.cpp b/clang/unittests/Format/ConfigParseTest.cpp
index 9350ba7eb3de4..abf8c0b55a67b 100644
--- a/clang/unittests/Format/ConfigParseTest.cpp
+++ b/clang/unittests/Format/ConfigParseTest.cpp
@@ -241,6 +241,7 @@ TEST(ConfigParseTest, ParsesConfigurationBools) {
   CHECK_PARSE_NESTED_BOOL(BraceWrapping, AfterFunction);
   CHECK_PARSE_NESTED_BOOL(BraceWrapping, AfterNamespace);
   CHECK_PARSE_NESTED_BOOL(BraceWrapping, AfterObjCDeclaration);
+  CHECK_PARSE_NESTED_BOOL(BraceWrapping, AfterRequiresExpression);
   CHECK_PARSE_NESTED_BOOL(BraceWrapping, AfterStruct);
   CHECK_PARSE_NESTED_BOOL(BraceWrapping, AfterUnion);
   CHECK_PARSE_NESTED_BOOL(BraceWrapping, AfterExternBlock);
diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp
index b72a683ac1fff..18e2aa551918c 100644
--- a/clang/unittests/Format/FormatTest.cpp
+++ b/clang/unittests/Format/FormatTest.cpp
@@ -24676,6 +24676,98 @@ TEST_F(FormatTest, RequiresExpressionIndentation) {
                Style);
 }
 
+TEST_F(FormatTest, RequiresExpressionBraceWrapping) {
+  auto Style = getLLVMStyle();
+  EXPECT_FALSE(Style.BraceWrapping.AfterRequiresExpression);
+
+  Style.BreakBeforeBraces = FormatStyle::BS_Custom;
+  Style.BraceWrapping.AfterRequiresExpression = true;
+
+  // Requires expressions that fit on a single line are not wrapped.
+  verifyFormat("template <typename T>\n"
+               "concept C = requires(T t) { t.foo(); };",
+               Style);
+  verifyFormat("static_assert(requires(int i) { i + 1; });", Style);
+
+  verifyFormat("template <typename T>\n"
+               "concept C = requires(T t)\n"
+               "{\n"
+               "  t.foo();\n"
+               "  t.bar();\n"
+               "};",
+               Style);
+
+  verifyFormat("template <typename T>\n"
+               "concept C = requires\n"
+               "{\n"
+               "  typename T::value_type;\n"
+               "  typename T::size_type;\n"
+               "};",
+               Style);
+
+  verifyFormat("template <typename T>\n"
+               "concept C = requires(T t)\n"
+               "{\n"
+               "  { t.foo() } -> std::same_as<int>;\n"
+               "};",
+               Style);
+
+  verifyFormat("template <typename T>\n"
+               "void bar(T)\n"
+               "  requires requires(T t)\n"
+               "  {\n"
+               "    t.foo();\n"
+               "    t.bar();\n"
+               "  };",
+               Style);
+
+  verifyFormat("template <typename T> void f() {\n"
+               "  if constexpr (requires(T t)\n"
+               "                {\n"
+               "                  { t.bar() } -> std::same_as<bool>;\n"
+               "                }) {\n"
+               "  }\n"
+               "}",
+               Style);
+
+  // The wrapped brace is aligned with the closing brace.
+  verifyFormat("template <typename T>\n"
+               "  requires Foo<T> &&\n"
+               "           requires(T t)\n"
+               "           {\n"
+               "             { t.foo() } -> std::same_as<int>;\n"
+               "           } &&\n"
+               "           requires(T t)\n"
+               "           {\n"
+               "             { t.bar() } -> std::same_as<bool>;\n"
+               "             --t;\n"
+               "           }\n"
+               "void bar(T);",
+               Style);
+
+  Style.RequiresExpressionIndentation = FormatStyle::REI_Keyword;
+  verifyFormat("template <typename T>\n"
+               "concept C = requires(T t)\n"
+               "            {\n"
+               "              typename T::value;\n"
+               "              requires requires(typename T::value v)\n"
+               "                       {\n"
+               "                         { t == v } -> std::same_as<bool>;\n"
+               "                       };\n"
+               "            };",
+               Style);
+  Style.RequiresExpressionIndentation = FormatStyle::REI_OuterScope;
+
+  Style.BreakBeforeBraces = FormatStyle::BS_Allman;
+  verifyFormat("template <typename T>\n"
+               "concept Uart = requires(T a)\n"
+               "{\n"
+               "  { a.write() } -> std::convertible_to<std::size_t>;\n"
+               "  a.flush();\n"
+               "};",
+               Style);
+}
+
 TEST_F(FormatTest, StatementAttributeLikeMacros) {
   FormatStyle Style = getLLVMStyle();
   StringRef Source = "void Foo::slot() {\n"

``````````

</details>


https://github.com/llvm/llvm-project/pull/216465


More information about the cfe-commits mailing list