diff --git a/clang/docs/ClangFormatStyleOptions.rst b/clang/docs/ClangFormatStyleOptions.rst index 39f7cded36edbf..bbc2b6dd176d4f 100644 --- a/clang/docs/ClangFormatStyleOptions.rst +++ b/clang/docs/ClangFormatStyleOptions.rst @@ -3359,6 +3359,50 @@ the configuration (without a prefix: ``Auto``). +.. _BreakStreamOperator: + +**BreakStreamOperator** (``BreakStreamOperatorStyle``) :versionbadge:`clang-format 19` :ref:`¶ ` + Break Between Stream Operators. + + Possible values: + + * ``BCOS_Normal`` (in configuration: ``Normal``) + Break using ColumnLimit rules. + + .. code-block:: c++ + + os << "aaaaa" << "bbbbb" << "\n"; + + * ``BCOS_BetweenStrings`` (in configuration: ``BetweenStrings``) + Break between adjacent strings. + + .. code-block:: c++ + + os << "aaaaa" + << "bbbbb" + << "\n"; + + * ``BCOS_BetweenNewlineStrings`` (in configuration: ``BetweenNewlineStrings``) + Break between adjacent strings that end with \n. + + .. code-block:: c++ + + os << "aaaaa\n" + << "bbbbb" << "ccccc\n" + << "\n"; + + * ``BCOS_Always`` (in configuration: ``Always``) + Break between adjacent stream operations. + + .. code-block:: c++ + + os << "aaaaa\n" + << "bbbbb" + << "ccccc\n" + << "\n"; + + + .. _BreakStringLiterals: **BreakStringLiterals** (``Boolean``) :versionbadge:`clang-format 3.9` :ref:`¶ ` diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 45a9a79739a4eb..b4c2c00f1a0229 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -671,6 +671,8 @@ clang-format ``BreakTemplateDeclarations``. - ``AlwaysBreakAfterReturnType`` is deprecated and renamed to ``BreakAfterReturnType``. +- ``BreakStreamOperator`` Style is added and the previous default + of breaking between strings is reverted. libclang -------- diff --git a/clang/include/clang/Format/Format.h b/clang/include/clang/Format/Format.h index 48f5fb44157570..81f621f5fbb363 100644 --- a/clang/include/clang/Format/Format.h +++ b/clang/include/clang/Format/Format.h @@ -2348,6 +2348,41 @@ struct FormatStyle { /// \version 7 BreakInheritanceListStyle BreakInheritanceList; + /// Different ways to Break Between Stream Operators. + enum BreakStreamOperatorStyle : int8_t { + /// Break using ColumnLimit rules. + /// \code + /// os << "aaaaa" << "bbbbb" << "\n"; + /// \endcode + BCOS_Normal, + /// Break between adjacent strings. + /// \code + /// os << "aaaaa" + /// << "bbbbb" + /// << "\n"; + /// \endcode + BCOS_BetweenStrings, + /// Break between adjacent strings that end with \n. + /// \code + /// os << "aaaaa\n" + /// << "bbbbb" << "ccccc\n" + /// << "\n"; + /// \endcode + BCOS_BetweenNewlineStrings, + /// Break between adjacent stream operations. + /// \code + /// os << "aaaaa\n" + /// << "bbbbb" + /// << "ccccc\n" + /// << "\n"; + /// \endcode + BCOS_Always + }; + + /// Break Between Stream Operators. + /// \version 19 + BreakStreamOperatorStyle BreakStreamOperator; + /// The template declaration breaking style to use. /// \version 19 BreakTemplateDeclarationsStyle BreakTemplateDeclarations; @@ -4951,6 +4986,7 @@ struct FormatStyle { BreakBeforeConceptDeclarations == R.BreakBeforeConceptDeclarations && BreakBeforeInlineASMColon == R.BreakBeforeInlineASMColon && BreakBeforeTernaryOperators == R.BreakBeforeTernaryOperators && + BreakStreamOperator == R.BreakStreamOperator && BreakConstructorInitializers == R.BreakConstructorInitializers && BreakFunctionDefinitionParameters == R.BreakFunctionDefinitionParameters && diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp old mode 100644 new mode 100755 index 89e6c19b0af45c..25910770e805f6 --- a/clang/lib/Format/Format.cpp +++ b/clang/lib/Format/Format.cpp @@ -264,6 +264,18 @@ struct ScalarEnumerationTraits { } }; +template <> +struct ScalarEnumerationTraits { + static void enumeration(IO &IO, + FormatStyle::BreakStreamOperatorStyle &Value) { + IO.enumCase(Value, "Normal", FormatStyle::BCOS_Normal); + IO.enumCase(Value, "BetweenStrings", FormatStyle::BCOS_BetweenStrings); + IO.enumCase(Value, "BetweenNewlineStrings", + FormatStyle::BCOS_BetweenNewlineStrings); + IO.enumCase(Value, "Always", FormatStyle::BCOS_Always); + } +}; + template <> struct ScalarEnumerationTraits { static void enumeration(IO &IO, @@ -953,6 +965,7 @@ template <> struct MappingTraits { Style.BreakBeforeInlineASMColon); IO.mapOptional("BreakBeforeTernaryOperators", Style.BreakBeforeTernaryOperators); + IO.mapOptional("BreakStreamOperator", Style.BreakStreamOperator); IO.mapOptional("BreakConstructorInitializers", Style.BreakConstructorInitializers); IO.mapOptional("BreakFunctionDefinitionParameters", @@ -1469,6 +1482,7 @@ FormatStyle getLLVMStyle(FormatStyle::LanguageKind Language) { LLVMStyle.BreakConstructorInitializers = FormatStyle::BCIS_BeforeColon; LLVMStyle.BreakFunctionDefinitionParameters = false; LLVMStyle.BreakInheritanceList = FormatStyle::BILS_BeforeColon; + LLVMStyle.BreakStreamOperator = FormatStyle::BCOS_BetweenStrings; LLVMStyle.BreakStringLiterals = true; LLVMStyle.BreakTemplateDeclarations = FormatStyle::BTDS_MultiLine; LLVMStyle.ColumnLimit = 80; diff --git a/clang/lib/Format/TokenAnnotator.cpp b/clang/lib/Format/TokenAnnotator.cpp index 628f70417866c3..d5f8989369b395 100644 --- a/clang/lib/Format/TokenAnnotator.cpp +++ b/clang/lib/Format/TokenAnnotator.cpp @@ -5598,11 +5598,45 @@ bool TokenAnnotator::mustBreakBefore(const AnnotatedLine &Line, // FIXME: Breaking after newlines seems useful in general. Turn this into an // option and recognize more cases like endl etc, and break independent of // what comes after operator lessless. - if (Right.is(tok::lessless) && Right.Next && - Right.Next->is(tok::string_literal) && Left.is(tok::string_literal) && - Left.TokenText.ends_with("\\n\"")) { - return true; + switch (Style.BreakStreamOperator) { + case FormatStyle::BCOS_BetweenStrings: { + if (Right.is(tok::lessless) && Right.Next && Left.is(tok::string_literal) && + Right.Next->is(tok::string_literal)) { + return true; + } + break; + } + case FormatStyle::BCOS_BetweenNewlineStrings: { + if (Right.is(tok::lessless) && Right.Next && + Right.Next->is(tok::string_literal) && Left.is(tok::string_literal) && + Left.TokenText.ends_with("\\n\"")) { + return true; + } + break; + } + case FormatStyle::BCOS_Always: { + // Don't break after the very first << or >> + // but the Left token can be os or std::os so + // scan back + auto *FirstStream = Right.Previous; + while (FirstStream) { + if (FirstStream->isOneOf(tok::lessless, tok::greater)) + break; + FirstStream = FirstStream->Previous; + } + + if (Right.is(tok::lessless) && FirstStream) + return true; + if (Right.is(tok::greater) && Right.Next && Right.Next->is(tok::greater) && + FirstStream) { + return true; + } + break; } + case FormatStyle::BCOS_Normal: + break; + } + if (Right.is(TT_RequiresClause)) { switch (Style.RequiresClausePosition) { case FormatStyle::RCPS_OwnLine: diff --git a/clang/unittests/Format/ConfigParseTest.cpp b/clang/unittests/Format/ConfigParseTest.cpp old mode 100644 new mode 100755 index 8c74ed2d119a3f..3130431d7f4259 --- a/clang/unittests/Format/ConfigParseTest.cpp +++ b/clang/unittests/Format/ConfigParseTest.cpp @@ -420,6 +420,16 @@ TEST(ConfigParseTest, ParsesConfiguration) { CHECK_PARSE("BreakBeforeInheritanceComma: true", BreakInheritanceList, FormatStyle::BILS_BeforeComma); + Style.BreakStreamOperator = FormatStyle::BCOS_BetweenStrings; + CHECK_PARSE("BreakStreamOperator: BetweenNewlineStrings", BreakStreamOperator, + FormatStyle::BCOS_BetweenNewlineStrings); + CHECK_PARSE("BreakStreamOperator: Normal", BreakStreamOperator, + FormatStyle::BCOS_Normal); + CHECK_PARSE("BreakStreamOperator: BetweenStrings", BreakStreamOperator, + FormatStyle::BCOS_BetweenStrings); + CHECK_PARSE("BreakStreamOperator: Always", BreakStreamOperator, + FormatStyle::BCOS_Always); + Style.PackConstructorInitializers = FormatStyle::PCIS_BinPack; CHECK_PARSE("PackConstructorInitializers: Never", PackConstructorInitializers, FormatStyle::PCIS_Never); diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp old mode 100644 new mode 100755 index 4906b3350b5b22..514ffa9e97b7dd --- a/clang/unittests/Format/FormatTest.cpp +++ b/clang/unittests/Format/FormatTest.cpp @@ -27340,9 +27340,105 @@ TEST_F(FormatTest, PPDirectivesAndCommentsInBracedInit) { } TEST_F(FormatTest, StreamOutputOperator) { - verifyFormat("std::cout << \"foo\" << \"bar\" << baz;"); - verifyFormat("std::cout << \"foo\\n\"\n" - " << \"bar\";"); + auto Style = getLLVMStyle(); + + // This should be the default as it was the original style, thats + // been in place since the beginning. + verifyFormat("std::cout << \"aaaa\"\n" + " << \"bbbbb\";", + Style); + verifyFormat("std::cout << \"aaaa\"\n" + " << \"bbbbb\"\n" + " << \"ccc\";", + Style); + verifyFormat("std::cout << \"aaaa\"\n" + " << \"bbbbb\"\n" + " << \"cccc\"\n" + " << \"ddd\";", + Style); + verifyFormat("std::cout << \"aaaa\"\n" + " << \"bbbbb\" << baz << \"cccc\"\n" + " << \"ddd\";", + Style); + + Style.BreakStreamOperator = FormatStyle::BCOS_Normal; + verifyFormat("std::cout << \"aaaa\" << \"bbb\" << baz;", Style); + verifyFormat("std::cout << \"ccc\\n\" << \"dddd\";", Style); + + Style.BreakStreamOperator = FormatStyle::BCOS_BetweenStrings; + verifyFormat("std::cout << \"eee\"\n" + " << \"ffff\";", + Style); + verifyFormat("std::cout << \"aa\\n\"\n" + " << \"bbbb\";", + Style); + + Style.BreakStreamOperator = FormatStyle::BCOS_BetweenNewlineStrings; + verifyFormat("std::cout << \"aaaa\" << \"bbb\" << baz;", Style); + verifyFormat("std::cout << \"ggg\\n\"\n" + " << \"dddd\";", + Style); + + Style.BreakStreamOperator = FormatStyle::BCOS_Always; + verifyFormat("std::cout << \"aaaa\"\n" + " << \"bbb\"\n" + " << baz;", + Style); + verifyFormat("std::cout << \"ggg\\n\"\n" + " << \"dddd\";", + Style); + verifyFormat("std::cout << \"aaaa\"\n" + " << \"bbbbb\"\n" + " << \"cccc\"\n" + " << \"ddd\";", + Style); + verifyFormat("std::cout << \"aaaa\"\n" + " << \"bbbbb\"\n" + " << baz\n" + " << \"cccc\"\n" + " << \"ddd\";", + Style); + verifyFormat("cout << \"aaaa\"\n" + " << \"bbbbb\"\n" + " << baz\n" + " << \"cccc\"\n" + " << \"ddd\";", + Style); +} + +TEST_F(FormatTest, StreamInputOperator) { + auto Style = getLLVMStyle(); + + Style.BreakStreamOperator = FormatStyle::BCOS_Normal; + verifyFormat("std::in >> aaaa >> bbb >> baz;", Style); + verifyFormat("std::in >> ccc >> dddd;", Style); + + Style.BreakStreamOperator = FormatStyle::BCOS_Always; + verifyFormat("std::in >> ccc;", Style); + verifyFormat("std::in >> aaaa\n" + " >> bbb\n" + " >> baz;", + Style); + verifyFormat("std::in >> ggg\n" + " >> dddd;", + Style); + verifyFormat("std::in >> aaaa\n" + " >> bbbbb\n" + " >> cccc\n" + " >> ddd;", + Style); + verifyFormat("std::in >> aaaa\n" + " >> bbbbb\n" + " >> baz\n" + " >> cccc\n" + " >> ddd;", + Style); + verifyFormat("in >> aaaa\n" + " >> bbbbb\n" + " >> baz\n" + " >> cccc\n" + " >> ddd;", + Style); } TEST_F(FormatTest, BreakAdjacentStringLiterals) {