Skip to content

[clang-format] revert to string << string handling to previous default #88490

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed

Conversation

mydeveloperday
Copy link
Contributor

Fixes 88433

A change made to the handling of chevron operators causes a large amount of flux in code bases that were previously using clang-format, this fix reverts that change to the default behaviour but adds that new behaviour behind a new option.

default

Fixes 88433

A change made to the handling of chevron operators causes a large amount
of flux in code bases that were previously using clang-format, this fix
reverts that change to the default behaviour but adds that new behaviour
behind a new option.
@llvmbot llvmbot added clang Clang issues not falling into any other category clang-format labels Apr 12, 2024
@mydeveloperday mydeveloperday requested review from owenca and rymiel April 12, 2024 09:37
@llvmbot
Copy link
Member

llvmbot commented Apr 12, 2024

@llvm/pr-subscribers-clang-format

@llvm/pr-subscribers-clang

Author: MyDeveloperDay (mydeveloperday)

Changes

Fixes 88433

A change made to the handling of chevron operators causes a large amount of flux in code bases that were previously using clang-format, this fix reverts that change to the default behaviour but adds that new behaviour behind a new option.


Full diff: https://github.com/llvm/llvm-project/pull/88490.diff

7 Files Affected:

  • (modified) clang/docs/ClangFormatStyleOptions.rst (+34)
  • (modified) clang/docs/ReleaseNotes.rst (+2)
  • (modified) clang/include/clang/Format/Format.h (+28)
  • (modified) clang/lib/Format/Format.cpp (+13)
  • (modified) clang/lib/Format/TokenAnnotator.cpp (+12-4)
  • (modified) clang/unittests/Format/ConfigParseTest.cpp (+8)
  • (modified) clang/unittests/Format/FormatTest.cpp (+39-3)
diff --git a/clang/docs/ClangFormatStyleOptions.rst b/clang/docs/ClangFormatStyleOptions.rst
index 39f7cded36edbf..a40a940f39d860 100644
--- a/clang/docs/ClangFormatStyleOptions.rst
+++ b/clang/docs/ClangFormatStyleOptions.rst
@@ -3258,6 +3258,40 @@ the configuration (without a prefix: ``Auto``).
          firstValue :
          SecondValueVeryVeryVeryVeryLong;
 
