[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