Index: cfe/trunk/lib/Format/ContinuationIndenter.cpp =================================================================== --- cfe/trunk/lib/Format/ContinuationIndenter.cpp +++ cfe/trunk/lib/Format/ContinuationIndenter.cpp @@ -479,7 +479,7 @@ // is common and should be formatted like a free-standing function. if (Style.Language != FormatStyle::LK_JavaScript || Current.NestingLevel != 0 || !PreviousNonComment->is(tok::equal) || - !Current.is(Keywords.kw_function)) + !Current.isOneOf(Keywords.kw_async, Keywords.kw_function)) State.Stack.back().NestedBlockIndent = State.Column; if (NextNonComment->isMemberAccess()) { Index: cfe/trunk/lib/Format/FormatToken.h =================================================================== --- cfe/trunk/lib/Format/FormatToken.h +++ cfe/trunk/lib/Format/FormatToken.h @@ -535,13 +535,16 @@ kw_NS_ENUM = &IdentTable.get("NS_ENUM"); kw_NS_OPTIONS = &IdentTable.get("NS_OPTIONS"); + kw_async = &IdentTable.get("async"); + kw_await = &IdentTable.get("await"); kw_finally = &IdentTable.get("finally"); - kw_function = &IdentTable.get("function"); kw_from = &IdentTable.get("from"); + kw_function = &IdentTable.get("function"); kw_import = &IdentTable.get("import"); kw_is = &IdentTable.get("is"); kw_let = &IdentTable.get("let"); kw_var = &IdentTable.get("var"); + kw_yield = &IdentTable.get("yield"); kw_abstract = &IdentTable.get("abstract"); kw_assert = &IdentTable.get("assert"); @@ -582,13 +585,16 @@ IdentifierInfo *kw___except; // JavaScript keywords. + IdentifierInfo *kw_async; + IdentifierInfo *kw_await; IdentifierInfo *kw_finally; - IdentifierInfo *kw_function; IdentifierInfo *kw_from; + IdentifierInfo *kw_function; IdentifierInfo *kw_import; IdentifierInfo *kw_is; IdentifierInfo *kw_let; IdentifierInfo *kw_var; + IdentifierInfo *kw_yield; // Java keywords. IdentifierInfo *kw_abstract; Index: cfe/trunk/lib/Format/TokenAnnotator.cpp =================================================================== --- cfe/trunk/lib/Format/TokenAnnotator.cpp +++ cfe/trunk/lib/Format/TokenAnnotator.cpp @@ -2078,6 +2078,9 @@ } else if (Style.Language == FormatStyle::LK_JavaScript) { if (Left.is(TT_JsFatArrow)) return true; + if (Right.is(tok::star) && + Left.isOneOf(Keywords.kw_function, Keywords.kw_yield)) + return false; if (Left.isOneOf(Keywords.kw_let, Keywords.kw_var, Keywords.kw_in, Keywords.kw_of) && (!Left.Previous || !Left.Previous->is(tok::period))) Index: cfe/trunk/lib/Format/UnwrappedLineParser.cpp =================================================================== --- cfe/trunk/lib/Format/UnwrappedLineParser.cpp +++ cfe/trunk/lib/Format/UnwrappedLineParser.cpp @@ -668,7 +668,8 @@ // FIXME: This returns true for C/C++ keywords like 'struct'. return FormatTok->is(tok::identifier) && (FormatTok->Tok.getIdentifierInfo() == nullptr || - !FormatTok->isOneOf(Keywords.kw_in, Keywords.kw_of, + !FormatTok->isOneOf(Keywords.kw_in, Keywords.kw_of, Keywords.kw_async, + Keywords.kw_await, Keywords.kw_yield, Keywords.kw_finally, Keywords.kw_function, Keywords.kw_import, Keywords.kw_is, Keywords.kw_let, Keywords.kw_var, @@ -687,7 +688,7 @@ static bool isJSDeclOrStmt(const AdditionalKeywords &Keywords, const FormatToken *FormatTok) { return FormatTok->isOneOf( - tok::kw_return, + tok::kw_return, Keywords.kw_yield, // conditionals tok::kw_if, tok::kw_else, // loops @@ -698,7 +699,9 @@ tok::kw_throw, tok::kw_try, tok::kw_catch, Keywords.kw_finally, // declaration tok::kw_const, tok::kw_class, Keywords.kw_var, Keywords.kw_let, - Keywords.kw_function); + Keywords.kw_async, Keywords.kw_function, + // import/export + Keywords.kw_import, tok::kw_export); } // readTokenWithJavaScriptASI reads the next token and terminates the current @@ -1003,7 +1006,8 @@ // Parse function literal unless 'function' is the first token in a line // in which case this should be treated as a free-standing function. if (Style.Language == FormatStyle::LK_JavaScript && - FormatTok->is(Keywords.kw_function) && Line->Tokens.size() > 0) { + FormatTok->isOneOf(Keywords.kw_async, Keywords.kw_function) && + Line->Tokens.size() > 0) { tryToParseJSFunction(); break; } @@ -1189,8 +1193,16 @@ } void UnwrappedLineParser::tryToParseJSFunction() { + assert(FormatTok->isOneOf(Keywords.kw_async, Keywords.kw_function)); + if (FormatTok->is(Keywords.kw_async)) + nextToken(); + // Consume "function". nextToken(); + // Consume * (generator function). + if (FormatTok->is(tok::star)) + nextToken(); + // Consume function name. if (FormatTok->is(tok::identifier)) nextToken(); @@ -1235,7 +1247,7 @@ // replace this by using parseAssigmentExpression() inside. do { if (Style.Language == FormatStyle::LK_JavaScript) { - if (FormatTok->is(Keywords.kw_function)) { + if (FormatTok->isOneOf(Keywords.kw_async, Keywords.kw_function)) { tryToParseJSFunction(); continue; } @@ -1333,7 +1345,7 @@ break; case tok::identifier: if (Style.Language == FormatStyle::LK_JavaScript && - FormatTok->is(Keywords.kw_function)) + FormatTok->isOneOf(Keywords.kw_async, Keywords.kw_function)) tryToParseJSFunction(); else nextToken(); @@ -1904,8 +1916,11 @@ if (FormatTok->is(tok::kw_default)) nextToken(); - // Consume "function" and "default function", so that these get parsed as - // free-standing JS functions, i.e. do not require a trailing semicolon. + // Consume "async function", "function" and "default function", so that these + // get parsed as free-standing JS functions, i.e. do not require a trailing + // semicolon. + if (FormatTok->is(Keywords.kw_async)) + nextToken(); if (FormatTok->is(Keywords.kw_function)) { nextToken(); return; Index: cfe/trunk/unittests/Format/FormatTestJS.cpp =================================================================== --- cfe/trunk/unittests/Format/FormatTestJS.cpp +++ cfe/trunk/unittests/Format/FormatTestJS.cpp @@ -320,6 +320,40 @@ verifyFormat("function f() {}"); } +TEST_F(FormatTestJS, GeneratorFunctions) { + verifyFormat("function* f() {\n" + " let x = 1;\n" + " yield x;\n" + " yield* something();\n" + "}"); + verifyFormat("function*\n" + " f() {\n" + "}", + getGoogleJSStyleWithColumns(8)); + verifyFormat("export function* f() {\n" + " yield 1;\n" + "}\n"); + verifyFormat("class X {\n" + " * generatorMethod() { yield x; }\n" + "}"); +} + +TEST_F(FormatTestJS, AsyncFunctions) { + verifyFormat("async function f() {\n" + " let x = 1;\n" + " return fetch(x);\n" + "}"); + verifyFormat("async function* f() {\n" + " yield fetch(x);\n" + "}"); + verifyFormat("export async function f() {\n" + " return fetch(x);\n" + "}"); + verifyFormat("class X {\n" + " async asyncMethod() { return fetch(1); }\n" + "}"); +} + TEST_F(FormatTestJS, ArrayLiterals) { verifyFormat("var aaaaa: List =\n" " [new SomeThingAAAAAAAAAAAA(), new SomeThingBBBBBBBBB()];");