Skip to content

Commit 7521891

Browse files
committed
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
1 parent e9122a9 commit 7521891

File tree

5 files changed

+107
-7
lines changed

5 files changed

+107
-7
lines changed

src/compiler/parser.ts

Lines changed: 48 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2685,7 +2685,8 @@ namespace ts {
26852685
// 2) LeftHandSideExpression = AssignmentExpression[?in,?yield]
26862686
// 3) LeftHandSideExpression AssignmentOperator AssignmentExpression[?in,?yield]
26872687
// 4) ArrowFunctionExpression[?in,?yield]
2688-
// 5) [+Yield] YieldExpression[?In]
2688+
// 5) AsyncArrowFunctionExpression[in,yield,await]
2689+
// 6) [+Yield] YieldExpression[?In]
26892690
//
26902691
// Note: for ease of implementation we treat productions '2' and '3' as the same thing.
26912692
// (i.e. they're both BinaryExpressions with an assignment operator in it).
@@ -2695,11 +2696,18 @@ namespace ts {
26952696
return parseYieldExpression();
26962697
}
26972698

2698-
// Then, check if we have an arrow function (production '4') that starts with a parenthesized
2699-
// parameter list. If we do, we must *not* recurse for productions 1, 2 or 3. An ArrowFunction is
2699+
// Then, check if we have an arrow function (production '4' and '5') that starts with a parenthesized
2700+
// parameter list or is an async arrow function.
2701+
// AsyncArrowFunctionExpression:
2702+
// 1) async[no LineTerminator here]AsyncArrowBindingIdentifier[?Yield][no LineTerminator here]=>AsyncConciseBody[?In]
2703+
// 2) CoverCallExpressionAndAsyncArrowHead[?Yield, ?Await][no LineTerminator here]=>AsyncConciseBody[?In]
2704+
// Production (1) of AsyncArrowFunctionExpression is parsed in "tryParseAsyncSimpleArrowFunctionExpression".
2705+
// And production (2) is parsed in "tryParseParenthesizedArrowFunctionExpression".
2706+
//
2707+
// If we do successfully parse arrow-function, we must *not* recurse for productions 1, 2 or 3. An ArrowFunction is
27002708
// not a LeftHandSideExpression, nor does it start a ConditionalExpression. So we are done
27012709
// with AssignmentExpression if we see one.
2702-
const arrowExpression = tryParseParenthesizedArrowFunctionExpression();
2710+
const arrowExpression = tryParseParenthesizedArrowFunctionExpression() || tryParseAsyncSimpleArrowFunctionExpression();
27032711
if (arrowExpression) {
27042712
return arrowExpression;
27052713
}
@@ -2791,10 +2799,17 @@ namespace ts {
27912799
}
27922800
}
27932801

