jp4a50 created this revision.
Herald added projects: All, clang, clang-format.
Herald added a subscriber: cfe-commits.
Herald added reviewers: rymiel, HazardyKnusperkeks, owenpan, MyDeveloperDay.
jp4a50 requested review of this revision.

Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D148131

Files:
  clang/docs/ReleaseNotes.rst
  clang/lib/Format/ContinuationIndenter.cpp
  clang/lib/Format/TokenAnnotator.cpp
  clang/unittests/Format/FormatTest.cpp

Index: clang/unittests/Format/FormatTest.cpp
===================================================================
--- clang/unittests/Format/FormatTest.cpp
+++ clang/unittests/Format/FormatTest.cpp
@@ -22011,8 +22011,7 @@
                "      []() -> auto {\n"
                "    int b = 32;\n"
                "    return 3;\n"
-               "  },\n"
-               "      foo, bar)\n"
+               "  }, foo, bar)\n"
                "      .foo();\n"
                "}",
                Style);
@@ -22038,32 +22037,12 @@
                "  })));\n"
                "}",
                Style);
-  Style.AlignAfterOpenBracket = FormatStyle::BAS_AlwaysBreak;
-  verifyFormat("void foo() {\n"
-               "  aFunction(\n"
-               "      1, b(c(\n"
-               "             [](d) -> Foo {\n"
-               "    auto f = e(d);\n"
-               "    return f;\n"
-               "  },\n"
-               "             foo, Bar{},\n"
-               "             [] {\n"
-               "    auto g = h();\n"
-               "    return g;\n"
-               "  },\n"
-               "             baz)));\n"
-               "}",
-               Style);
   verifyFormat("void foo() {\n"
                "  aFunction(1, b(c(foo, Bar{}, baz, [](d) -> Foo {\n"
-               "    auto f = e(\n"
-               "        foo,\n"
-               "        [&] {\n"
+               "    auto f = e(foo, [&] {\n"
                "      auto g = h();\n"
                "      return g;\n"
-               "    },\n"
-               "        qux,\n"
-               "        [&] -> Bar {\n"
+               "    }, qux, [&] -> Bar {\n"
                "      auto i = j();\n"
                "      return i;\n"
                "    });\n"
@@ -22071,28 +22050,74 @@
                "  })));\n"
                "}",
                Style);
