Add top-level await for esnext and system modules (#35813)

This commit is contained in:
Ron Buckton 2019-12-22 13:24:31 -08:00 committed by GitHub
parent deb5288e31
commit 114dad7f56
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
28 changed files with 274 additions and 49 deletions

View file

@ -3902,10 +3902,13 @@ namespace ts {
switch (kind) {
case SyntaxKind.AsyncKeyword:
case SyntaxKind.AwaitExpression:
// async/await is ES2017 syntax, but may be ES2018 syntax (for async generators)
// async is ES2017 syntax, but may be ES2018 syntax (for async generators)
transformFlags |= TransformFlags.AssertES2018 | TransformFlags.AssertES2017;
break;
case SyntaxKind.AwaitExpression:
// await is ES2017 syntax, but may be ES2018 syntax (for async generators)
transformFlags |= TransformFlags.AssertES2018 | TransformFlags.AssertES2017 | TransformFlags.ContainsAwait;
break;
case SyntaxKind.TypeAssertionExpression:
case SyntaxKind.AsExpression:

View file

@ -26437,21 +26437,41 @@ namespace ts {
return undefinedWideningType;
}
function isTopLevelAwait(node: AwaitExpression) {
const container = getThisContainer(node, /*includeArrowFunctions*/ true);
return isSourceFile(container);
}
function checkAwaitExpression(node: AwaitExpression): Type {
// Grammar checking
if (produceDiagnostics) {
if (!(node.flags & NodeFlags.AwaitContext)) {
// use of 'await' in non-async function
const sourceFile = getSourceFileOfNode(node);
if (!hasParseDiagnostics(sourceFile)) {
const span = getSpanOfTokenAtPosition(sourceFile, node.pos);
const diagnostic = createFileDiagnostic(sourceFile, span.start, span.length, Diagnostics.await_expression_is_only_allowed_within_an_async_function);
const func = getContainingFunction(node);
if (func && func.kind !== SyntaxKind.Constructor && (getFunctionFlags(func) & FunctionFlags.Async) === 0) {
const relatedInfo = createDiagnosticForNode(func, Diagnostics.Did_you_mean_to_mark_this_function_as_async);
addRelatedInfo(diagnostic, relatedInfo);
if (isTopLevelAwait(node)) {
const sourceFile = getSourceFileOfNode(node);
if ((moduleKind !== ModuleKind.ESNext && moduleKind !== ModuleKind.System) ||
languageVersion < ScriptTarget.ES2017 ||
!isEffectiveExternalModule(sourceFile, compilerOptions)) {
if (!hasParseDiagnostics(sourceFile)) {
const span = getSpanOfTokenAtPosition(sourceFile, node.pos);
const diagnostic = createFileDiagnostic(sourceFile, span.start, span.length,
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);
diagnostics.add(diagnostic);
}
}
}
else {
// use of 'await' in non-async function
const sourceFile = getSourceFileOfNode(node);
if (!hasParseDiagnostics(sourceFile)) {
const span = getSpanOfTokenAtPosition(sourceFile, node.pos);
const diagnostic = createFileDiagnostic(sourceFile, span.start, span.length, Diagnostics.await_expression_is_only_allowed_within_an_async_function);
const func = getContainingFunction(node);
if (func && func.kind !== SyntaxKind.Constructor && (getFunctionFlags(func) & FunctionFlags.Async) === 0) {
const relatedInfo = createDiagnosticForNode(func, Diagnostics.Did_you_mean_to_mark_this_function_as_async);
addRelatedInfo(diagnostic, relatedInfo);
}
diagnostics.add(diagnostic);
}
diagnostics.add(diagnostic);
}
}

View file

@ -1059,6 +1059,10 @@
"category": "Error",
"code": 1360
},
"'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.": {
"category": "Error",
"code": 1361
},
"The types of '{0}' are incompatible between these types.": {
"category": "Error",

View file

@ -7,6 +7,11 @@ namespace ts {
AsyncMethodsWithSuper = 1 << 0
}
const enum ContextFlags {
NonTopLevel = 1 << 0,
HasLexicalThis = 1 << 1
}
export function transformES2017(context: TransformationContext) {
const {
resumeLexicalEnvironment,
@ -41,7 +46,7 @@ namespace ts {
/** A set of node IDs for generated super accessors (variable statements). */
const substitutedSuperAccessors: boolean[] = [];
let topLevel: boolean;
let contextFlags: ContextFlags = 0;
// Save the previous transformation hooks.
const previousOnEmitNode = context.onEmitNode;
@ -58,17 +63,35 @@ namespace ts {
return node;
}
topLevel = isEffectiveStrictModeSourceFile(node, compilerOptions);
setContextFlag(ContextFlags.NonTopLevel, false);
setContextFlag(ContextFlags.HasLexicalThis, !isEffectiveStrictModeSourceFile(node, compilerOptions));
const visited = visitEachChild(node, visitor, context);
addEmitHelpers(visited, context.readEmitHelpers());
return visited;
}
function doOutsideOfTopLevel<T, U>(cb: (value: T) => U, value: T) {
if (topLevel) {
topLevel = false;
function setContextFlag(flag: ContextFlags, val: boolean) {
contextFlags = val ? contextFlags | flag : contextFlags & ~flag;
}
function inContext(flags: ContextFlags) {
return (contextFlags & flags) !== 0;
}
function inTopLevelContext() {
return !inContext(ContextFlags.NonTopLevel);
}
function inHasLexicalThisContext() {
return inContext(ContextFlags.HasLexicalThis);
}
function doWithContext<T, U>(flags: ContextFlags, cb: (value: T) => U, value: T) {
const contextFlagsToSet = flags & ~contextFlags;
if (contextFlagsToSet) {
setContextFlag(contextFlagsToSet, /*val*/ true);
const result = cb(value);
topLevel = true;
setContextFlag(contextFlagsToSet, /*val*/ false);
return result;
}
return cb(value);
@ -91,16 +114,16 @@ namespace ts {
return visitAwaitExpression(<AwaitExpression>node);
case SyntaxKind.MethodDeclaration:
return doOutsideOfTopLevel(visitMethodDeclaration, <MethodDeclaration>node);
return doWithContext(ContextFlags.NonTopLevel | ContextFlags.HasLexicalThis, visitMethodDeclaration, <MethodDeclaration>node);
case SyntaxKind.FunctionDeclaration:
return doOutsideOfTopLevel(visitFunctionDeclaration, <FunctionDeclaration>node);
return doWithContext(ContextFlags.NonTopLevel | ContextFlags.HasLexicalThis, visitFunctionDeclaration, <FunctionDeclaration>node);
case SyntaxKind.FunctionExpression:
return doOutsideOfTopLevel(visitFunctionExpression, <FunctionExpression>node);
return doWithContext(ContextFlags.NonTopLevel | ContextFlags.HasLexicalThis, visitFunctionExpression, <FunctionExpression>node);
case SyntaxKind.ArrowFunction:
return visitArrowFunction(<ArrowFunction>node);
return doWithContext(ContextFlags.NonTopLevel, visitArrowFunction, <ArrowFunction>node);
case SyntaxKind.PropertyAccessExpression:
if (capturedSuperProperties && isPropertyAccessExpression(node) && node.expression.kind === SyntaxKind.SuperKeyword) {
@ -119,7 +142,7 @@ namespace ts {
case SyntaxKind.Constructor:
case SyntaxKind.ClassDeclaration:
case SyntaxKind.ClassExpression:
return doOutsideOfTopLevel(visitDefault, node);
return doWithContext(ContextFlags.NonTopLevel | ContextFlags.HasLexicalThis, visitDefault, node);
default:
return visitEachChild(node, visitor, context);
@ -237,6 +260,10 @@ namespace ts {
* @param node The node to visit.
*/
function visitAwaitExpression(node: AwaitExpression): Expression {
// do not downlevel a top-level await as it is module syntax...
if (inTopLevelContext()) {
return visitEachChild(node, visitor, context);
}
return setOriginalNode(
setTextRange(
createYield(
@ -457,7 +484,7 @@ namespace ts {
createReturn(
createAwaiterHelper(
context,
!topLevel,
inHasLexicalThisContext(),
hasLexicalArguments,
promiseConstructor,
transformAsyncFunctionBodyWorker(<Block>node.body, statementOffset)
@ -498,7 +525,7 @@ namespace ts {
else {
const expression = createAwaiterHelper(
context,
!topLevel,
inHasLexicalThisContext(),
hasLexicalArguments,
promiseConstructor,
transformAsyncFunctionBodyWorker(node.body!)

View file

@ -26,7 +26,7 @@ namespace ts {
let enabledSubstitutions: ESNextSubstitutionFlags;
let enclosingFunctionFlags: FunctionFlags;
let enclosingSuperContainerFlags: NodeCheckFlags = 0;
let topLevel: boolean;
let hasLexicalThis: boolean;
/** Keeps track of property names accessed on super (`super.x`) within async functions. */
let capturedSuperProperties: UnderscoreEscapedMap<true>;
@ -43,7 +43,7 @@ namespace ts {
}
exportedVariableStatement = false;
topLevel = isEffectiveStrictModeSourceFile(node, compilerOptions);
hasLexicalThis = !isEffectiveStrictModeSourceFile(node, compilerOptions);
const visited = visitEachChild(node, visitor, context);
addEmitHelpers(visited, context.readEmitHelpers());
return visited;
@ -64,11 +64,11 @@ namespace ts {
return node;
}
function doOutsideOfTopLevel<T, U>(cb: (value: T) => U, value: T) {
if (topLevel) {
topLevel = false;
function doWithLexicalThis<T, U>(cb: (value: T) => U, value: T) {
if (!hasLexicalThis) {
hasLexicalThis = true;
const result = cb(value);
topLevel = true;
hasLexicalThis = false;
return result;
}
return cb(value);
@ -108,17 +108,17 @@ namespace ts {
case SyntaxKind.VoidExpression:
return visitVoidExpression(node as VoidExpression);
case SyntaxKind.Constructor:
return doOutsideOfTopLevel(visitConstructorDeclaration, node as ConstructorDeclaration);
return doWithLexicalThis(visitConstructorDeclaration, node as ConstructorDeclaration);
case SyntaxKind.MethodDeclaration:
return doOutsideOfTopLevel(visitMethodDeclaration, node as MethodDeclaration);
return doWithLexicalThis(visitMethodDeclaration, node as MethodDeclaration);
case SyntaxKind.GetAccessor:
return doOutsideOfTopLevel(visitGetAccessorDeclaration, node as GetAccessorDeclaration);
return doWithLexicalThis(visitGetAccessorDeclaration, node as GetAccessorDeclaration);
case SyntaxKind.SetAccessor:
return doOutsideOfTopLevel(visitSetAccessorDeclaration, node as SetAccessorDeclaration);
return doWithLexicalThis(visitSetAccessorDeclaration, node as SetAccessorDeclaration);
case SyntaxKind.FunctionDeclaration:
return doOutsideOfTopLevel(visitFunctionDeclaration, node as FunctionDeclaration);
return doWithLexicalThis(visitFunctionDeclaration, node as FunctionDeclaration);
case SyntaxKind.FunctionExpression:
return doOutsideOfTopLevel(visitFunctionExpression, node as FunctionExpression);
return doWithLexicalThis(visitFunctionExpression, node as FunctionExpression);
case SyntaxKind.ArrowFunction:
return visitArrowFunction(node as ArrowFunction);
case SyntaxKind.Parameter:
@ -139,7 +139,7 @@ namespace ts {
return visitEachChild(node, visitor, context);
case SyntaxKind.ClassDeclaration:
case SyntaxKind.ClassExpression:
return doOutsideOfTopLevel(visitDefault, node);
return doWithLexicalThis(visitDefault, node);
default:
return visitEachChild(node, visitor, context);
}
@ -774,7 +774,7 @@ namespace ts {
visitLexicalEnvironment(node.body!.statements, visitor, context, statementOffset)
)
),
!topLevel
hasLexicalThis
)
);

View file

@ -262,13 +262,16 @@ namespace ts {
insertStatementsAfterStandardPrologue(statements, endLexicalEnvironment());
const exportStarFunction = addExportStarIfNeeded(statements)!; // TODO: GH#18217
const modifiers = node.transformFlags & TransformFlags.ContainsAwait ?
createModifiersFromModifierFlags(ModifierFlags.Async) :
undefined;
const moduleObject = createObjectLiteral([
createPropertyAssignment("setters",
createSettersArray(exportStarFunction, dependencyGroups)
),
createPropertyAssignment("execute",
createFunctionExpression(
/*modifiers*/ undefined,
modifiers,
/*asteriskToken*/ undefined,
/*name*/ undefined,
/*typeParameters*/ undefined,

View file

@ -5608,9 +5608,10 @@ namespace ts {
ContainsBlockScopedBinding = 1 << 15,
ContainsBindingPattern = 1 << 16,
ContainsYield = 1 << 17,
ContainsHoistedDeclarationOrCompletion = 1 << 18,
ContainsDynamicImport = 1 << 19,
ContainsClassFields = 1 << 20,
ContainsAwait = 1 << 18,
ContainsHoistedDeclarationOrCompletion = 1 << 19,
ContainsDynamicImport = 1 << 20,
ContainsClassFields = 1 << 21,
// Please leave this as 1 << 29.
// It is the maximum bit we can set before we outgrow the size of a v8 small integer (SMI) on an x86 system.
@ -5636,10 +5637,10 @@ namespace ts {
OuterExpressionExcludes = HasComputedFlags,
PropertyAccessExcludes = OuterExpressionExcludes,
NodeExcludes = PropertyAccessExcludes,
ArrowFunctionExcludes = NodeExcludes | ContainsTypeScriptClassSyntax | ContainsBlockScopedBinding | ContainsYield | ContainsHoistedDeclarationOrCompletion | ContainsBindingPattern | ContainsObjectRestOrSpread,
FunctionExcludes = NodeExcludes | ContainsTypeScriptClassSyntax | ContainsLexicalThis | ContainsBlockScopedBinding | ContainsYield | ContainsHoistedDeclarationOrCompletion | ContainsBindingPattern | ContainsObjectRestOrSpread,
ConstructorExcludes = NodeExcludes | ContainsLexicalThis | ContainsBlockScopedBinding | ContainsYield | ContainsHoistedDeclarationOrCompletion | ContainsBindingPattern | ContainsObjectRestOrSpread,
MethodOrAccessorExcludes = NodeExcludes | ContainsLexicalThis | ContainsBlockScopedBinding | ContainsYield | ContainsHoistedDeclarationOrCompletion | ContainsBindingPattern | ContainsObjectRestOrSpread,
ArrowFunctionExcludes = NodeExcludes | ContainsTypeScriptClassSyntax | ContainsBlockScopedBinding | ContainsYield | ContainsAwait | ContainsHoistedDeclarationOrCompletion | ContainsBindingPattern | ContainsObjectRestOrSpread,
FunctionExcludes = NodeExcludes | ContainsTypeScriptClassSyntax | ContainsLexicalThis | ContainsBlockScopedBinding | ContainsYield | ContainsAwait | ContainsHoistedDeclarationOrCompletion | ContainsBindingPattern | ContainsObjectRestOrSpread,
ConstructorExcludes = NodeExcludes | ContainsLexicalThis | ContainsBlockScopedBinding | ContainsYield | ContainsAwait | ContainsHoistedDeclarationOrCompletion | ContainsBindingPattern | ContainsObjectRestOrSpread,
MethodOrAccessorExcludes = NodeExcludes | ContainsLexicalThis | ContainsBlockScopedBinding | ContainsYield | ContainsAwait | ContainsHoistedDeclarationOrCompletion | ContainsBindingPattern | ContainsObjectRestOrSpread,
PropertyExcludes = NodeExcludes | ContainsLexicalThis,
ClassExcludes = NodeExcludes | ContainsTypeScriptClassSyntax | ContainsComputedPropertyName,
ModuleExcludes = NodeExcludes | ContainsTypeScriptClassSyntax | ContainsLexicalThis | ContainsBlockScopedBinding | ContainsHoistedDeclarationOrCompletion,

View file

@ -13,7 +13,7 @@ tests/cases/compiler/awaitInNonAsyncFunction.ts(31,5): error TS1308: 'await' exp
tests/cases/compiler/awaitInNonAsyncFunction.ts(34,7): error TS1103: A 'for-await-of' statement is only allowed within an async function or async generator.
tests/cases/compiler/awaitInNonAsyncFunction.ts(35,5): error TS1308: 'await' expression is only allowed within an async function.
tests/cases/compiler/awaitInNonAsyncFunction.ts(39,5): error TS1103: A 'for-await-of' statement is only allowed within an async function or async generator.
tests/cases/compiler/awaitInNonAsyncFunction.ts(40,1): error TS1308: 'await' expression is only allowed within an async function.
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.
==== tests/cases/compiler/awaitInNonAsyncFunction.ts (16 errors) ====
@ -100,4 +100,4 @@ tests/cases/compiler/awaitInNonAsyncFunction.ts(40,1): error TS1308: 'await' exp
!!! error TS1103: A 'for-await-of' statement is only allowed within an async function or async generator.
await null;
~~~~~
!!! error TS1308: 'await' expression is only allowed within an async function.
!!! 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.

View file

@ -0,0 +1,9 @@
tests/cases/conformance/externalModules/topLevelAwait.ts(2,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.
==== tests/cases/conformance/externalModules/topLevelAwait.ts (1 errors) ====
export const x = 1;
await x;
~~~~~
!!! 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.

View file

@ -0,0 +1,8 @@
//// [topLevelAwait.ts]
export const x = 1;
await x;
//// [topLevelAwait.js]
export const x = 1;
await x;

View file

@ -0,0 +1,7 @@
=== tests/cases/conformance/externalModules/topLevelAwait.ts ===
export const x = 1;
>x : Symbol(x, Decl(topLevelAwait.ts, 0, 12))
await x;
>x : Symbol(x, Decl(topLevelAwait.ts, 0, 12))

View file

@ -0,0 +1,9 @@
=== tests/cases/conformance/externalModules/topLevelAwait.ts ===
export const x = 1;
>x : 1
>1 : 1
await x;
>await x : 1
>x : 1

View file

@ -0,0 +1,8 @@
//// [topLevelAwait.ts]
export const x = 1;
await x;
//// [topLevelAwait.js]
export const x = 1;
await x;

View file

@ -0,0 +1,7 @@
=== tests/cases/conformance/externalModules/topLevelAwait.ts ===
export const x = 1;
>x : Symbol(x, Decl(topLevelAwait.ts, 0, 12))
await x;
>x : Symbol(x, Decl(topLevelAwait.ts, 0, 12))

View file

@ -0,0 +1,9 @@
=== tests/cases/conformance/externalModules/topLevelAwait.ts ===
export const x = 1;
>x : 1
>1 : 1
await x;
>await x : 1
>x : 1

View file

@ -0,0 +1,9 @@
tests/cases/conformance/externalModules/topLevelAwait.ts(2,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.
==== tests/cases/conformance/externalModules/topLevelAwait.ts (1 errors) ====
export const x = 1;
await x;
~~~~~
!!! 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.

View file

@ -0,0 +1,18 @@
//// [topLevelAwait.ts]
export const x = 1;
await x;
//// [topLevelAwait.js]
System.register([], function (exports_1, context_1) {
"use strict";
var x;
var __moduleName = context_1 && context_1.id;
return {
setters: [],
execute: async function () {
exports_1("x", x = 1);
await x;
}
};
});

View file

@ -0,0 +1,7 @@
=== tests/cases/conformance/externalModules/topLevelAwait.ts ===
export const x = 1;
>x : Symbol(x, Decl(topLevelAwait.ts, 0, 12))
await x;
>x : Symbol(x, Decl(topLevelAwait.ts, 0, 12))

View file

@ -0,0 +1,9 @@
=== tests/cases/conformance/externalModules/topLevelAwait.ts ===
export const x = 1;
>x : 1
>1 : 1
await x;
>await x : 1
>x : 1

View file

@ -0,0 +1,18 @@
//// [topLevelAwait.ts]
export const x = 1;
await x;
//// [topLevelAwait.js]
System.register([], function (exports_1, context_1) {
"use strict";
var x;
var __moduleName = context_1 && context_1.id;
return {
setters: [],
execute: async function () {
exports_1("x", x = 1);
await x;
}
};
});

View file

@ -0,0 +1,7 @@
=== tests/cases/conformance/externalModules/topLevelAwait.ts ===
export const x = 1;
>x : Symbol(x, Decl(topLevelAwait.ts, 0, 12))
await x;
>x : Symbol(x, Decl(topLevelAwait.ts, 0, 12))

View file

@ -0,0 +1,9 @@
=== tests/cases/conformance/externalModules/topLevelAwait.ts ===
export const x = 1;
>x : 1
>1 : 1
await x;
>await x : 1
>x : 1

View file

@ -0,0 +1,11 @@
tests/cases/conformance/externalModules/topLevelAwaitNonModule.ts(1,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.
tests/cases/conformance/externalModules/topLevelAwaitNonModule.ts(1,7): error TS2304: Cannot find name 'x'.
==== tests/cases/conformance/externalModules/topLevelAwaitNonModule.ts (2 errors) ====
await x;
~~~~~
!!! 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.
~
!!! error TS2304: Cannot find name 'x'.

View file

@ -0,0 +1,6 @@
//// [topLevelAwaitNonModule.ts]
await x;
//// [topLevelAwaitNonModule.js]
await x;

View file

@ -0,0 +1,4 @@
=== tests/cases/conformance/externalModules/topLevelAwaitNonModule.ts ===
await x;
No type information for this code.
No type information for this code.

View file

@ -0,0 +1,5 @@
=== tests/cases/conformance/externalModules/topLevelAwaitNonModule.ts ===
await x;
>await x : any
>x : any

View file

@ -0,0 +1,4 @@
// @target: es2015,es2017
// @module: esnext,system
export const x = 1;
await x;

View file

@ -0,0 +1,3 @@
// @target: esnext
// @module: esnext
await x;