diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 89e1404e43a..8d3eacc3dfe 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -2685,7 +2685,8 @@ namespace ts { // 2) LeftHandSideExpression = AssignmentExpression[?in,?yield] // 3) LeftHandSideExpression AssignmentOperator AssignmentExpression[?in,?yield] // 4) ArrowFunctionExpression[?in,?yield] - // 5) [+Yield] YieldExpression[?In] + // 5) AsyncArrowFunctionExpression[in,yield,await] + // 6) [+Yield] YieldExpression[?In] // // Note: for ease of implementation we treat productions '2' and '3' as the same thing. // (i.e. they're both BinaryExpressions with an assignment operator in it). @@ -2695,11 +2696,18 @@ namespace ts { return parseYieldExpression(); } - // Then, check if we have an arrow function (production '4') that starts with a parenthesized - // parameter list. If we do, we must *not* recurse for productions 1, 2 or 3. An ArrowFunction is + // Then, check if we have an arrow function (production '4' and '5') that starts with a parenthesized + // parameter list or is an async arrow function. + // AsyncArrowFunctionExpression: + // 1) async[no LineTerminator here]AsyncArrowBindingIdentifier[?Yield][no LineTerminator here]=>AsyncConciseBody[?In] + // 2) CoverCallExpressionAndAsyncArrowHead[?Yield, ?Await][no LineTerminator here]=>AsyncConciseBody[?In] + // Production (1) of AsyncArrowFunctionExpression is parsed in "tryParseAsyncSimpleArrowFunctionExpression". + // And production (2) is parsed in "tryParseParenthesizedArrowFunctionExpression". + // + // If we do successfully parse arrow-function, we must *not* recurse for productions 1, 2 or 3. An ArrowFunction is // not a LeftHandSideExpression, nor does it start a ConditionalExpression. So we are done // with AssignmentExpression if we see one. - const arrowExpression = tryParseParenthesizedArrowFunctionExpression(); + const arrowExpression = tryParseParenthesizedArrowFunctionExpression() || tryParseAsyncSimpleArrowFunctionExpression(); if (arrowExpression) { return arrowExpression; } @@ -2791,10 +2799,17 @@ namespace ts { } } - function parseSimpleArrowFunctionExpression(identifier: Identifier): Expression { + function parseSimpleArrowFunctionExpression(identifier: Identifier, asyncModifier?: ModifiersArray): ArrowFunction { Debug.assert(token === SyntaxKind.EqualsGreaterThanToken, "parseSimpleArrowFunctionExpression should only have been called if we had a =>"); - const node = createNode(SyntaxKind.ArrowFunction, identifier.pos); + let node: ArrowFunction; + if (asyncModifier) { + node = createNode(SyntaxKind.ArrowFunction, asyncModifier.pos); + setModifiers(node, asyncModifier); + } + else { + node = createNode(SyntaxKind.ArrowFunction, identifier.pos); + } const parameter = createNode(SyntaxKind.Parameter, identifier.pos); parameter.name = identifier; @@ -2805,7 +2820,7 @@ namespace ts { node.parameters.end = parameter.end; node.equalsGreaterThanToken = parseExpectedToken(SyntaxKind.EqualsGreaterThanToken, /*reportAtCurrentPosition*/ false, Diagnostics._0_expected, "=>"); - node.body = parseArrowFunctionExpressionBody(/*isAsync*/ false); + node.body = parseArrowFunctionExpressionBody(/*isAsync*/ !!asyncModifier); return finishNode(node); } @@ -2973,6 +2988,32 @@ namespace ts { return parseParenthesizedArrowFunctionExpressionHead(/*allowAmbiguity*/ false); } + function tryParseAsyncSimpleArrowFunctionExpression(): ArrowFunction { + const isUnParenthesizedAsyncArrowFunction = lookAhead(isUnParenthesizedAsyncArrowFunctionWorker); + if (isUnParenthesizedAsyncArrowFunction === Tristate.True) { + const asyncModifier = parseModifiersForArrowFunction(); + const expr = parseBinaryExpressionOrHigher(/*precedence*/ 0); + return parseSimpleArrowFunctionExpression(expr, asyncModifier); + } + return undefined; + } + + function isUnParenthesizedAsyncArrowFunctionWorker(): Tristate { + if (token === SyntaxKind.AsyncKeyword) { + nextToken(); + if (scanner.hasPrecedingLineBreak()) { + return Tristate.False; + } + // Check for un-parenthesized AsyncArrowFunction + const expr = parseBinaryExpressionOrHigher(/*precedence*/ 0); + if (!scanner.hasPrecedingLineBreak() && expr.kind === SyntaxKind.Identifier && token === SyntaxKind.EqualsGreaterThanToken) { + return Tristate.True; + } + } + + return Tristate.False; + } + function parseParenthesizedArrowFunctionExpressionHead(allowAmbiguity: boolean): ArrowFunction { const node = createNode(SyntaxKind.ArrowFunction); setModifiers(node, parseModifiersForArrowFunction()); diff --git a/tests/baselines/reference/asyncUnParenthesizedArrowFunction_es6.js b/tests/baselines/reference/asyncUnParenthesizedArrowFunction_es6.js new file mode 100644 index 00000000000..a4b097a8715 --- /dev/null +++ b/tests/baselines/reference/asyncUnParenthesizedArrowFunction_es6.js @@ -0,0 +1,9 @@ +//// [asyncUnParenthesizedArrowFunction_es6.ts] + +declare function someOtherFunction(i: any): Promise; +const x = async i => await someOtherFunction(i) +const x1 = async (i) => await someOtherFunction(i); + +//// [asyncUnParenthesizedArrowFunction_es6.js] +const x = (i) => __awaiter(this, void 0, void 0, function* () { return yield someOtherFunction(i); }); +const x1 = (i) => __awaiter(this, void 0, void 0, function* () { return yield someOtherFunction(i); }); diff --git a/tests/baselines/reference/asyncUnParenthesizedArrowFunction_es6.symbols b/tests/baselines/reference/asyncUnParenthesizedArrowFunction_es6.symbols new file mode 100644 index 00000000000..018c2399258 --- /dev/null +++ b/tests/baselines/reference/asyncUnParenthesizedArrowFunction_es6.symbols @@ -0,0 +1,19 @@ +=== tests/cases/conformance/async/es6/asyncArrowFunction/asyncUnParenthesizedArrowFunction_es6.ts === + +declare function someOtherFunction(i: any): Promise; +>someOtherFunction : Symbol(someOtherFunction, Decl(asyncUnParenthesizedArrowFunction_es6.ts, 0, 0)) +>i : Symbol(i, Decl(asyncUnParenthesizedArrowFunction_es6.ts, 1, 35)) +>Promise : Symbol(Promise, Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --)) + +const x = async i => await someOtherFunction(i) +>x : Symbol(x, Decl(asyncUnParenthesizedArrowFunction_es6.ts, 2, 5)) +>i : Symbol(i, Decl(asyncUnParenthesizedArrowFunction_es6.ts, 2, 15)) +>someOtherFunction : Symbol(someOtherFunction, Decl(asyncUnParenthesizedArrowFunction_es6.ts, 0, 0)) +>i : Symbol(i, Decl(asyncUnParenthesizedArrowFunction_es6.ts, 2, 15)) + +const x1 = async (i) => await someOtherFunction(i); +>x1 : Symbol(x1, Decl(asyncUnParenthesizedArrowFunction_es6.ts, 3, 5)) +>i : Symbol(i, Decl(asyncUnParenthesizedArrowFunction_es6.ts, 3, 18)) +>someOtherFunction : Symbol(someOtherFunction, Decl(asyncUnParenthesizedArrowFunction_es6.ts, 0, 0)) +>i : Symbol(i, Decl(asyncUnParenthesizedArrowFunction_es6.ts, 3, 18)) + diff --git a/tests/baselines/reference/asyncUnParenthesizedArrowFunction_es6.types b/tests/baselines/reference/asyncUnParenthesizedArrowFunction_es6.types new file mode 100644 index 00000000000..d2cc97ce6c0 --- /dev/null +++ b/tests/baselines/reference/asyncUnParenthesizedArrowFunction_es6.types @@ -0,0 +1,25 @@ +=== tests/cases/conformance/async/es6/asyncArrowFunction/asyncUnParenthesizedArrowFunction_es6.ts === + +declare function someOtherFunction(i: any): Promise; +>someOtherFunction : (i: any) => Promise +>i : any +>Promise : Promise + +const x = async i => await someOtherFunction(i) +>x : (i: any) => Promise +>async i => await someOtherFunction(i) : (i: any) => Promise +>i : any +>await someOtherFunction(i) : void +>someOtherFunction(i) : Promise +>someOtherFunction : (i: any) => Promise +>i : any + +const x1 = async (i) => await someOtherFunction(i); +>x1 : (i: any) => Promise +>async (i) => await someOtherFunction(i) : (i: any) => Promise +>i : any +>await someOtherFunction(i) : void +>someOtherFunction(i) : Promise +>someOtherFunction : (i: any) => Promise +>i : any + diff --git a/tests/cases/conformance/async/es6/asyncArrowFunction/asyncUnParenthesizedArrowFunction_es6.ts b/tests/cases/conformance/async/es6/asyncArrowFunction/asyncUnParenthesizedArrowFunction_es6.ts new file mode 100644 index 00000000000..6a5086f70d0 --- /dev/null +++ b/tests/cases/conformance/async/es6/asyncArrowFunction/asyncUnParenthesizedArrowFunction_es6.ts @@ -0,0 +1,6 @@ +// @target: ES6 +// @noEmitHelpers: true + +declare function someOtherFunction(i: any): Promise; +const x = async i => await someOtherFunction(i) +const x1 = async (i) => await someOtherFunction(i); \ No newline at end of file