-  verifyFormat("Namespace::Foo::Foo(\n"
-               "    LongClassName bar, AnotherLongClassName baz)\n"
+  verifyFormat("Namespace::Foo::Foo(LongClassName bar,\n"
+               "                    AnotherLongClassName baz)\n"
                "    : baz{baz}, func{[&] {\n"
                "  auto qux = bar;\n"
                "  return aFunkyFunctionCall(qux);\n"
                "}} {}",
                Style);
+  Style.AlignAfterOpenBracket = FormatStyle::BAS_AlwaysBreak;
+  // As long as all the non-lambda arguments fit on a single line, AlwaysBreak
+  // doesn't force an initial line break, even if lambdas span multiple lines.
+  // This should probably be considered a bug.
+  verifyFormat("void foo() {\n"
+               "  aFunction([](d) -> Foo {\n"
+               "    auto f = e(d);\n"
+               "    return f;\n"
+               "  }, foo, Bar{}, [] {\n"
+               "    auto g = h();\n"
+               "    return g;\n"
+               "  }, baz);\n"
+               "}",
+               Style);
+  // A long non-lambda argument forces arguments to span multiple lines and thus
+  // forces an initial line break when using AlwaysBreak.
+  verifyFormat("void foo() {\n"
+               "  aFunction(\n"
+               "      1,\n"
+               "      [](d) -> Foo {\n"
+               "    auto f = e(d);\n"
+               "    return f;\n"
+               "  }, foo, Bar{},\n"
+               "      [] {\n"
+               "    auto g = h();\n"
+               "    return g;\n"
+               "  }, bazzzzz,\n"
+               "      quuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuux);\n"
+               "}",
+               Style);
+  Style.BinPackArguments = false;
+  verifyFormat("void foo() {\n"
+               "  aFunction(\n"
+               "      1,\n"
+               "      [](d) -> Foo {\n"
+               "    auto f = e(d);\n"
+               "    return f;\n"
+               "  },\n"
+               "      foo,\n"
+               "      Bar{},\n"
+               "      [] {\n"
+               "    auto g = h();\n"
+               "    return g;\n"
+               "  },\n"
+               "      bazzzzz,\n"
+               "      quuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuux);\n"
+               "}",
+               Style);
+  Style.BinPackArguments = true;
   Style.BreakBeforeBraces = FormatStyle::BS_Custom;
   Style.BraceWrapping.BeforeLambdaBody = true;
   verifyFormat("void foo() {\n"
                "  aFunction(\n"
-               "      1, b(c(foo, Bar{}, baz,\n"
-               "             [](d) -> Foo\n"
+               "      1, b(c(foo, Bar{}, baz, [](d) -> Foo\n"
                "  {\n"
                "    auto f = e(\n"
                "        [&]\n"
                "    {\n"
                "      auto g = h();\n"
                "      return g;\n"
-               "    },\n"
-               "        qux,\n"
-               "        [&] -> Bar\n"
+               "    }, qux, [&] -> Bar\n"
                "    {\n"
                "      auto i = j();\n"
                "      return i;\n"
Index: clang/lib/Format/TokenAnnotator.cpp
===================================================================
--- clang/lib/Format/TokenAnnotator.cpp
+++ clang/lib/Format/TokenAnnotator.cpp
@@ -5127,7 +5127,8 @@
   // Deal with lambda arguments in C++ - we want consistent line breaks whether
   // they happen to be at arg0, arg1 or argN. The selection is a bit nuanced
   // as aggressive line breaks are placed when the lambda is not the last arg.
-  if ((Style.Language == FormatStyle::LK_Cpp ||
+  if (Style.LambdaBodyIndentation == FormatStyle::LBI_Signature &&
+      (Style.Language == FormatStyle::LK_Cpp ||
        Style.Language == FormatStyle::LK_ObjC) &&
       Left.is(tok::l_paren) && Left.BlockParameterCount > 0 &&
       !Right.isOneOf(tok::l_paren, TT_LambdaLSquare)) {
Index: clang/lib/Format/ContinuationIndenter.cpp
===================================================================
--- clang/lib/Format/ContinuationIndenter.cpp
+++ clang/lib/Format/ContinuationIndenter.cpp
@@ -309,10 +309,11 @@
     return false;
 
   // Don't create a 'hanging' indent if there are multiple blocks in a single
-  // statement.
+  // statement and we are aligning lambda blocks to their signatures.
   if (Previous.is(tok::l_brace) && State.Stack.size() > 1 &&
       State.Stack[State.Stack.size() - 2].NestedBlockInlined &&
-      State.Stack[State.Stack.size() - 2].HasMultipleNestedBlocks) {
+      State.Stack[State.Stack.size() - 2].HasMultipleNestedBlocks &&
+      Style.LambdaBodyIndentation == FormatStyle::LBI_Signature) {
     return false;
   }
 
@@ -326,6 +327,10 @@
   // If binary operators are moved to the next line (including commas for some
   // styles of constructor initializers), that's always ok.
   if (!Current.isOneOf(TT_BinaryOperator, tok::comma) &&
+      // Allow breaking opening brace of lambdas (when passed as function
+      // arguments) to a new line when BeforeLambdaBody brace wrapping is
+      // enabled.
+      !(Current.is(TT_LambdaLBrace) && Style.BraceWrapping.BeforeLambdaBody) &&
       CurrentState.NoLineBreakInOperand) {
     return false;
   }
@@ -1022,9 +1027,41 @@
       NestedBlockSpecialCase ||
       (Current.MatchingParen &&
        Current.MatchingParen->is(TT_RequiresExpressionLBrace));
-  if (!NestedBlockSpecialCase)
-    for (ParenState &PState : llvm::drop_end(State.Stack))
-      PState.BreakBeforeParameter = true;
+  if (!NestedBlockSpecialCase) {
+    auto ParentLevelIt = State.Stack.rbegin() + 1;
+    if (Style.LambdaBodyIndentation == FormatStyle::LBI_OuterScope &&
+        Current.MatchingParen && Current.MatchingParen->is(TT_LambdaLBrace)) {
+      // If the first character on the new line is a lambda's closing brace, the
+      // stack still contains that lambda's parenthesis. As such, we need to
+      // recurse further down the stack than usual to find the parenthesis level
+      // containing the lambda, which is where we want to set
+      // BreakBeforeParameter.
+      //
+      // We specifically special case "OuterScope"-formatted lambdas here
+      // because, when using that setting, breaking before the parameter
+      // directly following the lambda is particularly unsightly. However, when
+      // "OuterScope" is not set, the logic to find the parent parenthesis level
+      // still appears to be sometimes incorrect. It has not been fixed yet
+      // because it would lead to significant changes in existing behaviour.
+      //
+      // TODO: fix the non-"OuterScope" case too.
+      auto FindCurrentLevel = [&](const auto &It) {
+        return std::find_if(It, State.Stack.rend(), [](const auto &PState) {
+          return PState.Tok != nullptr; // Ignore fake parens.
+        });
+      };
+      auto MaybeIncrement = [&](const auto &It) {
+        return It != State.Stack.rend() ? (It + 1) : It;
+      };
+      auto LambdaLevelIt = FindCurrentLevel(State.Stack.rbegin());
+      auto LevelContainingLambdaIt =
+          FindCurrentLevel(MaybeIncrement(LambdaLevelIt));
+      ParentLevelIt = MaybeIncrement(LevelContainingLambdaIt);
+    }
+    if (ParentLevelIt != State.Stack.rend())
+      for (auto It = ParentLevelIt; It != State.Stack.rend(); ++It)
+        It->BreakBeforeParameter = true;
+  }
 
   if (PreviousNonComment &&
       !PreviousNonComment->isOneOf(tok::comma, tok::colon, tok::semi) &&
@@ -1034,7 +1071,10 @@
       !PreviousNonComment->isOneOf(
           TT_BinaryOperator, TT_FunctionAnnotationRParen, TT_JavaAnnotation,
           TT_LeadingJavaAnnotation) &&
-      Current.isNot(TT_BinaryOperator) && !PreviousNonComment->opensScope()) {
+      Current.isNot(TT_BinaryOperator) && !PreviousNonComment->opensScope() &&
+      // We don't want to enforce line breaks for subsequent arguments just
+      // because we have been forced to break before a lambda body.
+      !(Style.BraceWrapping.BeforeLambdaBody && Current.is(TT_LambdaLBrace))) {
     CurrentState.BreakBeforeParameter = true;
   }
 
@@ -1053,7 +1093,7 @@
 
   if (CurrentState.AvoidBinPacking) {
     // If we are breaking after '(', '{', '<', or this is the break after a ':'
-    // to start a member initializater list in a constructor, this should not
+    // to start a member initializer list in a constructor, this should not
     // be considered bin packing unless the relevant AllowAll option is false or
     // this is a dict/object literal.
     bool PreviousIsBreakingCtorInitializerColon =
Index: clang/docs/ReleaseNotes.rst
===================================================================
--- clang/docs/ReleaseNotes.rst
+++ clang/docs/ReleaseNotes.rst
@@ -446,6 +446,8 @@
 - Add additional Qualifier Ordering support for special cases such
   as templates, requires clauses, long qualified names.
 - Fix all known issues associated with ``LambdaBodyIndentation: OuterScope``.
+- Avoid unnecessarily aggressive line-breaking when using
+  ``LambdaBodyIndentation: OuterScope`` with argument bin-packing.
 
 libclang
 --------
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to