+.. _BreakChevronOperator:
+
+**BreakChevronOperator** (``BreakChevronOperatorStyle``) :versionbadge:`clang-format 19` :ref:`¶ <BreakChevronOperator>`
+  Break Between Chevron Operators
+
+  Possible values:
+
+  * ``BCOS_Never`` (in configuration: ``Never``)
+    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";
+
+
+
 .. _BreakConstructorInitializers:
 
 **BreakConstructorInitializers** (``BreakConstructorInitializersStyle``) :versionbadge:`clang-format 5` :ref:`¶ <BreakConstructorInitializers>`
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 45a9a79739a4eb..01838b0ccd653d 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``.
+- ``BreakChevronOperator`` 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..205c597af8fb0f 100644
--- a/clang/include/clang/Format/Format.h
+++ b/clang/include/clang/Format/Format.h
@@ -2193,6 +2193,33 @@ struct FormatStyle {
   /// \version 3.7
   bool BreakBeforeTernaryOperators;
 
+  /// Different ways to Break Between Chevrons
+  enum BreakChevronOperatorStyle : int8_t {
+    /// Break using ColumnLimit rules.
+    /// \code
+    ///   os << "aaaaa" << "bbbbb" << "\n";
+    /// \endcode
+    BCOS_Never,
+    /// 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 Chevron Operators
+  /// \version 19
+  BreakChevronOperatorStyle BreakChevronOperator;
+
   /// Different ways to break initializers.
   enum BreakConstructorInitializersStyle : int8_t {
     /// Break constructor initializers before the colon and after the commas.
@@ -4951,6 +4978,7 @@ struct FormatStyle {
            BreakBeforeConceptDeclarations == R.BreakBeforeConceptDeclarations &&
            BreakBeforeInlineASMColon == R.BreakBeforeInlineASMColon &&
            BreakBeforeTernaryOperators == R.BreakBeforeTernaryOperators &&
+           BreakChevronOperator == R.BreakChevronOperator &&
            BreakConstructorInitializers == R.BreakConstructorInitializers &&
            BreakFunctionDefinitionParameters ==
                R.BreakFunctionDefinitionParameters &&
diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp
index 89e6c19b0af45c..b781a7e161db78 100644
--- a/clang/lib/Format/Format.cpp
+++ b/clang/lib/Format/Format.cpp
@@ -243,6 +243,17 @@ struct ScalarEnumerationTraits<FormatStyle::BreakBeforeInlineASMColonStyle> {
   }
 };
 
+template <>
+struct ScalarEnumerationTraits<FormatStyle::BreakChevronOperatorStyle> {
+  static void enumeration(IO &IO,
+                          FormatStyle::BreakChevronOperatorStyle &Value) {
+    IO.enumCase(Value, "Never", FormatStyle::BCOS_Never);
+    IO.enumCase(Value, "BetweenStrings", FormatStyle::BCOS_BetweenStrings);
+    IO.enumCase(Value, "BetweenNewlineStrings",
+                FormatStyle::BCOS_BetweenNewlineStrings);
+  }
+};
+
 template <>
 struct ScalarEnumerationTraits<FormatStyle::BreakConstructorInitializersStyle> {
   static void
@@ -953,6 +964,7 @@ template <> struct MappingTraits<FormatStyle> {
                    Style.BreakBeforeInlineASMColon);
     IO.mapOptional("BreakBeforeTernaryOperators",
                    Style.BreakBeforeTernaryOperators);
+    IO.mapOptional("BreakChevronOperator", Style.BreakChevronOperator);
     IO.mapOptional("BreakConstructorInitializers",
                    Style.BreakConstructorInitializers);
     IO.mapOptional("BreakFunctionDefinitionParameters",
@@ -1466,6 +1478,7 @@ FormatStyle getLLVMStyle(FormatStyle::LanguageKind Language) {
   LLVMStyle.BreakBeforeConceptDeclarations = FormatStyle::BBCDS_Always;
   LLVMStyle.BreakBeforeInlineASMColon = FormatStyle::BBIAS_OnlyMultiline;
   LLVMStyle.BreakBeforeTernaryOperators = true;
+  LLVMStyle.BreakChevronOperator = FormatStyle::BCOS_BetweenStrings;
   LLVMStyle.BreakConstructorInitializers = FormatStyle::BCIS_BeforeColon;
   LLVMStyle.BreakFunctionDefinitionParameters = false;
   LLVMStyle.BreakInheritanceList = FormatStyle::BILS_BeforeColon;
diff --git a/clang/lib/Format/TokenAnnotator.cpp b/clang/lib/Format/TokenAnnotator.cpp
index 628f70417866c3..ae4bda3511b58d 100644
--- a/clang/lib/Format/TokenAnnotator.cpp
+++ b/clang/lib/Format/TokenAnnotator.cpp
@@ -5598,10 +5598,18 @@ 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;
+  if (Style.BreakChevronOperator == FormatStyle::BCOS_BetweenStrings) {
+    if (Right.is(tok::lessless) && Right.Next && Left.is(tok::string_literal) &&
+        Right.Next->is(tok::string_literal)) {
+      return true;
+    }
+  }
+  if (Style.BreakChevronOperator == 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;
+    }
   }
   if (Right.is(TT_RequiresClause)) {
     switch (Style.RequiresClausePosition) {
diff --git a/clang/unittests/Format/ConfigParseTest.cpp b/clang/unittests/Format/ConfigParseTest.cpp
index 8c74ed2d119a3f..07c070b0338711 100644
--- a/clang/unittests/Format/ConfigParseTest.cpp
+++ b/clang/unittests/Format/ConfigParseTest.cpp
@@ -396,6 +396,14 @@ TEST(ConfigParseTest, ParsesConfiguration) {
   CHECK_PARSE("BreakBeforeBinaryOperators: true", BreakBeforeBinaryOperators,
               FormatStyle::BOS_All);
 
+  Style.BreakChevronOperator = FormatStyle::BCOS_BetweenStrings;
+  CHECK_PARSE("BreakChevronOperator: BetweenNewlineStrings",
+              BreakChevronOperator, FormatStyle::BCOS_BetweenNewlineStrings);
+  CHECK_PARSE("BreakChevronOperator: Never", BreakChevronOperator,
+              FormatStyle::BCOS_Never);
+  CHECK_PARSE("BreakChevronOperator: BetweenStrings", BreakChevronOperator,
+              FormatStyle::BCOS_BetweenStrings);
+
   Style.BreakConstructorInitializers = FormatStyle::BCIS_BeforeColon;
   CHECK_PARSE("BreakConstructorInitializers: BeforeComma",
               BreakConstructorInitializers, FormatStyle::BCIS_BeforeComma);
diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp
index 4906b3350b5b22..566bc9b472d21e 100644
--- a/clang/unittests/Format/FormatTest.cpp
+++ b/clang/unittests/Format/FormatTest.cpp
@@ -27340,9 +27340,44 @@ 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.BreakChevronOperator = FormatStyle::BCOS_Never;
+  verifyFormat("std::cout << \"aaaa\" << \"bbb\" << baz;", Style);
+  verifyFormat("std::cout << \"ccc\\n\" << \"dddd\";", Style);
+
+  Style.BreakChevronOperator = FormatStyle::BCOS_BetweenStrings;
+  verifyFormat("std::cout << \"eee\"\n"
+               "          << \"ffff\";",
+               Style);
+  verifyFormat("std::cout << \"aa\\n\"\n"
+               "          << \"bbbb\";",
+               Style);
+
+  Style.BreakChevronOperator = FormatStyle::BCOS_BetweenNewlineStrings;
+  verifyFormat("std::cout << \"aaaa\" << \"bbb\" << baz;", Style);
+  verifyFormat("std::cout << \"ggg\\n\"\n"
+               "          << \"dddd\";",
+               Style);
 }
 
 TEST_F(FormatTest, BreakAdjacentStringLiterals) {
@@ -27363,3 +27398,4 @@ TEST_F(FormatTest, BreakAdjacentStringLiterals) {
 } // namespace test
 } // namespace format
 } // namespace clang
+                    

Copy link

github-actions bot commented Apr 12, 2024

✅ With the latest revision this PR passed the C/C++ code formatter.

@mydeveloperday
Copy link
Contributor Author

Fixes #88483

@mydeveloperday
Copy link
Contributor Author

Other related issues

#44363 , #59797

@mydeveloperday mydeveloperday requested a review from kadircet April 12, 2024 09:44
…efault

Fixes llvm#88433

A change made to the handling of chevron operators causes a large amount
of flux in code bases that were previously using clang-format, this fix
reverts that change to the default behaviour but adds that new
behaviours behind a new option.
Fixes llvm#88433

A change made to the handling of chevron operators causes a large amount
of flux in code bases that were previously using clang-format, this fix
reverts that change to the default behaviour but adds that new
behaviours behind a new option BreakChevronOperator.
Fixes llvm#88433

A change made to the handling of chevron operators causes a large amount
of flux in code bases that were previously using clang-format, this fix
reverts that change to the default behaviour but adds that new
behaviours behind a new option BreakChevronOperator.
@mydeveloperday mydeveloperday changed the title [clang-format] revery to string << string handling back to previous default behaviour [clang-format] revert to string << string handling to previous default Apr 12, 2024
Fixes llvm#88433

A change made to the handling of chevron operators causes a large amount
of flux in code bases that were previously using clang-format, this fix
reverts that change to the default behaviour but adds that new
behaviours behind a new option BreakChevronOperator.
}
}
if (Style.BreakChevronOperator == FormatStyle::BCOS_Always) {
// can be std::os or os
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand that comment.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

when you have

os << "A"
<< "B"

you don't want to break between os and << so the loop tracks back to determine if this is the first << in the line, this must also handle std::os << and a::b::os etc.. whats its really saying is don't break before the first <<.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah ok, but I'd skip the os.

Fixes llvm#88433

A change made to the handling of streaming operators causes a large amount
of flux in code bases that were previously using clang-format, this fix
reverts that change to the default behaviour but adds that new
behaviours behind a new option BreakStreamOperator.
Fixes llvm#88433

A change made to the handling of streaming operators causes a large amount
of flux in code bases that were previously using clang-format, this fix
reverts that change to the default behaviour but adds that new
behaviours behind a new option BreakStreamOperator.

- Sort Alphabetically
}
case FormatStyle::BCOS_Always: {
// Don't break after the very first << or >>
// but the Left token can be os or std::os so
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still don't understand the part of os or std::os.

}
}
if (Style.BreakChevronOperator == FormatStyle::BCOS_Always) {
// can be std::os or os
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah ok, but I'd skip the os.

@owenca
Copy link
Contributor

owenca commented Apr 14, 2024

Please see #88483 (comment) and Conversation in #69859, which was the first attempt at adding an option for breaking consecutive stream insertion operations. Instead of handling everything specified in #69859 (comment) in a single patch, we can take a few steps:

  1. Handle when to break before <<. The current default (since 18.x) is only when the left operand is a string literal ending with a newline and the right operand is a string literal.
  2. Handle whether to indent or align after the break.
  3. Do the above for breaking after <<.

(Ditto for stream extraction operations if needed.)

For No. 1 above, an enum option (e.g. BreakBeforeStreamInsertionOperator) with Leave, AfterNewline (LLVM default), Always, and Never may be sufficient. @s1Sharp

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang Clang issues not falling into any other category clang-format
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants