diff --git a/clang/docs/ClangFormatStyleOptions.rst b/clang/docs/ClangFormatStyleOptions.rst index ff424828ff63c..37d76d5c2dd58 100644 --- a/clang/docs/ClangFormatStyleOptions.rst +++ b/clang/docs/ClangFormatStyleOptions.rst @@ -2046,6 +2046,21 @@ the configuration (without a prefix: ``Auto``). }; } +.. _BreakAdjacentStringLiterals: + +**BreakAdjacentStringLiterals** (``Boolean``) :versionbadge:`clang-format 18` :ref:`¶ ` + Break between adjacent string literals. + + .. code-block:: c++ + + true: + return "Code" + "\0\52\26\55\55\0" + "x013" + "\02\xBA"; + false: + return "Code" "\0\52\26\55\55\0" "x013" "\02\xBA"; + .. _BreakAfterAttributes: **BreakAfterAttributes** (``AttributeBreakingStyle``) :versionbadge:`clang-format 16` :ref:`¶ ` diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 52c9d6eb69617..b9d285c2a5fd3 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -884,6 +884,7 @@ clang-format - Add ``AllowBreakBeforeNoexceptSpecifier`` option. - Add ``AllowShortCompoundRequirementOnASingleLine`` option. - Change ``BreakAfterAttributes`` from ``Never`` to ``Leave`` in LLVM style. +- Add ``BreakAdjacentStringLiterals`` option. libclang -------- diff --git a/clang/include/clang/Format/Format.h b/clang/include/clang/Format/Format.h index bc412611ef624..37cb3b5cc0673 100644 --- a/clang/include/clang/Format/Format.h +++ b/clang/include/clang/Format/Format.h @@ -1424,6 +1424,19 @@ struct FormatStyle { /// \version 3.8 BraceWrappingFlags BraceWrapping; + /// Break between adjacent string literals. + /// \code + /// true: + /// return "Code" + /// "\0\52\26\55\55\0" + /// "x013" + /// "\02\xBA"; + /// false: + /// return "Code" "\0\52\26\55\55\0" "x013" "\02\xBA"; + /// \endcode + /// \version 18 + bool BreakAdjacentStringLiterals; + /// Different ways to break after attributes. enum AttributeBreakingStyle : int8_t { /// Always break after attributes. @@ -4745,6 +4758,7 @@ struct FormatStyle { BinPackParameters == R.BinPackParameters && BitFieldColonSpacing == R.BitFieldColonSpacing && BracedInitializerIndentWidth == R.BracedInitializerIndentWidth && + BreakAdjacentStringLiterals == R.BreakAdjacentStringLiterals && BreakAfterAttributes == R.BreakAfterAttributes && BreakAfterJavaFieldAnnotations == R.BreakAfterJavaFieldAnnotations && BreakArrays == R.BreakArrays && diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp index e1abcac5604a4..db0cb8a310849 100644 --- a/clang/lib/Format/Format.cpp +++ b/clang/lib/Format/Format.cpp @@ -965,6 +965,8 @@ template <> struct MappingTraits { IO.mapOptional("BracedInitializerIndentWidth", Style.BracedInitializerIndentWidth); IO.mapOptional("BraceWrapping", Style.BraceWrapping); + IO.mapOptional("BreakAdjacentStringLiterals", + Style.BreakAdjacentStringLiterals); IO.mapOptional("BreakAfterAttributes", Style.BreakAfterAttributes); IO.mapOptional("BreakAfterJavaFieldAnnotations", Style.BreakAfterJavaFieldAnnotations); @@ -1476,6 +1478,7 @@ FormatStyle getLLVMStyle(FormatStyle::LanguageKind Language) { /*SplitEmptyFunction=*/true, /*SplitEmptyRecord=*/true, /*SplitEmptyNamespace=*/true}; + LLVMStyle.BreakAdjacentStringLiterals = true; LLVMStyle.BreakAfterAttributes = FormatStyle::ABS_Leave; LLVMStyle.BreakAfterJavaFieldAnnotations = false; LLVMStyle.BreakArrays = true; diff --git a/clang/lib/Format/TokenAnnotator.cpp b/clang/lib/Format/TokenAnnotator.cpp index cfca7c00312aa..b3da3c3bf2036 100644 --- a/clang/lib/Format/TokenAnnotator.cpp +++ b/clang/lib/Format/TokenAnnotator.cpp @@ -5078,11 +5078,10 @@ bool TokenAnnotator::mustBreakBefore(const AnnotatedLine &Line, // it is hard to identify them in UnwrappedLineParser. if (!Keywords.isVerilogBegin(Right) && Keywords.isVerilogEndOfLabel(Left)) return true; - } else if (Style.Language == FormatStyle::LK_Cpp || - Style.Language == FormatStyle::LK_ObjC || - Style.Language == FormatStyle::LK_Proto || - Style.Language == FormatStyle::LK_TableGen || - Style.Language == FormatStyle::LK_TextProto) { + } else if (Style.BreakAdjacentStringLiterals && + (Style.isCpp() || Style.isProto() || + Style.Language == FormatStyle::LK_TableGen || + Style.Language == FormatStyle::LK_TextProto)) { if (Left.isStringLiteral() && Right.isStringLiteral()) return true; } diff --git a/clang/unittests/Format/ConfigParseTest.cpp b/clang/unittests/Format/ConfigParseTest.cpp index 3a6667dbe0eb1..0c9f68f303d86 100644 --- a/clang/unittests/Format/ConfigParseTest.cpp +++ b/clang/unittests/Format/ConfigParseTest.cpp @@ -159,6 +159,7 @@ TEST(ConfigParseTest, ParsesConfigurationBools) { CHECK_PARSE_BOOL(AllowShortLoopsOnASingleLine); CHECK_PARSE_BOOL(BinPackArguments); CHECK_PARSE_BOOL(BinPackParameters); + CHECK_PARSE_BOOL(BreakAdjacentStringLiterals); CHECK_PARSE_BOOL(BreakAfterJavaFieldAnnotations); CHECK_PARSE_BOOL(BreakBeforeTernaryOperators); CHECK_PARSE_BOOL(BreakStringLiterals); diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp index 67423f9b06fbc..8366345ee9abc 100644 --- a/clang/unittests/Format/FormatTest.cpp +++ b/clang/unittests/Format/FormatTest.cpp @@ -26642,6 +26642,20 @@ TEST_F(FormatTest, StreamOutputOperator) { verifyFormat("std::cout << \"foo\" << \"bar\" << baz;"); } +TEST_F(FormatTest, BreakAdjacentStringLiterals) { + constexpr StringRef Code{ + "return \"Code\" \"\\0\\52\\26\\55\\55\\0\" \"x013\" \"\\02\\xBA\";"}; + + verifyFormat("return \"Code\"\n" + " \"\\0\\52\\26\\55\\55\\0\"\n" + " \"x013\"\n" + " \"\\02\\xBA\";", + Code); + + auto Style = getLLVMStyle(); + Style.BreakAdjacentStringLiterals = false; + verifyFormat(Code, Style); +} } // namespace } // namespace test } // namespace format