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(); 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"