]> granicus.if.org Git - clang/commitdiff
clang-format: remove trailing lines in lamdas and arrow functions.
authorMartin Probst <martin@probst.io>
Fri, 17 Nov 2017 18:06:33 +0000 (18:06 +0000)
committerMartin Probst <martin@probst.io>
Fri, 17 Nov 2017 18:06:33 +0000 (18:06 +0000)
Summary:
clang-format already removes empty lines at the beginning & end of
blocks:

    int x() {

      foo();  // lines before and after will be removed.

    }

However because lamdas and arrow functions are parsed as expressions,
the existing logic to remove empty lines in UnwrappedLineFormatter
doesn't handle them.

This change special cases arrow functions in ContinuationIndenter to
remove empty lines:

    x = []() {

      foo();  // lines before and after will now be removed.

    };

Reviewers: djasper

Subscribers: klimek, cfe-commits

Differential Revision: https://reviews.llvm.org/D40178

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@318537 91177308-0d34-0410-b5e6-96231b3b80d8

lib/Format/ContinuationIndenter.cpp
lib/Format/UnwrappedLineFormatter.cpp
unittests/Format/FormatTest.cpp
unittests/Format/FormatTestJS.cpp

index 3fed32f6f4c9d3fd874ddf953ac5f9e4aec49a13..44e6ad4232ea77aff00f2fc561564edc95bda5a3 100644 (file)
@@ -701,8 +701,18 @@ unsigned ContinuationIndenter::addTokenOnNewLine(LineState &State,
     State.Stack.back().BreakBeforeParameter = false;
 
   if (!DryRun) {
+    unsigned MaxEmptyLinesToKeep = Style.MaxEmptyLinesToKeep + 1;
+    if (Current.is(tok::r_brace) && Current.MatchingParen &&
+        // Only strip trailing empty lines for l_braces that have children, i.e.
+        // for function expressions (lambdas, arrows, etc).
+        !Current.MatchingParen->Children.empty()) {
+      // lambdas and arrow functions are expressions, thus their r_brace is not
+      // on its own line, and thus not covered by UnwrappedLineFormatter's logic
+      // about removing empty lines on closing blocks. Special case them here.
+      MaxEmptyLinesToKeep = 1;
+    }
     unsigned Newlines = std::max(
-        1u, std::min(Current.NewlinesBefore, Style.MaxEmptyLinesToKeep + 1));
+        1u, std::min(Current.NewlinesBefore, MaxEmptyLinesToKeep));
     bool ContinuePPDirective =
         State.Line->InPPDirective && State.Line->Type != LT_ImportStatement;
     Whitespaces.replaceWhitespace(Current, Newlines, State.Column, State.Column,
index a82cd5abe27a1da136c40c4aec6f5bda6b1a85a4..2e9d803a191cd11958b1a742a4fb7a2feeef9793 100644 (file)
@@ -1117,6 +1117,9 @@ void UnwrappedLineFormatter::formatFirstToken(const AnnotatedLine &Line,
       (!RootToken.Next ||
        (RootToken.Next->is(tok::semi) && !RootToken.Next->Next)))
     Newlines = std::min(Newlines, 1u);
+  // Remove empty lines at the start of nested blocks (lambdas/arrow functions)
+  if (PreviousLine == nullptr && Line.Level > 0)
+    Newlines = std::min(Newlines, 1u);
   if (Newlines == 0 && !RootToken.IsFirst)
     Newlines = 1;
   if (RootToken.IsFirst && !RootToken.HasUnescapedNewline)
index 7fbf01dc129b62320fbcfbc56ce6285c168fdeab..acff8d32320aa2dca0c46530d3e5a330164443ac 100644 (file)
@@ -70,18 +70,23 @@ protected:
     return getStyleWithColumns(getGoogleStyle(), ColumnLimit);
   }
 
-  void verifyFormat(llvm::StringRef Code,
+  void verifyFormat(llvm::StringRef Expected, llvm::StringRef Code,
                     const FormatStyle &Style = getLLVMStyle()) {
-    EXPECT_EQ(Code.str(), format(test::messUp(Code), Style));
+    EXPECT_EQ(Expected.str(), format(Code, Style));
     if (Style.Language == FormatStyle::LK_Cpp) {
       // Objective-C++ is a superset of C++, so everything checked for C++
       // needs to be checked for Objective-C++ as well.
       FormatStyle ObjCStyle = Style;
       ObjCStyle.Language = FormatStyle::LK_ObjC;
-      EXPECT_EQ(Code.str(), format(test::messUp(Code), ObjCStyle));
+      EXPECT_EQ(Expected.str(), format(test::messUp(Code), ObjCStyle));
     }
   }
 
+  void verifyFormat(llvm::StringRef Code,
+                    const FormatStyle &Style = getLLVMStyle()) {
+    verifyFormat(Code, test::messUp(Code), Style);
+  }
+
   void verifyIncompleteFormat(llvm::StringRef Code,
                               const FormatStyle &Style = getLLVMStyle()) {
     EXPECT_EQ(Code.str(),
@@ -11089,6 +11094,17 @@ TEST_F(FormatTest, FormatsLambdas) {
       "    });");
 }
 
+TEST_F(FormatTest, EmptyLinesInLambdas) {
+  verifyFormat("auto lambda = []() {\n"
+               "  x(); //\n"
+               "};",
+               "auto lambda = []() {\n"
+               "\n"
+               "  x(); //\n"
+               "\n"
+               "};");
+}
+
 TEST_F(FormatTest, FormatsBlocks) {
   FormatStyle ShortBlocks = getLLVMStyle();
   ShortBlocks.AllowShortBlocksOnASingleLine = true;
index 3df01663e9d7b2640978812bcfdd6cb6aad5a685..3e5abdc098ad16f86e572f04f44233678ecc67aa 100644 (file)
@@ -1606,6 +1606,17 @@ TEST_F(FormatTestJS, TypeInterfaceLineWrapping) {
       Style);
 }
 
+TEST_F(FormatTestJS, RemoveEmptyLinesInArrowFunctions) {
+  verifyFormat("x = () => {\n"
+               "  foo();\n"
+               "};\n",
+               "x = () => {\n"
+               "\n"
+               "  foo();\n"
+               "\n"
+               "};\n");
+}
+
 TEST_F(FormatTestJS, Modules) {
   verifyFormat("import SomeThing from 'some/module.js';");
   verifyFormat("import {X, Y} from 'some/module.js';");