From 4cf9a4266167c686e7e405ead5d6c1cd389eeca5 Mon Sep 17 00:00:00 2001 From: Nathan Sidwell Date: Fri, 16 Aug 2024 21:05:08 -0400 Subject: [PATCH] [clang-format] Adjust requires clause wrapping (#101550) (#102078) Address #101550 by adding OwnLineWithBrace option for RequiresClausePosition. This permits placing a following '{' on the same line as the requires clause. Thus, instead of: ``` bool Foo () requires(true) { return true; } ``` we have: ``` bool Foo () requires(true) { return true; } ``` If the function body is empty, we'll get: ``` bool Foo () requires(true) {} ``` I attempted to get a line break between the open and close braces, but failed. Perhaps that's fine -- it's rare and only happens in the empty body case. --- clang/docs/ClangFormatStyleOptions.rst | 36 ++++++++-- clang/include/clang/Format/Format.h | 34 ++++++++-- clang/lib/Format/ContinuationIndenter.cpp | 1 + clang/lib/Format/Format.cpp | 1 + clang/lib/Format/TokenAnnotator.cpp | 7 +- clang/unittests/Format/FormatTest.cpp | 83 +++++++++++++++++++++++ 6 files changed, 150 insertions(+), 12 deletions(-) diff --git a/clang/docs/ClangFormatStyleOptions.rst b/clang/docs/ClangFormatStyleOptions.rst index 72e1bd19b6b520..c79a635d86a6ef 100644 --- a/clang/docs/ClangFormatStyleOptions.rst +++ b/clang/docs/ClangFormatStyleOptions.rst @@ -4187,7 +4187,8 @@ the configuration (without a prefix: ``Auto``). **IndentRequiresClause** (``Boolean``) :versionbadge:`clang-format 15` :ref:`ΒΆ ` Indent the requires clause in a template. This only applies when - ``RequiresClausePosition`` is ``OwnLine``, or ``WithFollowing``. + ``RequiresClausePosition`` is ``OwnLine``, ``OwnLineWithBrace``, + or ``WithFollowing``. In clang-format 12, 13 and 14 it was named ``IndentRequires``. @@ -5419,23 +5420,48 @@ the configuration (without a prefix: ``Auto``). Possible values: * ``RCPS_OwnLine`` (in configuration: ``OwnLine``) - Always put the ``requires`` clause on its own line. + Always put the ``requires`` clause on its own line (possibly followed by + a semicolon). .. code-block:: c++ template - requires C + requires C struct Foo {... template - requires C + void bar(T t) + requires C; + + template + requires C void bar(T t) {... template void baz(T t) - requires C + requires C {... + * ``RCPS_OwnLineWithBrace`` (in configuration: ``OwnLineWithBrace``) + As with ``OwnLine``, except, unless otherwise prohibited, place a + following open brace (of a function definition) to follow on the same + line. + + .. code-block:: c++ + + void bar(T t) + requires C { + return; + } + + void bar(T t) + requires C {} + + template + requires C + void baz(T t) { + ... + * ``RCPS_WithPreceding`` (in configuration: ``WithPreceding``) Try to put the clause together with the preceding part of a declaration. For class templates: stick to the template declaration. diff --git a/clang/include/clang/Format/Format.h b/clang/include/clang/Format/Format.h index ef6c76a070bfaa..2af1d4065c3cc1 100644 --- a/clang/include/clang/Format/Format.h +++ b/clang/include/clang/Format/Format.h @@ -2858,7 +2858,8 @@ struct FormatStyle { PPDirectiveIndentStyle IndentPPDirectives; /// Indent the requires clause in a template. This only applies when - /// ``RequiresClausePosition`` is ``OwnLine``, or ``WithFollowing``. + /// ``RequiresClausePosition`` is ``OwnLine``, ``OwnLineWithBrace``, + /// or ``WithFollowing``. /// /// In clang-format 12, 13 and 14 it was named ``IndentRequires``. /// \code @@ -3944,22 +3945,45 @@ struct FormatStyle { /// ``IndentRequires`` option is only used if the ``requires`` is put on the /// start of a line. enum RequiresClausePositionStyle : int8_t { - /// Always put the ``requires`` clause on its own line. + /// Always put the ``requires`` clause on its own line (possibly followed by + /// a semicolon). /// \code /// template - /// requires C + /// requires C /// struct Foo {... /// /// template - /// requires C + /// void bar(T t) + /// requires C; + /// + /// template + /// requires C /// void bar(T t) {... /// /// template /// void baz(T t) - /// requires C + /// requires C /// {... /// \endcode RCPS_OwnLine, + /// As with ``OwnLine``, except, unless otherwise prohibited, place a + /// following open brace (of a function definition) to follow on the same + /// line. + /// \code + /// void bar(T t) + /// requires C { + /// return; + /// } + /// + /// void bar(T t) + /// requires C {} + /// + /// template + /// requires C + /// void baz(T t) { + /// ... + /// \endcode + RCPS_OwnLineWithBrace, /// Try to put the clause together with the preceding part of a declaration. /// For class templates: stick to the template declaration. /// For function templates: stick to the template declaration. diff --git a/clang/lib/Format/ContinuationIndenter.cpp b/clang/lib/Format/ContinuationIndenter.cpp index 43d246b7f82419..46dafad65863dc 100644 --- a/clang/lib/Format/ContinuationIndenter.cpp +++ b/clang/lib/Format/ContinuationIndenter.cpp @@ -1427,6 +1427,7 @@ unsigned ContinuationIndenter::getNewLineColumn(const LineState &State) { switch (Style.RequiresClausePosition) { case FormatStyle::RCPS_OwnLine: case FormatStyle::RCPS_WithFollowing: + case FormatStyle::RCPS_OwnLineWithBrace: return CurrentState.Indent; default: break; diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp index 5358b35c19de25..a795d869a3cb66 100644 --- a/clang/lib/Format/Format.cpp +++ b/clang/lib/Format/Format.cpp @@ -540,6 +540,7 @@ struct ScalarEnumerationTraits { static void enumeration(IO &IO, FormatStyle::RequiresClausePositionStyle &Value) { IO.enumCase(Value, "OwnLine", FormatStyle::RCPS_OwnLine); + IO.enumCase(Value, "OwnLineWithBrace", FormatStyle::RCPS_OwnLineWithBrace); IO.enumCase(Value, "WithPreceding", FormatStyle::RCPS_WithPreceding); IO.enumCase(Value, "WithFollowing", FormatStyle::RCPS_WithFollowing); IO.enumCase(Value, "SingleLine", FormatStyle::RCPS_SingleLine); diff --git a/clang/lib/Format/TokenAnnotator.cpp b/clang/lib/Format/TokenAnnotator.cpp index 9f79fa9fc516ca..9d4204655b8ed6 100644 --- a/clang/lib/Format/TokenAnnotator.cpp +++ b/clang/lib/Format/TokenAnnotator.cpp @@ -5681,6 +5681,7 @@ bool TokenAnnotator::mustBreakBefore(const AnnotatedLine &Line, if (Right.is(TT_RequiresClause)) { switch (Style.RequiresClausePosition) { case FormatStyle::RCPS_OwnLine: + case FormatStyle::RCPS_OwnLineWithBrace: case FormatStyle::RCPS_WithFollowing: return true; default: @@ -5699,11 +5700,13 @@ bool TokenAnnotator::mustBreakBefore(const AnnotatedLine &Line, (Style.BreakTemplateDeclarations == FormatStyle::BTDS_Leave && Right.NewlinesBefore > 0); } - if (Left.ClosesRequiresClause && Right.isNot(tok::semi)) { + if (Left.ClosesRequiresClause) { switch (Style.RequiresClausePosition) { case FormatStyle::RCPS_OwnLine: case FormatStyle::RCPS_WithPreceding: - return true; + return Right.isNot(tok::semi); + case FormatStyle::RCPS_OwnLineWithBrace: + return !Right.isOneOf(tok::semi, tok::l_brace); default: break; } diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp index bad1b1d662d133..794ccab3704534 100644 --- a/clang/unittests/Format/FormatTest.cpp +++ b/clang/unittests/Format/FormatTest.cpp @@ -25857,7 +25857,90 @@ TEST_F(FormatTest, RequiresClausesPositions) { "}", Style); + Style.RequiresClausePosition = FormatStyle::RCPS_OwnLineWithBrace; + Style.IndentRequiresClause = true; + + verifyFormat("template \n" + " requires(Foo && std::trait)\n" + "struct Bar;", + Style); + + verifyFormat("template \n" + " requires(Foo && std::trait)\n" + "class Bar {\n" + "public:\n" + " Bar(T t);\n" + " bool baz();\n" + "};", + Style); + + verifyFormat( + "template \n" + " requires requires(T &&t) {\n" + " typename T::I;\n" + " requires(F && std::trait);\n" + " }\n" + "Bar(T) -> Bar;", + Style); + + verifyFormat("template \n" + " requires(Foo && std::trait)\n" + "constexpr T MyGlobal;", + Style); + + verifyFormat("template \n" + " requires Foo && requires(T t) {\n" + " { t.baz() } -> std::same_as;\n" + " requires std::same_as;\n" + " }\n" + "inline int bar(T t) {\n" + " return t.baz() ? T::Factor : 5;\n" + "}", + Style); + + verifyFormat("template \n" + "inline int bar(T t)\n" + " requires Foo && requires(T t) {\n" + " { t.baz() } -> std::same_as;\n" + " requires std::same_as;\n" + " } {\n" + " return t.baz() ? T::Factor : 5;\n" + "}", + Style); + + verifyFormat("template \n" + " requires F\n" + "int bar(T t) {\n" + " return 5;\n" + "}", + Style); + + verifyFormat("template \n" + "int bar(T t)\n" + " requires F {\n" + " return 5;\n" + "}", + Style); + + verifyFormat("template \n" + "int S::bar(T t) &&\n" + " requires F {\n" + " return 5;\n" + "}", + Style); + + verifyFormat("template \n" + "int bar(T t)\n" + " requires F;", + Style); + + verifyFormat("template \n" + "int bar(T t)\n" + " requires F {}", + Style); + Style.RequiresClausePosition = FormatStyle::RCPS_SingleLine; + Style.IndentRequiresClause = false; verifyFormat("template requires Foo struct Bar {};\n" "template requires Foo void bar() {}\n" "template void bar() requires Foo {}\n"