Author: Aditya Goyal
Date: 2026-08-28T21:15:29+02:00
New Revision: 1633ec8140884d364302710814ee9df344aa51ea

URL: 
https://github.com/llvm/llvm-project/commit/1633ec8140884d364302710814ee9df344aa51ea
DIFF: 
https://github.com/llvm/llvm-project/commit/1633ec8140884d364302710814ee9df344aa51ea.diff

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

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

Added: 
    

Modified: 
    clang/docs/ClangFormatStyleOptions.md
    clang/docs/ReleaseNotes.md
    clang/include/clang/Format/Format.h
    clang/lib/Format/ContinuationIndenter.cpp
    clang/lib/Format/Format.cpp
    clang/lib/Format/TokenAnnotator.cpp
    clang/unittests/Format/ConfigParseTest.cpp
    clang/unittests/Format/FormatTest.cpp

Removed: 
    


################################################################################
diff  --git a/clang/docs/ClangFormatStyleOptions.md 
b/clang/docs/ClangFormatStyleOptions.md
index ee239f768cf2b..f575f61745cd6 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 a96b0af93ab0b..a3b0ef3740b7a 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -660,6 +660,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`.
 
 - `QualifierOrder` now supports `typedef`, `consteval`, `constinit`,
   `thread_local`, `extern`, `mutable`, `signed`, `unsigned`, `long`, `short`,

diff  --git a/clang/include/clang/Format/Format.h 
b/clang/include/clang/Format/Format.h
index 0f91df5b1b32d..6deb5bcbea80c 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 574014f360354..192375434d912 100644
--- a/clang/lib/Format/Format.cpp
+++ b/clang/lib/Format/Format.cpp
@@ -226,6 +226,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);
@@ -1725,6 +1726,7 @@ static void expandPresetsBraceWrapping(FormatStyle 
&Expanded) {
                             /*AfterFunction=*/false,
                             /*AfterNamespace=*/false,
                             /*AfterObjCDeclaration=*/false,
+                            /*AfterRequiresExpression=*/false,
                             /*AfterStruct=*/false,
                             /*AfterUnion=*/false,
                             /*AfterExportBlock=*/false,
@@ -1767,6 +1769,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.AfterExportBlock = true;
@@ -1783,6 +1786,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;
@@ -1798,6 +1802,7 @@ static void expandPresetsBraceWrapping(FormatStyle 
&Expanded) {
         /*AfterFunction=*/true,
         /*AfterNamespace=*/true,
         /*AfterObjCDeclaration=*/true,
+        /*AfterRequiresExpression=*/true,
         /*AfterStruct=*/true,
         /*AfterUnion=*/true,
         /*AfterExportBlock=*/true,
@@ -1901,6 +1906,7 @@ FormatStyle getLLVMStyle(FormatStyle::LanguageKind 
Language) {
                              /*AfterFunction=*/false,
                              /*AfterNamespace=*/false,
                              /*AfterObjCDeclaration=*/false,
+                             /*AfterRequiresExpression=*/false,
                              /*AfterStruct=*/false,
                              /*AfterUnion=*/false,
                              /*AfterExportBlock=*/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 53fbec9806ca4..6d12dfd715ae3 100644
--- a/clang/unittests/Format/ConfigParseTest.cpp
+++ b/clang/unittests/Format/ConfigParseTest.cpp
@@ -242,6 +242,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 ca9312ae40f1d..5aed37aa56d52 100644
--- a/clang/unittests/Format/FormatTest.cpp
+++ b/clang/unittests/Format/FormatTest.cpp
@@ -24705,6 +24705,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"


        
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to