2794-
function parseSimpleArrowFunctionExpression(identifier: Identifier): Expression {
2802+
function parseSimpleArrowFunctionExpression(identifier: Identifier, asyncModifier?: ModifiersArray): ArrowFunction {
27952803
Debug.assert(token === SyntaxKind.EqualsGreaterThanToken, "parseSimpleArrowFunctionExpression should only have been called if we had a =>");
27962804

2797-
const node = <ArrowFunction>createNode(SyntaxKind.ArrowFunction, identifier.pos);
2805+
let node: ArrowFunction;
2806+
if (asyncModifier) {
2807+
node = <ArrowFunction>createNode(SyntaxKind.ArrowFunction, asyncModifier.pos);
2808+
setModifiers(node, asyncModifier);
2809+
}
2810+
else {
2811+
node = <ArrowFunction>createNode(SyntaxKind.ArrowFunction, identifier.pos);
2812+
}
27982813

27992814
const parameter = <ParameterDeclaration>createNode(SyntaxKind.Parameter, identifier.pos);
28002815
parameter.name = identifier;
@@ -2805,7 +2820,7 @@ namespace ts {
28052820
node.parameters.end = parameter.end;
28062821

28072822
node.equalsGreaterThanToken = parseExpectedToken(SyntaxKind.EqualsGreaterThanToken, /*reportAtCurrentPosition*/ false, Diagnostics._0_expected, "=>");
2808-
node.body = parseArrowFunctionExpressionBody(/*isAsync*/ false);
2823+
node.body = parseArrowFunctionExpressionBody(/*isAsync*/ !!asyncModifier);
28092824

28102825
return finishNode(node);
28112826
}
@@ -2973,6 +2988,32 @@ namespace ts {
29732988
return parseParenthesizedArrowFunctionExpressionHead(/*allowAmbiguity*/ false);
29742989
}
29752990

2991+
function tryParseAsyncSimpleArrowFunctionExpression(): ArrowFunction {
2992+
const isUnParenthesizedAsyncArrowFunction = lookAhead(isUnParenthesizedAsyncArrowFunctionWorker);
2993+
if (isUnParenthesizedAsyncArrowFunction === Tristate.True) {
2994+
const asyncModifier = parseModifiersForArrowFunction();
2995+
const expr = parseBinaryExpressionOrHigher(/*precedence*/ 0);
2996+
return parseSimpleArrowFunctionExpression(<Identifier>expr, asyncModifier);
2997+
}
2998+
return undefined;
2999+
}
3000+
3001+
function isUnParenthesizedAsyncArrowFunctionWorker(): Tristate {
3002+
if (token === SyntaxKind.AsyncKeyword) {
3003+
nextToken();
3004+
if (scanner.hasPrecedingLineBreak()) {
3005+
return Tristate.False;
3006+
}
3007+
// Check for un-parenthesized AsyncArrowFunction
3008+
const expr = parseBinaryExpressionOrHigher(/*precedence*/ 0);
3009+
if (!scanner.hasPrecedingLineBreak() && expr.kind === SyntaxKind.Identifier && token === SyntaxKind.EqualsGreaterThanToken) {
3010+
return Tristate.True;
3011+
}
3012+
}
3013+
3014+
return Tristate.False;
3015+
}
3016+
29763017
function parseParenthesizedArrowFunctionExpressionHead(allowAmbiguity: boolean): ArrowFunction {
29773018
const node = <ArrowFunction>createNode(SyntaxKind.ArrowFunction);
29783019
setModifiers(node, parseModifiersForArrowFunction());
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
//// [asyncUnParenthesizedArrowFunction_es6.ts]
2+
3+
declare function someOtherFunction(i: any): Promise<void>;
4+
const x = async i => await someOtherFunction(i)
5+
const x1 = async (i) => await someOtherFunction(i);
6+
7+
//// [asyncUnParenthesizedArrowFunction_es6.js]
8+
const x = (i) => __awaiter(this, void 0, void 0, function* () { return yield someOtherFunction(i); });
9+
const x1 = (i) => __awaiter(this, void 0, void 0, function* () { return yield someOtherFunction(i); });
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
=== tests/cases/conformance/async/es6/asyncArrowFunction/asyncUnParenthesizedArrowFunction_es6.ts ===
2+
3+
declare function someOtherFunction(i: any): Promise<void>;
4+
>someOtherFunction : Symbol(someOtherFunction, Decl(asyncUnParenthesizedArrowFunction_es6.ts, 0, 0))
5+
>i : Symbol(i, Decl(asyncUnParenthesizedArrowFunction_es6.ts, 1, 35))
6+
>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, --, --))
7+
8+
const x = async i => await someOtherFunction(i)
9+
>x : Symbol(x, Decl(asyncUnParenthesizedArrowFunction_es6.ts, 2, 5))
10+
>i : Symbol(i, Decl(asyncUnParenthesizedArrowFunction_es6.ts, 2, 15))
11+
>someOtherFunction : Symbol(someOtherFunction, Decl(asyncUnParenthesizedArrowFunction_es6.ts, 0, 0))
12+
>i : Symbol(i, Decl(asyncUnParenthesizedArrowFunction_es6.ts, 2, 15))
13+
14+
const x1 = async (i) => await someOtherFunction(i);
15+
>x1 : Symbol(x1, Decl(asyncUnParenthesizedArrowFunction_es6.ts, 3, 5))
16+
>i : Symbol(i, Decl(asyncUnParenthesizedArrowFunction_es6.ts, 3, 18))
17+
>someOtherFunction : Symbol(someOtherFunction, Decl(asyncUnParenthesizedArrowFunction_es6.ts, 0, 0))
18+
>i : Symbol(i, Decl(asyncUnParenthesizedArrowFunction_es6.ts, 3, 18))
19+
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
=== tests/cases/conformance/async/es6/asyncArrowFunction/asyncUnParenthesizedArrowFunction_es6.ts ===
2+
3+
declare function someOtherFunction(i: any): Promise<void>;
4+
>someOtherFunction : (i: any) => Promise<void>
5+
>i : any
6+
>Promise : Promise<T>
7+
8+
const x = async i => await someOtherFunction(i)
9+
>x : (i: any) => Promise<void>
10+
>async i => await someOtherFunction(i) : (i: any) => Promise<void>
11+
>i : any
12+
>await someOtherFunction(i) : void
13+
>someOtherFunction(i) : Promise<void>
14+
>someOtherFunction : (i: any) => Promise<void>
15+
>i : any
16+
17+
const x1 = async (i) => await someOtherFunction(i);
18+
>x1 : (i: any) => Promise<void>
19+
>async (i) => await someOtherFunction(i) : (i: any) => Promise<void>
20+
>i : any
21+
>await someOtherFunction(i) : void
22+
>someOtherFunction(i) : Promise<void>
23+
>someOtherFunction : (i: any) => Promise<void>
24+
>i : any
25+
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// @target: ES6
2+
// @noEmitHelpers: true
3+
4+
declare function someOtherFunction(i: any): Promise<void>;
5+
const x = async i => await someOtherFunction(i)
6+
const x1 = async (i) => await someOtherFunction(i);

0 commit comments

Comments
 (0)