diff --git a/clang/docs/ClangFormatStyleOptions.rst b/clang/docs/ClangFormatStyleOptions.rst index df27f6166d371..44bffa41d1cde 100644 --- a/clang/docs/ClangFormatStyleOptions.rst +++ b/clang/docs/ClangFormatStyleOptions.rst @@ -1108,6 +1108,54 @@ the configuration (without a prefix: ``Auto``). int d, int e); +.. _AllowBreakBeforeNoexceptSpecifier: + +**AllowBreakBeforeNoexceptSpecifier** (``BreakBeforeNoexceptSpecifierStyle``) :versionbadge:`clang-format 18` :ref:`¶ ` + Controls if there could be a line break before a ``noexcept`` specifier. + + Possible values: + + * ``BBNSS_Never`` (in configuration: ``Never``) + No line break allowed. + + .. code-block:: c++ + + void foo(int arg1, + double arg2) noexcept; + + void bar(int arg1, double arg2) noexcept( + noexcept(baz(arg1)) && + noexcept(baz(arg2))); + + * ``BBNSS_OnlyWithParen`` (in configuration: ``OnlyWithParen``) + For a simple ``noexcept`` there is no line break allowed, but when we + have a condition it is. + + .. code-block:: c++ + + void foo(int arg1, + double arg2) noexcept; + + void bar(int arg1, double arg2) + noexcept(noexcept(baz(arg1)) && + noexcept(baz(arg2))); + + * ``BBNSS_Always`` (in configuration: ``Always``) + Line breaks are allowed. But note that because of the associated + penalties ``clang-format`` often prefers not to break before the + ``noexcept``. + + .. code-block:: c++ + + void foo(int arg1, + double arg2) noexcept; + + void bar(int arg1, double arg2) + noexcept(noexcept(baz(arg1)) && + noexcept(baz(arg2))); + + + .. _AllowShortBlocksOnASingleLine: **AllowShortBlocksOnASingleLine** (``ShortBlockStyle``) :versionbadge:`clang-format 3.5` :ref:`¶ ` diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 28a6ec0acd409..5c1f7ae54ffe0 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -374,6 +374,7 @@ AST Matchers clang-format ------------ +- Add ``AllowBreakBeforeNoexceptSpecifier`` option. libclang -------- diff --git a/clang/include/clang/Format/Format.h b/clang/include/clang/Format/Format.h index 7ebde3c174d26..e0444966b3eab 100644 --- a/clang/include/clang/Format/Format.h +++ b/clang/include/clang/Format/Format.h @@ -593,6 +593,47 @@ struct FormatStyle { /// \version 3.3 bool AllowAllParametersOfDeclarationOnNextLine; + /// Different ways to break before a noexcept specifier. + enum BreakBeforeNoexceptSpecifierStyle : int8_t { + /// No line break allowed. + /// \code + /// void foo(int arg1, + /// double arg2) noexcept; + /// + /// void bar(int arg1, double arg2) noexcept( + /// noexcept(baz(arg1)) && + /// noexcept(baz(arg2))); + /// \endcode + BBNSS_Never, + /// For a simple ``noexcept`` there is no line break allowed, but when we + /// have a condition it is. + /// \code + /// void foo(int arg1, + /// double arg2) noexcept; + /// + /// void bar(int arg1, double arg2) + /// noexcept(noexcept(baz(arg1)) && + /// noexcept(baz(arg2))); + /// \endcode + BBNSS_OnlyWithParen, + /// Line breaks are allowed. But note that because of the associated + /// penalties ``clang-format`` often prefers not to break before the + /// ``noexcept``. + /// \code + /// void foo(int arg1, + /// double arg2) noexcept; + /// + /// void bar(int arg1, double arg2) + /// noexcept(noexcept(baz(arg1)) && + /// noexcept(baz(arg2))); + /// \endcode + BBNSS_Always, + }; + + /// Controls if there could be a line break before a ``noexcept`` specifier. + /// \version 18 + BreakBeforeNoexceptSpecifierStyle AllowBreakBeforeNoexceptSpecifier; + /// Different styles for merging short blocks containing at most one /// statement. enum ShortBlockStyle : int8_t { @@ -4576,6 +4617,8 @@ struct FormatStyle { AllowAllArgumentsOnNextLine == R.AllowAllArgumentsOnNextLine && AllowAllParametersOfDeclarationOnNextLine == R.AllowAllParametersOfDeclarationOnNextLine && + AllowBreakBeforeNoexceptSpecifier == + R.AllowBreakBeforeNoexceptSpecifier && AllowShortBlocksOnASingleLine == R.AllowShortBlocksOnASingleLine && AllowShortCaseLabelsOnASingleLine == R.AllowShortCaseLabelsOnASingleLine && diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp index 339af30e38a7a..8e9be6752ce58 100644 --- a/clang/lib/Format/Format.cpp +++ b/clang/lib/Format/Format.cpp @@ -59,6 +59,16 @@ LLVM_YAML_IS_SEQUENCE_VECTOR(clang::format::FormatStyle::RawStringFormat) namespace llvm { namespace yaml { +template <> +struct ScalarEnumerationTraits { + static void enumeration(IO &IO, + FormatStyle::BreakBeforeNoexceptSpecifierStyle &Value) { + IO.enumCase(Value, "Never", FormatStyle::BBNSS_Never); + IO.enumCase(Value, "OnlyWithParen", FormatStyle::BBNSS_OnlyWithParen); + IO.enumCase(Value, "Always", FormatStyle::BBNSS_Always); + } +}; + template <> struct MappingTraits { static void enumInput(IO &IO, FormatStyle::AlignConsecutiveStyle &Value) { IO.enumCase(Value, "None", @@ -260,6 +270,7 @@ struct ScalarEnumerationTraits { IO.enumCase(Value, "Always", FormatStyle::BBIAS_Always); } }; + template <> struct ScalarEnumerationTraits { static void @@ -904,6 +915,8 @@ template <> struct MappingTraits { Style.AllowAllArgumentsOnNextLine); IO.mapOptional("AllowAllParametersOfDeclarationOnNextLine", Style.AllowAllParametersOfDeclarationOnNextLine); + IO.mapOptional("AllowBreakBeforeNoexceptSpecifier", + Style.AllowBreakBeforeNoexceptSpecifier); IO.mapOptional("AllowShortBlocksOnASingleLine", Style.AllowShortBlocksOnASingleLine); IO.mapOptional("AllowShortCaseLabelsOnASingleLine", @@ -1448,6 +1461,7 @@ FormatStyle getLLVMStyle(FormatStyle::LanguageKind Language) { LLVMStyle.BreakBeforeBraces = FormatStyle::BS_Attach; LLVMStyle.BreakBeforeConceptDeclarations = FormatStyle::BBCDS_Always; LLVMStyle.BreakBeforeInlineASMColon = FormatStyle::BBIAS_OnlyMultiline; + LLVMStyle.AllowBreakBeforeNoexceptSpecifier = FormatStyle::BBNSS_Never; LLVMStyle.BreakBeforeTernaryOperators = true; LLVMStyle.BreakConstructorInitializers = FormatStyle::BCIS_BeforeColon; LLVMStyle.BreakInheritanceList = FormatStyle::BILS_BeforeColon; diff --git a/clang/lib/Format/TokenAnnotator.cpp b/clang/lib/Format/TokenAnnotator.cpp index 3e0599eb540a9..2bb6565193cec 100644 --- a/clang/lib/Format/TokenAnnotator.cpp +++ b/clang/lib/Format/TokenAnnotator.cpp @@ -5654,6 +5654,17 @@ bool TokenAnnotator::canBreakBefore(const AnnotatedLine &Line, return !isItAnEmptyLambdaAllowed(Right, ShortLambdaOption); } + if (Right.is(tok::kw_noexcept) && Right.is(TT_TrailingAnnotation)) { + switch (Style.AllowBreakBeforeNoexceptSpecifier) { + case FormatStyle::BBNSS_Never: + return false; + case FormatStyle::BBNSS_Always: + return true; + case FormatStyle::BBNSS_OnlyWithParen: + return Right.Next && Right.Next->is(tok::l_paren); + } + } + return Left.isOneOf(tok::comma, tok::coloncolon, tok::semi, tok::l_brace, tok::kw_class, tok::kw_struct, tok::comment) || Right.isMemberAccess() || diff --git a/clang/unittests/Format/ConfigParseTest.cpp b/clang/unittests/Format/ConfigParseTest.cpp index b3b6755cadee4..dedaf546ea5ff 100644 --- a/clang/unittests/Format/ConfigParseTest.cpp +++ b/clang/unittests/Format/ConfigParseTest.cpp @@ -953,6 +953,14 @@ TEST(ConfigParseTest, ParsesConfiguration) { FormatStyle::RPS_ReturnStatement); CHECK_PARSE("RemoveParentheses: Leave", RemoveParentheses, FormatStyle::RPS_Leave); + + CHECK_PARSE("AllowBreakBeforeNoexceptSpecifier: Always", + AllowBreakBeforeNoexceptSpecifier, FormatStyle::BBNSS_Always); + CHECK_PARSE("AllowBreakBeforeNoexceptSpecifier: OnlyWithParen", + AllowBreakBeforeNoexceptSpecifier, + FormatStyle::BBNSS_OnlyWithParen); + CHECK_PARSE("AllowBreakBeforeNoexceptSpecifier: Never", + AllowBreakBeforeNoexceptSpecifier, FormatStyle::BBNSS_Never); } TEST(ConfigParseTest, ParsesConfigurationWithLanguages) { diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp index 721cf29e0db47..642459b3a0e60 100644 --- a/clang/unittests/Format/FormatTest.cpp +++ b/clang/unittests/Format/FormatTest.cpp @@ -26211,6 +26211,52 @@ TEST_F(FormatTest, RemoveParentheses) { Style); } +TEST_F(FormatTest, AllowBreakBeforeNoexceptSpecifier) { + auto Style = getLLVMStyleWithColumns(35); + + EXPECT_EQ(Style.AllowBreakBeforeNoexceptSpecifier, FormatStyle::BBNSS_Never); + verifyFormat("void foo(int arg1,\n" + " double arg2) noexcept;", + Style); + + // The following line does not fit within the 35 column limit, but that's what + // happens with no break allowed. + verifyFormat("void bar(int arg1, double arg2) noexcept(\n" + " noexcept(baz(arg1)) &&\n" + " noexcept(baz(arg2)));", + Style); + + verifyFormat("void aVeryLongFunctionNameWithoutAnyArguments() noexcept;", + Style); + + Style.AllowBreakBeforeNoexceptSpecifier = FormatStyle::BBNSS_Always; + verifyFormat("void foo(int arg1,\n" + " double arg2) noexcept;", + Style); + + verifyFormat("void bar(int arg1, double arg2)\n" + " noexcept(noexcept(baz(arg1)) &&\n" + " noexcept(baz(arg2)));", + Style); + + verifyFormat("void aVeryLongFunctionNameWithoutAnyArguments()\n" + " noexcept;", + Style); + + Style.AllowBreakBeforeNoexceptSpecifier = FormatStyle::BBNSS_OnlyWithParen; + verifyFormat("void foo(int arg1,\n" + " double arg2) noexcept;", + Style); + + verifyFormat("void bar(int arg1, double arg2)\n" + " noexcept(noexcept(baz(arg1)) &&\n" + " noexcept(baz(arg2)));", + Style); + + verifyFormat("void aVeryLongFunctionNameWithoutAnyArguments() noexcept;", + Style); +} + } // namespace } // namespace test } // namespace format