Fix 8262: allow unparenthesized parameter in async arrow-function (#8444)

* Allow unparenthesize of parameter in async arrow-function

* Add tests and baselines

* Address PR

* Address PR: refactor to use "parseSimpleArrowFunctionExpression"

* Address PR: add comment

* Address PR
This commit is contained in:
Yui 2016-05-04 17:00:28 -07:00
parent e9122a9f34
commit 7521891760
5 changed files with 107 additions and 7 deletions

View File

@ -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 = <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;
@ -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(<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());

View File

@ -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); });

View File

@ -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))

View File

@ -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

View File

@ -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);