Skip to content

Commit 1a30b10

Browse files
committed
Add top-level await for esnext and system modules
1 parent 2f0d07c commit 1a30b10

28 files changed

+274
-49
lines changed

src/compiler/binder.ts

+5-2
Original file line numberDiff line numberDiff line change
@@ -3898,10 +3898,13 @@ namespace ts {
38983898

38993899
switch (kind) {
39003900
case SyntaxKind.AsyncKeyword:
3901-
case SyntaxKind.AwaitExpression:
3902-
// async/await is ES2017 syntax, but may be ES2018 syntax (for async generators)
3901+
// async is ES2017 syntax, but may be ES2018 syntax (for async generators)
39033902
transformFlags |= TransformFlags.AssertES2018 | TransformFlags.AssertES2017;
39043903
break;
3904+
case SyntaxKind.AwaitExpression:
3905+
// await is ES2017 syntax, but may be ES2018 syntax (for async generators)
3906+
transformFlags |= TransformFlags.AssertES2018 | TransformFlags.AssertES2017 | TransformFlags.ContainsAwait;
3907+
break;
39053908

39063909
case SyntaxKind.TypeAssertionExpression:
39073910
case SyntaxKind.AsExpression:

src/compiler/checker.ts

+30-10
Original file line numberDiff line numberDiff line change
@@ -26422,21 +26422,41 @@ namespace ts {
2642226422
return undefinedWideningType;
2642326423
}
2642426424

26425+
function isTopLevelAwait(node: AwaitExpression) {
26426+
const container = getThisContainer(node, /*includeArrowFunctions*/ true);
26427+
return isSourceFile(container);
26428+
}
26429+
2642526430
function checkAwaitExpression(node: AwaitExpression): Type {
2642626431
// Grammar checking
2642726432
if (produceDiagnostics) {
2642826433
if (!(node.flags & NodeFlags.AwaitContext)) {
26429-
// use of 'await' in non-async function
26430-
const sourceFile = getSourceFileOfNode(node);
26431-
if (!hasParseDiagnostics(sourceFile)) {
26432-
const span = getSpanOfTokenAtPosition(sourceFile, node.pos);
26433-
const diagnostic = createFileDiagnostic(sourceFile, span.start, span.length, Diagnostics.await_expression_is_only_allowed_within_an_async_function);
26434-
const func = getContainingFunction(node);
26435-
if (func && func.kind !== SyntaxKind.Constructor && (getFunctionFlags(func) & FunctionFlags.Async) === 0) {
26436-
const relatedInfo = createDiagnosticForNode(func, Diagnostics.Did_you_mean_to_mark_this_function_as_async);
26437-
addRelatedInfo(diagnostic, relatedInfo);
26434+
if (isTopLevelAwait(node)) {
26435+
const sourceFile = getSourceFileOfNode(node);
26436+
if ((moduleKind !== ModuleKind.ESNext && moduleKind !== ModuleKind.System) ||
26437+
languageVersion < ScriptTarget.ES2017 ||
26438+
!isEffectiveExternalModule(sourceFile, compilerOptions)) {
26439+
if (!hasParseDiagnostics(sourceFile)) {
26440+
const span = getSpanOfTokenAtPosition(sourceFile, node.pos);
26441+
const diagnostic = createFileDiagnostic(sourceFile, span.start, span.length,
26442+
Diagnostics.await_outside_of_an_async_function_is_only_allowed_at_the_top_level_of_a_module_when_module_is_esnext_or_system_and_target_is_es2017_or_higher);
26443+
diagnostics.add(diagnostic);
26444+
}
26445+
}
26446+
}
26447+
else {
26448+
// use of 'await' in non-async function
26449+
const sourceFile = getSourceFileOfNode(node);
26450+
if (!hasParseDiagnostics(sourceFile)) {
26451+
const span = getSpanOfTokenAtPosition(sourceFile, node.pos);
26452+
const diagnostic = createFileDiagnostic(sourceFile, span.start, span.length, Diagnostics.await_expression_is_only_allowed_within_an_async_function);
26453+
const func = getContainingFunction(node);
26454+
if (func && func.kind !== SyntaxKind.Constructor && (getFunctionFlags(func) & FunctionFlags.Async) === 0) {
26455+
const relatedInfo = createDiagnosticForNode(func, Diagnostics.Did_you_mean_to_mark_this_function_as_async);
26456+
addRelatedInfo(diagnostic, relatedInfo);
26457+
}
26458+
diagnostics.add(diagnostic);
2643826459
}
26439-
diagnostics.add(diagnostic);
2644026460
}
2644126461
}
2644226462

src/compiler/diagnosticMessages.json

+4
Original file line numberDiff line numberDiff line change
@@ -1059,6 +1059,10 @@
10591059
"category": "Error",
10601060
"code": 1360
10611061
},
1062+
"'await' outside of an async function is only allowed at the top level of a module when '--module' is 'esnext' or 'system' and '--target' is 'es2017' or higher.": {
1063+
"category": "Error",
1064+
"code": 1361
1065+
},
10621066

10631067
"The types of '{0}' are incompatible between these types.": {
10641068
"category": "Error",

src/compiler/transformers/es2017.ts

+40-13
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@ namespace ts {
77
AsyncMethodsWithSuper = 1 << 0
88
}
99

10+
const enum ContextFlags {
11+
NonTopLevel = 1 << 0,
12+
HasLexicalThis = 1 << 1
13+
}
14+
1015
export function transformES2017(context: TransformationContext) {
1116
const {
1217
resumeLexicalEnvironment,
@@ -41,7 +46,7 @@ namespace ts {
4146
/** A set of node IDs for generated super accessors (variable statements). */
4247
const substitutedSuperAccessors: boolean[] = [];
4348

44-
let topLevel: boolean;
49+
let contextFlags: ContextFlags = 0;
4550

4651
// Save the previous transformation hooks.
4752
const previousOnEmitNode = context.onEmitNode;
@@ -58,17 +63,35 @@ namespace ts {
5863
return node;
5964
}
6065

61-
topLevel = isEffectiveStrictModeSourceFile(node, compilerOptions);
66+
setContextFlag(ContextFlags.NonTopLevel, false);
67+
setContextFlag(ContextFlags.HasLexicalThis, !isEffectiveStrictModeSourceFile(node, compilerOptions));
6268
const visited = visitEachChild(node, visitor, context);
6369
addEmitHelpers(visited, context.readEmitHelpers());
6470
return visited;
6571
}
6672

67-
function doOutsideOfTopLevel<T, U>(cb: (value: T) => U, value: T) {
68-
if (topLevel) {
69-
topLevel = false;
73+
function setContextFlag(flag: ContextFlags, val: boolean) {
74+
contextFlags = val ? contextFlags | flag : contextFlags & ~flag;
75+
}
76+
77+
function inContext(flags: ContextFlags) {
78+
return (contextFlags & flags) !== 0;
79+
}
80+
81+
function inTopLevelContext() {
82+
return !inContext(ContextFlags.NonTopLevel);
83+
}
84+
85+
function inHasLexicalThisContext() {
86+
return inContext(ContextFlags.HasLexicalThis);
87+
}
88+
89+
function doWithContext<T, U>(flags: ContextFlags, cb: (value: T) => U, value: T) {
90+
const contextFlagsToSet = flags & ~contextFlags;
91+
if (contextFlagsToSet) {
92+
setContextFlag(contextFlagsToSet, /*val*/ true);
7093
const result = cb(value);
71-
topLevel = true;
94+
setContextFlag(contextFlagsToSet, /*val*/ false);
7295
return result;
7396
}
7497
return cb(value);
@@ -91,16 +114,16 @@ namespace ts {
91114
return visitAwaitExpression(<AwaitExpression>node);
92115

93116
case SyntaxKind.MethodDeclaration:
94-
return doOutsideOfTopLevel(visitMethodDeclaration, <MethodDeclaration>node);
117+
return doWithContext(ContextFlags.NonTopLevel | ContextFlags.HasLexicalThis, visitMethodDeclaration, <MethodDeclaration>node);
95118

96119
case SyntaxKind.FunctionDeclaration:
97-
return doOutsideOfTopLevel(visitFunctionDeclaration, <FunctionDeclaration>node);
120+
return doWithContext(ContextFlags.NonTopLevel | ContextFlags.HasLexicalThis, visitFunctionDeclaration, <FunctionDeclaration>node);
98121

99122
case SyntaxKind.FunctionExpression:
100-
return doOutsideOfTopLevel(visitFunctionExpression, <FunctionExpression>node);
123+
return doWithContext(ContextFlags.NonTopLevel | ContextFlags.HasLexicalThis, visitFunctionExpression, <FunctionExpression>node);
101124

102125
case SyntaxKind.ArrowFunction:
103-
return visitArrowFunction(<ArrowFunction>node);
126+
return doWithContext(ContextFlags.NonTopLevel, visitArrowFunction, <ArrowFunction>node);
104127

105128
case SyntaxKind.PropertyAccessExpression:
106129
if (capturedSuperProperties && isPropertyAccessExpression(node) && node.expression.kind === SyntaxKind.SuperKeyword) {
@@ -119,7 +142,7 @@ namespace ts {
119142
case SyntaxKind.Constructor:
120143
case SyntaxKind.ClassDeclaration:
121144
case SyntaxKind.ClassExpression:
122-
return doOutsideOfTopLevel(visitDefault, node);
145+
return doWithContext(ContextFlags.NonTopLevel | ContextFlags.HasLexicalThis, visitDefault, node);
123146

124147
default:
125148
return visitEachChild(node, visitor, context);
@@ -237,6 +260,10 @@ namespace ts {
237260
* @param node The node to visit.
238261
*/
239262
function visitAwaitExpression(node: AwaitExpression): Expression {
263+
// do not downlevel a top-level await as it is module syntax...
264+
if (inTopLevelContext()) {
265+
return visitEachChild(node, visitor, context);
266+
}
240267
return setOriginalNode(
241268
setTextRange(
242269
createYield(
@@ -457,7 +484,7 @@ namespace ts {
457484
createReturn(
458485
createAwaiterHelper(
459486
context,
460-
!topLevel,
487+
inHasLexicalThisContext(),
461488
hasLexicalArguments,
462489
promiseConstructor,
463490
transformAsyncFunctionBodyWorker(<Block>node.body, statementOffset)
@@ -498,7 +525,7 @@ namespace ts {
498525
else {
499526
const expression = createAwaiterHelper(
500527
context,
501-
!topLevel,
528+
inHasLexicalThisContext(),
502529
hasLexicalArguments,
503530
promiseConstructor,
504531
transformAsyncFunctionBodyWorker(node.body!)

src/compiler/transformers/es2018.ts

+14-14
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ namespace ts {
2626
let enabledSubstitutions: ESNextSubstitutionFlags;
2727
let enclosingFunctionFlags: FunctionFlags;
2828
let enclosingSuperContainerFlags: NodeCheckFlags = 0;
29-
let topLevel: boolean;
29+
let hasLexicalThis: boolean;
3030

3131
/** Keeps track of property names accessed on super (`super.x`) within async functions. */
3232
let capturedSuperProperties: UnderscoreEscapedMap<true>;
@@ -43,7 +43,7 @@ namespace ts {
4343
}
4444

4545
exportedVariableStatement = false;
46-
topLevel = isEffectiveStrictModeSourceFile(node, compilerOptions);
46+
hasLexicalThis = !isEffectiveStrictModeSourceFile(node, compilerOptions);
4747
const visited = visitEachChild(node, visitor, context);
4848
addEmitHelpers(visited, context.readEmitHelpers());
4949
return visited;
@@ -64,11 +64,11 @@ namespace ts {
6464
return node;
6565
}
6666

67-
function doOutsideOfTopLevel<T, U>(cb: (value: T) => U, value: T) {
68-
if (topLevel) {
69-
topLevel = false;
67+
function doWithLexicalThis<T, U>(cb: (value: T) => U, value: T) {
68+
if (!hasLexicalThis) {
69+
hasLexicalThis = true;
7070
const result = cb(value);
71-
topLevel = true;
71+
hasLexicalThis = false;
7272
return result;
7373
}
7474
return cb(value);
@@ -108,17 +108,17 @@ namespace ts {
108108
case SyntaxKind.VoidExpression:
109109
return visitVoidExpression(node as VoidExpression);
110110
case SyntaxKind.Constructor:
111-
return doOutsideOfTopLevel(visitConstructorDeclaration, node as ConstructorDeclaration);
111+
return doWithLexicalThis(visitConstructorDeclaration, node as ConstructorDeclaration);
112112
case SyntaxKind.MethodDeclaration:
113-
return doOutsideOfTopLevel(visitMethodDeclaration, node as MethodDeclaration);
113+
return doWithLexicalThis(visitMethodDeclaration, node as MethodDeclaration);
114114
case SyntaxKind.GetAccessor:
115-
return doOutsideOfTopLevel(visitGetAccessorDeclaration, node as GetAccessorDeclaration);
115+
return doWithLexicalThis(visitGetAccessorDeclaration, node as GetAccessorDeclaration);
116116
case SyntaxKind.SetAccessor:
117-
return doOutsideOfTopLevel(visitSetAccessorDeclaration, node as SetAccessorDeclaration);
117+
return doWithLexicalThis(visitSetAccessorDeclaration, node as SetAccessorDeclaration);
118118
case SyntaxKind.FunctionDeclaration:
119-
return doOutsideOfTopLevel(visitFunctionDeclaration, node as FunctionDeclaration);
119+
return doWithLexicalThis(visitFunctionDeclaration, node as FunctionDeclaration);
120120
case SyntaxKind.FunctionExpression:
121-
return doOutsideOfTopLevel(visitFunctionExpression, node as FunctionExpression);
121+
return doWithLexicalThis(visitFunctionExpression, node as FunctionExpression);
122122
case SyntaxKind.ArrowFunction:
123123
return visitArrowFunction(node as ArrowFunction);
124124
case SyntaxKind.Parameter:
@@ -139,7 +139,7 @@ namespace ts {
139139
return visitEachChild(node, visitor, context);
140140
case SyntaxKind.ClassDeclaration:
141141
case SyntaxKind.ClassExpression:
142-
return doOutsideOfTopLevel(visitDefault, node);
142+
return doWithLexicalThis(visitDefault, node);
143143
default:
144144
return visitEachChild(node, visitor, context);
145145
}
@@ -774,7 +774,7 @@ namespace ts {
774774
visitLexicalEnvironment(node.body!.statements, visitor, context, statementOffset)
775775
)
776776
),
777-
!topLevel
777+
hasLexicalThis
778778
)
779779
);
780780

src/compiler/transformers/module/system.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -262,13 +262,16 @@ namespace ts {
262262
insertStatementsAfterStandardPrologue(statements, endLexicalEnvironment());
263263

264264
const exportStarFunction = addExportStarIfNeeded(statements)!; // TODO: GH#18217
265+
const modifiers = node.transformFlags & TransformFlags.ContainsAwait ?
266+
createModifiersFromModifierFlags(ModifierFlags.Async) :
267+
undefined;
265268
const moduleObject = createObjectLiteral([
266269
createPropertyAssignment("setters",
267270
createSettersArray(exportStarFunction, dependencyGroups)
268271
),
269272
createPropertyAssignment("execute",
270273
createFunctionExpression(
271-
/*modifiers*/ undefined,
274+
modifiers,
272275
/*asteriskToken*/ undefined,
273276
/*name*/ undefined,
274277
/*typeParameters*/ undefined,

src/compiler/types.ts

+8-7
Original file line numberDiff line numberDiff line change
@@ -5599,9 +5599,10 @@ namespace ts {
55995599
ContainsBlockScopedBinding = 1 << 15,
56005600
ContainsBindingPattern = 1 << 16,
56015601
ContainsYield = 1 << 17,
5602-
ContainsHoistedDeclarationOrCompletion = 1 << 18,
5603-
ContainsDynamicImport = 1 << 19,
5604-
ContainsClassFields = 1 << 20,
5602+
ContainsAwait = 1 << 18,
5603+
ContainsHoistedDeclarationOrCompletion = 1 << 19,
5604+
ContainsDynamicImport = 1 << 20,
5605+
ContainsClassFields = 1 << 21,
56055606

56065607
// Please leave this as 1 << 29.
56075608
// It is the maximum bit we can set before we outgrow the size of a v8 small integer (SMI) on an x86 system.
@@ -5627,10 +5628,10 @@ namespace ts {
56275628
OuterExpressionExcludes = HasComputedFlags,
56285629
PropertyAccessExcludes = OuterExpressionExcludes,
56295630
NodeExcludes = PropertyAccessExcludes,
5630-
ArrowFunctionExcludes = NodeExcludes | ContainsTypeScriptClassSyntax | ContainsBlockScopedBinding | ContainsYield | ContainsHoistedDeclarationOrCompletion | ContainsBindingPattern | ContainsObjectRestOrSpread,
5631-
FunctionExcludes = NodeExcludes | ContainsTypeScriptClassSyntax | ContainsLexicalThis | ContainsBlockScopedBinding | ContainsYield | ContainsHoistedDeclarationOrCompletion | ContainsBindingPattern | ContainsObjectRestOrSpread,
5632-
ConstructorExcludes = NodeExcludes | ContainsLexicalThis | ContainsBlockScopedBinding | ContainsYield | ContainsHoistedDeclarationOrCompletion | ContainsBindingPattern | ContainsObjectRestOrSpread,
5633-
MethodOrAccessorExcludes = NodeExcludes | ContainsLexicalThis | ContainsBlockScopedBinding | ContainsYield | ContainsHoistedDeclarationOrCompletion | ContainsBindingPattern | ContainsObjectRestOrSpread,
5631+
ArrowFunctionExcludes = NodeExcludes | ContainsTypeScriptClassSyntax | ContainsBlockScopedBinding | ContainsYield | ContainsAwait | ContainsHoistedDeclarationOrCompletion | ContainsBindingPattern | ContainsObjectRestOrSpread,
5632+
FunctionExcludes = NodeExcludes | ContainsTypeScriptClassSyntax | ContainsLexicalThis | ContainsBlockScopedBinding | ContainsYield | ContainsAwait | ContainsHoistedDeclarationOrCompletion | ContainsBindingPattern | ContainsObjectRestOrSpread,
5633+
ConstructorExcludes = NodeExcludes | ContainsLexicalThis | ContainsBlockScopedBinding | ContainsYield | ContainsAwait | ContainsHoistedDeclarationOrCompletion | ContainsBindingPattern | ContainsObjectRestOrSpread,
5634+
MethodOrAccessorExcludes = NodeExcludes | ContainsLexicalThis | ContainsBlockScopedBinding | ContainsYield | ContainsAwait | ContainsHoistedDeclarationOrCompletion | ContainsBindingPattern | ContainsObjectRestOrSpread,
56345635
PropertyExcludes = NodeExcludes | ContainsLexicalThis,
56355636
ClassExcludes = NodeExcludes | ContainsTypeScriptClassSyntax | ContainsComputedPropertyName,
56365637
ModuleExcludes = NodeExcludes | ContainsTypeScriptClassSyntax | ContainsLexicalThis | ContainsBlockScopedBinding | ContainsHoistedDeclarationOrCompletion,

tests/baselines/reference/awaitInNonAsyncFunction.errors.txt

+2-2
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ tests/cases/compiler/awaitInNonAsyncFunction.ts(31,5): error TS1308: 'await' exp
1313
tests/cases/compiler/awaitInNonAsyncFunction.ts(34,7): error TS1103: A 'for-await-of' statement is only allowed within an async function or async generator.
1414
tests/cases/compiler/awaitInNonAsyncFunction.ts(35,5): error TS1308: 'await' expression is only allowed within an async function.
1515
tests/cases/compiler/awaitInNonAsyncFunction.ts(39,5): error TS1103: A 'for-await-of' statement is only allowed within an async function or async generator.
16-
tests/cases/compiler/awaitInNonAsyncFunction.ts(40,1): error TS1308: 'await' expression is only allowed within an async function.
16+
tests/cases/compiler/awaitInNonAsyncFunction.ts(40,1): error TS1361: 'await' outside of an async function is only allowed at the top level of a module when '--module' is 'esnext' or 'system' and '--target' is 'es2017' or higher.
1717

1818

1919
==== tests/cases/compiler/awaitInNonAsyncFunction.ts (16 errors) ====
@@ -100,4 +100,4 @@ tests/cases/compiler/awaitInNonAsyncFunction.ts(40,1): error TS1308: 'await' exp
100100
!!! error TS1103: A 'for-await-of' statement is only allowed within an async function or async generator.
101101
await null;
102102
~~~~~
103-
!!! error TS1308: 'await' expression is only allowed within an async function.
103+
!!! error TS1361: 'await' outside of an async function is only allowed at the top level of a module when '--module' is 'esnext' or 'system' and '--target' is 'es2017' or higher.

0 commit comments

Comments
 (0)