Skip to content

Commit 4f3fb80

Browse files
committed
Parse, check, and downlevel emit for for-await-of
1 parent 4b5686a commit 4f3fb80

18 files changed

Lines changed: 101 additions & 37 deletions

Jakefile.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -301,8 +301,7 @@ var es2016LibrarySourceMap = es2016LibrarySource.map(function (source) {
301301

302302
var es2017LibrarySource = [
303303
"es2017.object.d.ts",
304-
"es2017.sharedmemory.d.ts",
305-
"es2017.asynciterable.d.ts"
304+
"es2017.sharedmemory.d.ts"
306305
];
307306

308307
var es2017LibrarySourceMap = es2017LibrarySource.map(function (source) {

src/compiler/binder.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -929,6 +929,9 @@ namespace ts {
929929
const postLoopLabel = createBranchLabel();
930930
addAntecedent(preLoopLabel, currentFlow);
931931
currentFlow = preLoopLabel;
932+
if (node.kind === SyntaxKind.ForOfStatement) {
933+
bind(node.awaitKeyword);
934+
}
932935
bind(node.expression);
933936
addAntecedent(postLoopLabel, currentFlow);
934937
bind(node.initializer);

src/compiler/checker.ts

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3155,7 +3155,8 @@ namespace ts {
31553155
// missing properties/signatures required to get its iteratedType (like
31563156
// [Symbol.iterator] or next). This may be because we accessed properties from anyType,
31573157
// or it may have led to an error inside getElementTypeOfIterable.
3158-
return checkRightHandSideOfForOf((<ForOfStatement>declaration.parent.parent).expression) || anyType;
3158+
const isForAwaitOf = (<ForOfStatement>declaration.parent.parent).awaitKeyword !== undefined;
3159+
return checkRightHandSideOfForOf((<ForOfStatement>declaration.parent.parent).expression, isForAwaitOf) || anyType;
31593160
}
31603161

31613162
if (isBindingPattern(declaration.parent)) {
@@ -8631,7 +8632,7 @@ namespace ts {
86318632
case SyntaxKind.ForInStatement:
86328633
return stringType;
86338634
case SyntaxKind.ForOfStatement:
8634-
return checkRightHandSideOfForOf((<ForOfStatement>parent).expression) || unknownType;
8635+
return checkRightHandSideOfForOf((<ForOfStatement>parent).expression, (<ForOfStatement>parent).awaitKeyword !== undefined) || unknownType;
86358636
case SyntaxKind.BinaryExpression:
86368637
return getAssignedTypeOfBinaryExpression(<BinaryExpression>parent);
86378638
case SyntaxKind.DeleteExpression:
@@ -8675,7 +8676,8 @@ namespace ts {
86758676
return stringType;
86768677
}
86778678
if (node.parent.parent.kind === SyntaxKind.ForOfStatement) {
8678-
return checkRightHandSideOfForOf((<ForOfStatement>node.parent.parent).expression) || unknownType;
8679+
const isForAwaitOf = (<ForOfStatement>node.parent.parent).awaitKeyword !== undefined;
8680+
return checkRightHandSideOfForOf((<ForOfStatement>node.parent.parent).expression, isForAwaitOf) || unknownType;
86798681
}
86808682
return unknownType;
86818683
}
@@ -16667,7 +16669,7 @@ namespace ts {
1666716669
}
1666816670
else {
1666916671
const varExpr = <Expression>node.initializer;
16670-
const iteratedType = checkRightHandSideOfForOf(node.expression);
16672+
const iteratedType = checkRightHandSideOfForOf(node.expression, node.awaitKeyword !== undefined);
1667116673

1667216674
// There may be a destructuring assignment on the left side
1667316675
if (varExpr.kind === SyntaxKind.ArrayLiteralExpression || varExpr.kind === SyntaxKind.ObjectLiteralExpression) {
@@ -16754,9 +16756,11 @@ namespace ts {
1675416756
}
1675516757
}
1675616758

16757-
function checkRightHandSideOfForOf(rhsExpression: Expression): Type {
16759+
function checkRightHandSideOfForOf(rhsExpression: Expression, isForAwaitOf: boolean): Type {
1675816760
const expressionType = checkNonNullExpression(rhsExpression);
16759-
return checkIteratedTypeOrElementType(expressionType, rhsExpression, /*allowStringInput*/ true);
16761+
return isForAwaitOf
16762+
? checkIteratedTypeOfIterableOrAsyncIterable(expressionType, rhsExpression)
16763+
: checkIteratedTypeOrElementType(expressionType, rhsExpression, /*allowStringInput*/ true);
1676016764
}
1676116765

1676216766
function checkIteratedTypeOrElementType(inputType: Type, errorNode: Node, allowStringInput: boolean): Type {
@@ -19327,7 +19331,7 @@ namespace ts {
1932719331
// for ( { a } of elems) {
1932819332
// }
1932919333
if (expr.parent.kind === SyntaxKind.ForOfStatement) {
19330-
const iteratedType = checkRightHandSideOfForOf((<ForOfStatement>expr.parent).expression);
19334+
const iteratedType = checkRightHandSideOfForOf((<ForOfStatement>expr.parent).expression, (<ForOfStatement>expr.parent).awaitKeyword !== undefined);
1933119335
return checkDestructuringAssignment(expr, iteratedType || unknownType);
1933219336
}
1933319337
// If this is from "for" initializer
@@ -20794,6 +20798,12 @@ namespace ts {
2079420798
return true;
2079520799
}
2079620800

20801+
if (forInOrOfStatement.kind === SyntaxKind.ForOfStatement && forInOrOfStatement.awaitKeyword) {
20802+
if ((forInOrOfStatement.flags & NodeFlags.AwaitContext) === NodeFlags.None) {
20803+
return grammarErrorOnNode(forInOrOfStatement.awaitKeyword, Diagnostics.A_for_await_of_statement_is_only_allowed_within_an_async_function_or_async_generator);
20804+
}
20805+
}
20806+
2079720807
if (forInOrOfStatement.initializer.kind === SyntaxKind.VariableDeclarationList) {
2079820808
const variableList = <VariableDeclarationList>forInOrOfStatement.initializer;
2079920809
if (!checkGrammarVariableDeclarationList(variableList)) {

src/compiler/commandLineParser.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -264,7 +264,7 @@ namespace ts {
264264
"es6": ScriptTarget.ES2015,
265265
"es2015": ScriptTarget.ES2015,
266266
"es2016": ScriptTarget.ES2016,
267-
"es2017": ScriptTarget.ES2017,
267+
"es2017": ScriptTarget.ES2017
268268
}),
269269
description: Diagnostics.Specify_ECMAScript_target_version_Colon_ES3_default_ES5_or_ES2015,
270270
paramType: Diagnostics.VERSION,
@@ -428,7 +428,7 @@ namespace ts {
428428
"es2015.symbol.wellknown": "lib.es2015.symbol.wellknown.d.ts",
429429
"es2016.array.include": "lib.es2016.array.include.d.ts",
430430
"es2017.object": "lib.es2017.object.d.ts",
431-
"es2017.sharedmemory": "lib.es2017.sharedmemory.d.ts"
431+
"es2017.sharedmemory": "lib.es2017.sharedmemory.d.ts",
432432
}),
433433
},
434434
description: Diagnostics.Specify_library_files_to_be_included_in_the_compilation_Colon

src/compiler/diagnosticMessages.json

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -291,14 +291,18 @@
291291
"category": "Error",
292292
"code": 1102
293293
},
294-
"A 'continue' statement can only be used within an enclosing iteration statement.": {
294+
"A 'for-await-of' statement is only allowed within an async function or async generator.": {
295295
"category": "Error",
296296
"code": 1104
297297
},
298-
"A 'break' statement can only be used within an enclosing iteration or switch statement.": {
298+
"A 'continue' statement can only be used within an enclosing iteration statement.": {
299299
"category": "Error",
300300
"code": 1105
301301
},
302+
"A 'break' statement can only be used within an enclosing iteration or switch statement.": {
303+
"category": "Error",
304+
"code": 1106
305+
},
302306
"Jump target cannot cross function boundary.": {
303307
"category": "Error",
304308
"code": 1107

src/compiler/emitter.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1341,6 +1341,7 @@ namespace ts {
13411341
function emitForOfStatement(node: ForOfStatement) {
13421342
const openParenPos = writeToken(SyntaxKind.ForKeyword, node.pos);
13431343
write(" ");
1344+
emitWithSuffix(node.awaitKeyword, " ");
13441345
writeToken(SyntaxKind.OpenParenToken, openParenPos);
13451346
emitForBinding(node.initializer);
13461347
write(" of ");

src/compiler/factory.ts

Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -280,9 +280,9 @@ namespace ts {
280280
return node;
281281
}
282282

283-
export function updateMethod(node: MethodDeclaration, decorators: Decorator[], modifiers: Modifier[], name: PropertyName, typeParameters: TypeParameterDeclaration[], parameters: ParameterDeclaration[], type: TypeNode, body: Block) {
284-
if (node.decorators !== decorators || node.modifiers !== modifiers || node.name !== name || node.typeParameters !== typeParameters || node.parameters !== parameters || node.type !== type || node.body !== body) {
285-
return updateNode(createMethod(decorators, modifiers, node.asteriskToken, name, typeParameters, parameters, type, body, /*location*/ node), node);
283+
export function updateMethod(node: MethodDeclaration, decorators: Decorator[], modifiers: Modifier[], asteriskToken: AsteriskToken, name: PropertyName, typeParameters: TypeParameterDeclaration[], parameters: ParameterDeclaration[], type: TypeNode, body: Block) {
284+
if (node.decorators !== decorators || node.modifiers !== modifiers || node.asteriskToken !== asteriskToken || node.name !== name || node.typeParameters !== typeParameters || node.parameters !== parameters || node.type !== type || node.body !== body) {
285+
return updateNode(createMethod(decorators, modifiers, asteriskToken, name, typeParameters, parameters, type, body, /*location*/ node), node);
286286
}
287287
return node;
288288
}
@@ -525,9 +525,9 @@ namespace ts {
525525
return node;
526526
}
527527

528-
export function updateFunctionExpression(node: FunctionExpression, modifiers: Modifier[], name: Identifier, typeParameters: TypeParameterDeclaration[], parameters: ParameterDeclaration[], type: TypeNode, body: Block) {
529-
if (node.name !== name || node.modifiers !== modifiers || node.typeParameters !== typeParameters || node.parameters !== parameters || node.type !== type || node.body !== body) {
530-
return updateNode(createFunctionExpression(modifiers, node.asteriskToken, name, typeParameters, parameters, type, body, /*location*/ node), node);
528+
export function updateFunctionExpression(node: FunctionExpression, modifiers: Modifier[], asteriskToken: AsteriskToken, name: Identifier, typeParameters: TypeParameterDeclaration[], parameters: ParameterDeclaration[], type: TypeNode, body: Block) {
529+
if (node.modifiers !== modifiers || node.asteriskToken !== asteriskToken || node.name !== name || node.typeParameters !== typeParameters || node.parameters !== parameters || node.type !== type || node.body !== body) {
530+
return updateNode(createFunctionExpression(modifiers, asteriskToken, name, typeParameters, parameters, type, body, /*location*/ node), node);
531531
}
532532
return node;
533533
}
@@ -1068,9 +1068,9 @@ namespace ts {
10681068
return node;
10691069
}
10701070

1071-
export function updateFunctionDeclaration(node: FunctionDeclaration, decorators: Decorator[], modifiers: Modifier[], name: Identifier, typeParameters: TypeParameterDeclaration[], parameters: ParameterDeclaration[], type: TypeNode, body: Block) {
1072-
if (node.decorators !== decorators || node.modifiers !== modifiers || node.name !== name || node.typeParameters !== typeParameters || node.parameters !== parameters || node.type !== type || node.body !== body) {
1073-
return updateNode(createFunctionDeclaration(decorators, modifiers, node.asteriskToken, name, typeParameters, parameters, type, body, /*location*/ node), node);
1071+
export function updateFunctionDeclaration(node: FunctionDeclaration, decorators: Decorator[], modifiers: Modifier[], asteriskToken: AsteriskToken, name: Identifier, typeParameters: TypeParameterDeclaration[], parameters: ParameterDeclaration[], type: TypeNode, body: Block) {
1072+
if (node.decorators !== decorators || node.modifiers !== modifiers || node.asteriskToken !== asteriskToken || node.name !== name || node.typeParameters !== typeParameters || node.parameters !== parameters || node.type !== type || node.body !== body) {
1073+
return updateNode(createFunctionDeclaration(decorators, modifiers, asteriskToken, name, typeParameters, parameters, type, body, /*location*/ node), node);
10741074
}
10751075
return node;
10761076
}
@@ -1825,6 +1825,28 @@ namespace ts {
18251825
return node;
18261826
}
18271827

1828+
export function createForOfBindingStatement(node: ForInitializer, boundValue: Expression): Statement {
1829+
if (isVariableDeclarationList(node)) {
1830+
const firstDeclaration = firstOrUndefined(node.declarations);
1831+
const updatedDeclaration = updateVariableDeclaration(
1832+
firstDeclaration,
1833+
firstDeclaration.name,
1834+
/*typeNode*/ undefined,
1835+
boundValue
1836+
);
1837+
return createVariableStatement(
1838+
/*modifiers*/ undefined,
1839+
updateVariableDeclarationList(node, [updatedDeclaration]),
1840+
/*location*/ node
1841+
);
1842+
}
1843+
else {
1844+
const expression = node;
1845+
const updatedExpression = createAssignment(expression, boundValue, /*location*/ expression);
1846+
return createStatement(updatedExpression, /*location*/ expression);
1847+
}
1848+
}
1849+
18281850
export interface CallBinding {
18291851
target: LeftHandSideExpression;
18301852
thisArg: Expression;

src/compiler/parser.ts

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,8 @@ namespace ts {
233233
visitNode(cbNode, (<ForInStatement>node).expression) ||
234234
visitNode(cbNode, (<ForInStatement>node).statement);
235235
case SyntaxKind.ForOfStatement:
236-
return visitNode(cbNode, (<ForOfStatement>node).initializer) ||
236+
return visitNode(cbNode, (<ForOfStatement>node).awaitKeyword) ||
237+
visitNode(cbNode, (<ForOfStatement>node).initializer) ||
237238
visitNode(cbNode, (<ForOfStatement>node).expression) ||
238239
visitNode(cbNode, (<ForOfStatement>node).statement);
239240
case SyntaxKind.ContinueStatement:
@@ -4362,6 +4363,7 @@ namespace ts {
43624363
function parseForOrForInOrForOfStatement(): Statement {
43634364
const pos = getNodePos();
43644365
parseExpected(SyntaxKind.ForKeyword);
4366+
const awaitKeyword = parseOptionalToken(SyntaxKind.AwaitKeyword);
43654367
parseExpected(SyntaxKind.OpenParenToken);
43664368

43674369
let initializer: VariableDeclarationList | Expression = undefined;
@@ -4374,20 +4376,21 @@ namespace ts {
43744376
}
43754377
}
43764378
let forOrForInOrForOfStatement: IterationStatement;
4377-
if (parseOptional(SyntaxKind.InKeyword)) {
4378-
const forInStatement = <ForInStatement>createNode(SyntaxKind.ForInStatement, pos);
4379-
forInStatement.initializer = initializer;
4380-
forInStatement.expression = allowInAnd(parseExpression);
4381-
parseExpected(SyntaxKind.CloseParenToken);
4382-
forOrForInOrForOfStatement = forInStatement;
4383-
}
4384-
else if (parseOptional(SyntaxKind.OfKeyword)) {
4379+
if (awaitKeyword ? parseExpected(SyntaxKind.OfKeyword) : parseOptional(SyntaxKind.OfKeyword)) {
43854380
const forOfStatement = <ForOfStatement>createNode(SyntaxKind.ForOfStatement, pos);
4381+
forOfStatement.awaitKeyword = awaitKeyword;
43864382
forOfStatement.initializer = initializer;
43874383
forOfStatement.expression = allowInAnd(parseAssignmentExpressionOrHigher);
43884384
parseExpected(SyntaxKind.CloseParenToken);
43894385
forOrForInOrForOfStatement = forOfStatement;
43904386
}
4387+
else if (parseOptional(SyntaxKind.InKeyword)) {
4388+
const forInStatement = <ForInStatement>createNode(SyntaxKind.ForInStatement, pos);
4389+
forInStatement.initializer = initializer;
4390+
forInStatement.expression = allowInAnd(parseExpression);
4391+
parseExpected(SyntaxKind.CloseParenToken);
4392+
forOrForInOrForOfStatement = forInStatement;
4393+
}
43914394
else {
43924395
const forStatement = <ForStatement>createNode(SyntaxKind.ForStatement, pos);
43934396
forStatement.initializer = initializer;

src/compiler/transformers/es2015.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1529,6 +1529,7 @@ namespace ts {
15291529
return updateFunctionExpression(
15301530
node,
15311531
/*modifiers*/ undefined,
1532+
node.asteriskToken,
15321533
node.name,
15331534
/*typeParameters*/ undefined,
15341535
visitParameterList(node.parameters, visitor, context),
@@ -1549,6 +1550,7 @@ namespace ts {
15491550
node,
15501551
/*decorators*/ undefined,
15511552
node.modifiers,
1553+
node.asteriskToken,
15521554
node.name,
15531555
/*typeParameters*/ undefined,
15541556
visitParameterList(node.parameters, visitor, context),

src/compiler/transformers/es2017.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,8 +175,17 @@ namespace ts {
175175
const iteratorRecord = isIdentifier(node.expression)
176176
? getGeneratedNameForNode(node.expression)
177177
: createUniqueName("iterator");
178+
178179
const expression = visitNode(node.expression, visitor, isExpression);
179180
const statements: Statement[] = [];
181+
const binding = createForOfBindingStatement(
182+
node.initializer,
183+
createPropertyAccess(
184+
createPropertyAccess(iteratorRecord, "result"),
185+
"value"
186+
)
187+
);
188+
statements.push(visitNode(binding, visitor, isStatement));
180189
const statement = visitNode(node.statement, visitor, isStatement);
181190
if (isBlock(statement)) {
182191
addRange(statements, statement.statements);
@@ -330,6 +339,7 @@ namespace ts {
330339
node,
331340
/*decorators*/ undefined,
332341
visitNodes(node.modifiers, visitor, isModifier),
342+
enclosingFunctionFlags & FunctionFlags.Async ? undefined : node.asteriskToken,
333343
node.name,
334344
/*typeParameters*/ undefined,
335345
visitParameterList(node.parameters, visitor, context),
@@ -359,6 +369,7 @@ namespace ts {
359369
node,
360370
/*decorators*/ undefined,
361371
visitNodes(node.modifiers, visitor, isModifier),
372+
enclosingFunctionFlags & FunctionFlags.Async ? undefined : node.asteriskToken,
362373
node.name,
363374
/*typeParameters*/ undefined,
364375
visitParameterList(node.parameters, visitor, context),
@@ -387,6 +398,7 @@ namespace ts {
387398
const updated = updateFunctionExpression(
388399
node,
389400
visitNodes(node.modifiers, visitor, isModifier),
401+
enclosingFunctionFlags & FunctionFlags.Async ? undefined : node.asteriskToken,
390402
node.name,
391403
/*typeParameters*/ undefined,
392404
visitParameterList(node.parameters, visitor, context),

0 commit comments

Comments
 (0)