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
