Skip to content

Commit 701493f

Browse files
Support top level "for await of" (#37424)
* Support Top Level "for await of". * Add test cases for top level "for await of". * Apply suggestions from code review * add test cases * remove redundant variables * fix test baselines * Update diagnostic message and tests Co-authored-by: Ron Buckton <[email protected]>
1 parent f529115 commit 701493f

File tree

39 files changed

+607
-62
lines changed

39 files changed

+607
-62
lines changed

src/compiler/checker.ts

+26-12
Original file line numberDiff line numberDiff line change
@@ -40179,19 +40179,33 @@ namespace ts {
4017940179
}
4018040180

4018140181
if (forInOrOfStatement.kind === SyntaxKind.ForOfStatement && forInOrOfStatement.awaitModifier) {
40182-
if ((forInOrOfStatement.flags & NodeFlags.AwaitContext) === NodeFlags.None) {
40183-
// use of 'for-await-of' in non-async function
40182+
if (!(forInOrOfStatement.flags & NodeFlags.AwaitContext)) {
4018440183
const sourceFile = getSourceFileOfNode(forInOrOfStatement);
40185-
if (!hasParseDiagnostics(sourceFile)) {
40186-
const diagnostic = createDiagnosticForNode(forInOrOfStatement.awaitModifier, Diagnostics.A_for_await_of_statement_is_only_allowed_within_an_async_function_or_async_generator);
40187-
const func = getContainingFunction(forInOrOfStatement);
40188-
if (func && func.kind !== SyntaxKind.Constructor) {
40189-
Debug.assert((getFunctionFlags(func) & FunctionFlags.Async) === 0, "Enclosing function should never be an async function.");
40190-
const relatedInfo = createDiagnosticForNode(func, Diagnostics.Did_you_mean_to_mark_this_function_as_async);
40191-
addRelatedInfo(diagnostic, relatedInfo);
40192-
}
40193-
diagnostics.add(diagnostic);
40194-
return true;
40184+
if (isInTopLevelContext(forInOrOfStatement)) {
40185+
if (!hasParseDiagnostics(sourceFile)) {
40186+
if (!isEffectiveExternalModule(sourceFile, compilerOptions)) {
40187+
diagnostics.add(createDiagnosticForNode(forInOrOfStatement.awaitModifier,
40188+
Diagnostics.for_await_loops_are_only_allowed_at_the_top_level_of_a_file_when_that_file_is_a_module_but_this_file_has_no_imports_or_exports_Consider_adding_an_empty_export_to_make_this_file_a_module));
40189+
}
40190+
if ((moduleKind !== ModuleKind.ESNext && moduleKind !== ModuleKind.System) || languageVersion < ScriptTarget.ES2017) {
40191+
diagnostics.add(createDiagnosticForNode(forInOrOfStatement.awaitModifier,
40192+
Diagnostics.Top_level_for_await_loops_are_only_allowed_when_the_module_option_is_set_to_esnext_or_system_and_the_target_option_is_set_to_es2017_or_higher));
40193+
}
40194+
}
40195+
}
40196+
else {
40197+
// use of 'for-await-of' in non-async function
40198+
if (!hasParseDiagnostics(sourceFile)) {
40199+
const diagnostic = createDiagnosticForNode(forInOrOfStatement.awaitModifier, Diagnostics.for_await_loops_are_only_allowed_within_async_functions_and_at_the_top_levels_of_modules);
40200+
const func = getContainingFunction(forInOrOfStatement);
40201+
if (func && func.kind !== SyntaxKind.Constructor) {
40202+
Debug.assert((getFunctionFlags(func) & FunctionFlags.Async) === 0, "Enclosing function should never be an async function.");
40203+
const relatedInfo = createDiagnosticForNode(func, Diagnostics.Did_you_mean_to_mark_this_function_as_async);
40204+
addRelatedInfo(diagnostic, relatedInfo);
40205+
}
40206+
diagnostics.add(diagnostic);
40207+
return true;
40208+
}
4019540209
}
4019640210
return false;
4019740211
}

src/compiler/diagnosticMessages.json

+10-1
Original file line numberDiff line numberDiff line change
@@ -307,7 +307,7 @@
307307
"category": "Error",
308308
"code": 1102
309309
},
310-
"A 'for-await-of' statement is only allowed within an async function or async generator.": {
310+
"'for await' loops are only allowed within async functions and at the top levels of modules.": {
311311
"category": "Error",
312312
"code": 1103
313313
},
@@ -1352,6 +1352,15 @@
13521352
"category": "Message",
13531353
"code": 1430
13541354
},
1355+
"'for await' loops are only allowed at the top level of a file when that file is a module, but this file has no imports or exports. Consider adding an empty 'export {}' to make this file a module.": {
1356+
"category": "Error",
1357+
"code": 1431
1358+
},
1359+
"Top-level 'for await' loops are only allowed when the 'module' option is set to 'esnext' or 'system', and the 'target' option is set to 'es2017' or higher.": {
1360+
"category": "Error",
1361+
"code": 1432
1362+
},
1363+
13551364
"The types of '{0}' are incompatible between these types.": {
13561365
"category": "Error",
13571366
"code": 2200

src/services/codefixes/addEmptyExportDeclaration.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
/* @internal */
22
namespace ts.codefix {
33
registerCodeFix({
4-
errorCodes: [Diagnostics.await_expressions_are_only_allowed_at_the_top_level_of_a_file_when_that_file_is_a_module_but_this_file_has_no_imports_or_exports_Consider_adding_an_empty_export_to_make_this_file_a_module.code],
4+
errorCodes: [
5+
Diagnostics.await_expressions_are_only_allowed_at_the_top_level_of_a_file_when_that_file_is_a_module_but_this_file_has_no_imports_or_exports_Consider_adding_an_empty_export_to_make_this_file_a_module.code,
6+
Diagnostics.for_await_loops_are_only_allowed_at_the_top_level_of_a_file_when_that_file_is_a_module_but_this_file_has_no_imports_or_exports_Consider_adding_an_empty_export_to_make_this_file_a_module.code,
7+
],
58
getCodeActions: context => {
69
const { sourceFile } = context;
710
const changes = textChanges.ChangeTracker.with(context, changes => {

src/services/codefixes/fixAwaitInSyncFunction.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ namespace ts.codefix {
33
const fixId = "fixAwaitInSyncFunction";
44
const errorCodes = [
55
Diagnostics.await_expressions_are_only_allowed_within_async_functions_and_at_the_top_levels_of_modules.code,
6-
Diagnostics.A_for_await_of_statement_is_only_allowed_within_an_async_function_or_async_generator.code,
6+
Diagnostics.for_await_loops_are_only_allowed_within_async_functions_and_at_the_top_levels_of_modules.code,
77
];
88
registerCodeFix({
99
errorCodes,

src/services/codefixes/fixModuleAndTargetOptions.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
/* @internal */
22
namespace ts.codefix {
33
registerCodeFix({
4-
errorCodes: [Diagnostics.Top_level_await_expressions_are_only_allowed_when_the_module_option_is_set_to_esnext_or_system_and_the_target_option_is_set_to_es2017_or_higher.code],
4+
errorCodes: [
5+
Diagnostics.Top_level_await_expressions_are_only_allowed_when_the_module_option_is_set_to_esnext_or_system_and_the_target_option_is_set_to_es2017_or_higher.code,
6+
Diagnostics.Top_level_for_await_loops_are_only_allowed_when_the_module_option_is_set_to_esnext_or_system_and_the_target_option_is_set_to_es2017_or_higher.code,
7+
],
58
getCodeActions: context => {
69
const compilerOptions = context.program.getCompilerOptions();
710
const { configFile } = compilerOptions;

tests/baselines/reference/awaitInNonAsyncFunction.errors.txt

+16-16
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
1-
tests/cases/compiler/awaitInNonAsyncFunction.ts(4,7): error TS1103: A 'for-await-of' statement is only allowed within an async function or async generator.
1+
tests/cases/compiler/awaitInNonAsyncFunction.ts(4,7): error TS1103: 'for await' loops are only allowed within async functions and at the top levels of modules.
22
tests/cases/compiler/awaitInNonAsyncFunction.ts(5,10): error TS1308: 'await' expressions are only allowed within async functions and at the top levels of modules.
3-
tests/cases/compiler/awaitInNonAsyncFunction.ts(9,7): error TS1103: A 'for-await-of' statement is only allowed within an async function or async generator.
3+
tests/cases/compiler/awaitInNonAsyncFunction.ts(9,7): error TS1103: 'for await' loops are only allowed within async functions and at the top levels of modules.
44
tests/cases/compiler/awaitInNonAsyncFunction.ts(10,10): error TS1308: 'await' expressions are only allowed within async functions and at the top levels of modules.
5-
tests/cases/compiler/awaitInNonAsyncFunction.ts(14,7): error TS1103: A 'for-await-of' statement is only allowed within an async function or async generator.
5+
tests/cases/compiler/awaitInNonAsyncFunction.ts(14,7): error TS1103: 'for await' loops are only allowed within async functions and at the top levels of modules.
66
tests/cases/compiler/awaitInNonAsyncFunction.ts(15,3): error TS1308: 'await' expressions are only allowed within async functions and at the top levels of modules.
7-
tests/cases/compiler/awaitInNonAsyncFunction.ts(19,7): error TS1103: A 'for-await-of' statement is only allowed within an async function or async generator.
7+
tests/cases/compiler/awaitInNonAsyncFunction.ts(19,7): error TS1103: 'for await' loops are only allowed within async functions and at the top levels of modules.
88
tests/cases/compiler/awaitInNonAsyncFunction.ts(20,10): error TS1308: 'await' expressions are only allowed within async functions and at the top levels of modules.
9-
tests/cases/compiler/awaitInNonAsyncFunction.ts(24,7): error TS1103: A 'for-await-of' statement is only allowed within an async function or async generator.
9+
tests/cases/compiler/awaitInNonAsyncFunction.ts(24,7): error TS1103: 'for await' loops are only allowed within async functions and at the top levels of modules.
1010
tests/cases/compiler/awaitInNonAsyncFunction.ts(25,9): error TS1308: 'await' expressions are only allowed within async functions and at the top levels of modules.
11-
tests/cases/compiler/awaitInNonAsyncFunction.ts(30,9): error TS1103: A 'for-await-of' statement is only allowed within an async function or async generator.
11+
tests/cases/compiler/awaitInNonAsyncFunction.ts(30,9): error TS1103: 'for await' loops are only allowed within async functions and at the top levels of modules.
1212
tests/cases/compiler/awaitInNonAsyncFunction.ts(31,5): error TS1308: 'await' expressions are only allowed within async functions and at the top levels of modules.
13-
tests/cases/compiler/awaitInNonAsyncFunction.ts(34,7): error TS1103: A 'for-await-of' statement is only allowed within an async function or async generator.
13+
tests/cases/compiler/awaitInNonAsyncFunction.ts(34,7): error TS1103: 'for await' loops are only allowed within async functions and at the top levels of modules.
1414
tests/cases/compiler/awaitInNonAsyncFunction.ts(35,5): error TS1308: 'await' expressions are only allowed within async functions and at the top levels of modules.
15-
tests/cases/compiler/awaitInNonAsyncFunction.ts(39,5): error TS1103: A 'for-await-of' statement is only allowed within an async function or async generator.
15+
tests/cases/compiler/awaitInNonAsyncFunction.ts(39,5): error TS1432: Top-level 'for await' loops are only allowed when the 'module' option is set to 'esnext' or 'system', and the 'target' option is set to 'es2017' or higher.
1616
tests/cases/compiler/awaitInNonAsyncFunction.ts(40,1): error TS1378: Top-level 'await' expressions are only allowed when the 'module' option is set to 'esnext' or 'system', and the 'target' option is set to 'es2017' or higher.
1717

1818

@@ -22,7 +22,7 @@ tests/cases/compiler/awaitInNonAsyncFunction.ts(40,1): error TS1378: Top-level '
2222
function normalFunc(p: Promise<number>) {
2323
for await (const _ of []);
2424
~~~~~
25-
!!! error TS1103: A 'for-await-of' statement is only allowed within an async function or async generator.
25+
!!! error TS1103: 'for await' loops are only allowed within async functions and at the top levels of modules.
2626
!!! related TS1356 tests/cases/compiler/awaitInNonAsyncFunction.ts:3:10: Did you mean to mark this function as 'async'?
2727
return await p;
2828
~~~~~
@@ -33,7 +33,7 @@ tests/cases/compiler/awaitInNonAsyncFunction.ts(40,1): error TS1378: Top-level '
3333
export function exportedFunc(p: Promise<number>) {
3434
for await (const _ of []);
3535
~~~~~
36-
!!! error TS1103: A 'for-await-of' statement is only allowed within an async function or async generator.
36+
!!! error TS1103: 'for await' loops are only allowed within async functions and at the top levels of modules.
3737
!!! related TS1356 tests/cases/compiler/awaitInNonAsyncFunction.ts:8:17: Did you mean to mark this function as 'async'?
3838
return await p;
3939
~~~~~
@@ -44,7 +44,7 @@ tests/cases/compiler/awaitInNonAsyncFunction.ts(40,1): error TS1378: Top-level '
4444
const functionExpression = function(p: Promise<number>) {
4545
for await (const _ of []);
4646
~~~~~
47-
!!! error TS1103: A 'for-await-of' statement is only allowed within an async function or async generator.
47+
!!! error TS1103: 'for await' loops are only allowed within async functions and at the top levels of modules.
4848
!!! related TS1356 tests/cases/compiler/awaitInNonAsyncFunction.ts:13:28: Did you mean to mark this function as 'async'?
4949
await p;
5050
~~~~~
@@ -55,7 +55,7 @@ tests/cases/compiler/awaitInNonAsyncFunction.ts(40,1): error TS1378: Top-level '
5555
const arrowFunc = (p: Promise<number>) => {
5656
for await (const _ of []);
5757
~~~~~
58-
!!! error TS1103: A 'for-await-of' statement is only allowed within an async function or async generator.
58+
!!! error TS1103: 'for await' loops are only allowed within async functions and at the top levels of modules.
5959
!!! related TS1356 tests/cases/compiler/awaitInNonAsyncFunction.ts:18:19: Did you mean to mark this function as 'async'?
6060
return await p;
6161
~~~~~
@@ -66,7 +66,7 @@ tests/cases/compiler/awaitInNonAsyncFunction.ts(40,1): error TS1378: Top-level '
6666
function* generatorFunc(p: Promise<number>) {
6767
for await (const _ of []);
6868
~~~~~
69-
!!! error TS1103: A 'for-await-of' statement is only allowed within an async function or async generator.
69+
!!! error TS1103: 'for await' loops are only allowed within async functions and at the top levels of modules.
7070
!!! related TS1356 tests/cases/compiler/awaitInNonAsyncFunction.ts:23:11: Did you mean to mark this function as 'async'?
7171
yield await p;
7272
~~~~~
@@ -78,15 +78,15 @@ tests/cases/compiler/awaitInNonAsyncFunction.ts(40,1): error TS1378: Top-level '
7878
constructor(p: Promise<number>) {
7979
for await (const _ of []);
8080
~~~~~
81-
!!! error TS1103: A 'for-await-of' statement is only allowed within an async function or async generator.
81+
!!! error TS1103: 'for await' loops are only allowed within async functions and at the top levels of modules.
8282
await p;
8383
~~~~~
8484
!!! error TS1308: 'await' expressions are only allowed within async functions and at the top levels of modules.
8585
}
8686
method(p: Promise<number>) {
8787
for await (const _ of []);
8888
~~~~~
89-
!!! error TS1103: A 'for-await-of' statement is only allowed within an async function or async generator.
89+
!!! error TS1103: 'for await' loops are only allowed within async functions and at the top levels of modules.
9090
!!! related TS1356 tests/cases/compiler/awaitInNonAsyncFunction.ts:33:3: Did you mean to mark this function as 'async'?
9191
await p;
9292
~~~~~
@@ -97,7 +97,7 @@ tests/cases/compiler/awaitInNonAsyncFunction.ts(40,1): error TS1378: Top-level '
9797

9898
for await (const _ of []);
9999
~~~~~
100-
!!! error TS1103: A 'for-await-of' statement is only allowed within an async function or async generator.
100+
!!! error TS1432: Top-level 'for await' loops are only allowed when the 'module' option is set to 'esnext' or 'system', and the 'target' option is set to 'es2017' or higher.
101101
await null;
102102
~~~~~
103103
!!! error TS1378: Top-level 'await' expressions are only allowed when the 'module' option is set to 'esnext' or 'system', and the 'target' option is set to 'es2017' or higher.

0 commit comments

Comments
 (0)