Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 48 additions & 7 deletions src/compiler/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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).
Expand All @@ -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;
}
Expand Down Expand Up @@ -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 = <ArrowFunction>createNode(SyntaxKind.ArrowFunction, identifier.pos);
let node: ArrowFunction;
if (asyncModifier) {
node = <ArrowFunction>createNode(SyntaxKind.ArrowFunction, asyncModifier.pos);
setModifiers(node, asyncModifier);
}
else {
node = <ArrowFunction>createNode(SyntaxKind.ArrowFunction, identifier.pos);
}

const parameter = <ParameterDeclaration>createNode(SyntaxKind.Parameter, identifier.pos);
parameter.name = identifier;
Expand All @@ -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);
}
Expand Down Expand Up @@ -2973,6 +2988,32 @@ namespace ts {
return parseParenthesizedArrowFunctionExpressionHead(/*allowAmbiguity*/ false);
}

function tryParseAsyncSimpleArrowFunctionExpression(): ArrowFunction {
const isUnParenthesizedAsyncArrowFunction = lookAhead(isUnParenthesizedAsyncArrowFunctionWorker);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you check first that token === SyntaxKind.AsyncKeyword before calling the function?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure. I just realize I should do that since lookAhead can be expensive

if (isUnParenthesizedAsyncArrowFunction === Tristate.True) {
const asyncModifier = parseModifiersForArrowFunction();
const expr = parseBinaryExpressionOrHigher(/*precedence*/ 0);
return parseSimpleArrowFunctionExpression(<Identifier>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 = <ArrowFunction>createNode(SyntaxKind.ArrowFunction);
setModifiers(node, parseModifiersForArrowFunction());
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
//// [asyncUnParenthesizedArrowFunction_es6.ts]

declare function someOtherFunction(i: any): Promise<void>;
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); });
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
=== tests/cases/conformance/async/es6/asyncArrowFunction/asyncUnParenthesizedArrowFunction_es6.ts ===

declare function someOtherFunction(i: any): Promise<void>;
>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))

Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
=== tests/cases/conformance/async/es6/asyncArrowFunction/asyncUnParenthesizedArrowFunction_es6.ts ===

declare function someOtherFunction(i: any): Promise<void>;
>someOtherFunction : (i: any) => Promise<void>
>i : any
>Promise : Promise<T>

const x = async i => await someOtherFunction(i)
>x : (i: any) => Promise<void>
>async i => await someOtherFunction(i) : (i: any) => Promise<void>
>i : any
>await someOtherFunction(i) : void
>someOtherFunction(i) : Promise<void>
>someOtherFunction : (i: any) => Promise<void>
>i : any

const x1 = async (i) => await someOtherFunction(i);
>x1 : (i: any) => Promise<void>
>async (i) => await someOtherFunction(i) : (i: any) => Promise<void>
>i : any
>await someOtherFunction(i) : void
>someOtherFunction(i) : Promise<void>
>someOtherFunction : (i: any) => Promise<void>
>i : any

Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// @target: ES6
// @noEmitHelpers: true

declare function someOtherFunction(i: any): Promise<void>;
const x = async i => await someOtherFunction(i)
const x1 = async (i) => await someOtherFunction(i);