strager created this revision.
Herald added a subscriber: klimek.

clang-format completely ruins the formatting of block literal
expressions which appear inside inside braced initializer lists. For
example:

      int main() {
        foo({
          ^() {
            return nil;
      }
      });
      }

Teach clang-format to parse blocks inside braced lists:

  int main() {
    foo({^() {

return nil;

    }});
  }


https://reviews.llvm.org/D40221

Files:
  lib/Format/UnwrappedLineParser.cpp
  lib/Format/UnwrappedLineParser.h
  unittests/Format/FormatTest.cpp

Index: unittests/Format/FormatTest.cpp
===================================================================
--- unittests/Format/FormatTest.cpp
+++ unittests/Format/FormatTest.cpp
@@ -11200,6 +11200,85 @@
   verifyFormat("BOOL (^aaa)(void) = ^BOOL {\n"
                "};");
 
+  verifyFormat("foo({^{\n"
+               "  return nil;\n"
+               "}});\n");
+  verifyFormat("foo({^() {\n"
+               "  return nil;\n"
+               "}});\n");
+  verifyFormat("foo({^id {\n"
+               "  return nil;\n"
+               "}});\n");
+  verifyFormat("foo({^id() {\n"
+               "  return nil;\n"
+               "}});\n");
+
+  verifyFormat("foo({.cb = ^{\n"
+               "  return nil;\n"
+               "}});\n");
+  verifyFormat("foo({.cb = ^() {\n"
+               "  return nil;\n"
+               "}});\n");
+  verifyFormat("foo({.cb = ^id {\n"
+               "  return nil;\n"
+               "}});\n");
+  verifyFormat("foo({.cb = ^id() {\n"
+               "  return nil;\n"
+               "}});\n");
+
+  verifyFormat("foo(\n"
+               "    ^{\n"
+               "      return nil;\n"
+               "    },\n"
+               "    ^() {\n"
+               "      return [NSArray new];\n"
+               "    });\n");
+  verifyFormat("foo(\n"
+               "    ^id {\n"
+               "      return nil;\n"
+               "    },\n"
+               "    ^id() {\n"
+               "      return [NSArray new];\n"
+               "    });\n");
+
+  verifyFormat("callbacks cbs = {\n"
+               "    ^{\n"
+               "      return nil;\n"
+               "    },\n"
+               "    ^id {\n"
+               "      return [NSArray new];\n"
+               "    },\n"
+               "};");
+  verifyFormat("callbacks cbs = {\n"
+               "    ^() {\n"
+               "      return nil;\n"
+               "    },\n"
+               "    ^id() {\n"
+               "      return [NSArray new];\n"
+               "    },\n"
+               "};");
+
+  verifyFormat("callbacks cbs = {\n"
+               "    .cb1 =\n"
+               "        ^{\n"
+               "          return nil;\n"
+               "        },\n"
+               "    .cb2 =\n"
+               "        ^id {\n"
+               "          return [NSArray new];\n"
+               "        },\n"
+               "};");
+  verifyFormat("callbacks cbs = {\n"
+               "    .cb1 =\n"
+               "        ^() {\n"
+               "          return nil;\n"
+               "        },\n"
+               "    .cb2 =\n"
+               "        ^id() {\n"
+               "          return nil;\n"
+               "        },\n"
+               "};");
+
   FormatStyle FourIndent = getLLVMStyle();
   FourIndent.ObjCBlockIndentWidth = 4;
   verifyFormat("[operation setCompletionBlock:^{\n"
Index: lib/Format/UnwrappedLineParser.h
===================================================================
--- lib/Format/UnwrappedLineParser.h
+++ lib/Format/UnwrappedLineParser.h
@@ -121,6 +121,7 @@
   void parseObjCInterfaceOrImplementation();
   void parseObjCProtocol();
   void parseJavaScriptEs6ImportExport();
+  bool tryToParseBlock();
   bool tryToParseLambda();
   bool tryToParseLambdaIntroducer();
   void tryToParseJSFunction();
Index: lib/Format/UnwrappedLineParser.cpp
===================================================================
--- lib/Format/UnwrappedLineParser.cpp
+++ lib/Format/UnwrappedLineParser.cpp
@@ -1177,14 +1177,7 @@
         nextToken();
       break;
     case tok::caret:
-      nextToken();
-      if (FormatTok->Tok.isAnyIdentifier() ||
-          FormatTok->isSimpleTypeSpecifier())
-        nextToken();
-      if (FormatTok->is(tok::l_paren))
-        parseParens();
-      if (FormatTok->is(tok::l_brace))
-        parseChildBlock();
+      tryToParseBlock();
       break;
     case tok::l_brace:
       if (!tryToParseBracedList()) {
@@ -1311,6 +1304,40 @@
   } while (!eof());
 }
 
+// Parses a block literal expression starting at the initial ^ token.
+//
+// Blocks literal expressions have several forms including the following:
+//
+// \code
+// ^{
+//   return 42;
+// }
+//
+// ^BOOL(int x) {
+//   return x > 0;
+// }
+// \endcode
+bool UnwrappedLineParser::tryToParseBlock() {
+  // Consume the leading ^.
+  assert(FormatTok->is(tok::caret));
+  nextToken();
+  if (!Style.isCpp()) {
+    // Blocks are only supported in C++ and Objective-C.
+    return false;
+  }
+  // Consume the optional return type.
+  if (FormatTok->Tok.isAnyIdentifier() || FormatTok->isSimpleTypeSpecifier())
+    nextToken();
+  // Consume the optional argument list.
+  if (FormatTok->is(tok::l_paren))
+    parseParens();
+  // Consume the required body.
+  if (!FormatTok->is(tok::l_brace))
+    return false;
+  parseChildBlock();
+  return true;
+}
+
 bool UnwrappedLineParser::tryToParseLambda() {
   if (!Style.isCpp()) {
     nextToken();
@@ -1461,10 +1488,7 @@
     }
     switch (FormatTok->Tok.getKind()) {
     case tok::caret:
-      nextToken();
-      if (FormatTok->is(tok::l_brace)) {
-        parseChildBlock();
-      }
+      tryToParseBlock();
       break;
     case tok::l_square:
       tryToParseLambda();
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to