Skip to content

Reparse top level 'await' in modules #39084

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jun 19, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 33 additions & 10 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28210,7 +28210,7 @@ namespace ts {
return undefinedWideningType;
}

function isTopLevelAwait(node: AwaitExpression) {
function isInTopLevelContext(node: Node) {
const container = getThisContainer(node, /*includeArrowFunctions*/ true);
return isSourceFile(container);
}
Expand All @@ -28219,7 +28219,7 @@ namespace ts {
// Grammar checking
if (produceDiagnostics) {
if (!(node.flags & NodeFlags.AwaitContext)) {
if (isTopLevelAwait(node)) {
if (isInTopLevelContext(node)) {
const sourceFile = getSourceFileOfNode(node);
if (!hasParseDiagnostics(sourceFile)) {
let span: TextSpan | undefined;
Expand Down Expand Up @@ -33533,7 +33533,7 @@ namespace ts {
function checkThrowStatement(node: ThrowStatement) {
// Grammar checking
if (!checkGrammarStatementInAmbientContext(node)) {
if (node.expression === undefined) {
if (isIdentifier(node.expression) && !node.expression.escapedText) {
grammarErrorAfterFirstToken(node, Diagnostics.Line_break_not_permitted_here);
}
}
Expand Down Expand Up @@ -34802,6 +34802,7 @@ namespace ts {
}

function checkImportBinding(node: ImportEqualsDeclaration | ImportClause | NamespaceImport | ImportSpecifier) {
checkGrammarAwaitIdentifier(node.name);
checkCollisionWithRequireExportsInGeneratedCode(node, node.name!);
checkCollisionWithGlobalPromiseInGeneratedCode(node, node.name!);
checkAliasSymbol(node);
Expand Down Expand Up @@ -37545,17 +37546,32 @@ namespace ts {
return false;
}

function checkGrammarAwaitIdentifier(name: Identifier | undefined): boolean {
if (name && isIdentifier(name) && name.originalKeywordKind === SyntaxKind.AwaitKeyword && isInTopLevelContext(name.parent)) {
const file = getSourceFileOfNode(name);
if (!file.isDeclarationFile && isExternalModule(file)) {
return grammarErrorOnNode(name, Diagnostics.Identifier_expected_0_is_a_reserved_word_at_the_top_level_of_a_module, idText(name));
}
}
return false;
}

function checkGrammarFunctionLikeDeclaration(node: FunctionLikeDeclaration | MethodSignature): boolean {
// Prevent cascading error by short-circuit
const file = getSourceFileOfNode(node);
return checkGrammarDecoratorsAndModifiers(node) || checkGrammarTypeParameterList(node.typeParameters, file) ||
checkGrammarParameterList(node.parameters) || checkGrammarArrowFunction(node, file) ||
return checkGrammarDecoratorsAndModifiers(node) ||
checkGrammarTypeParameterList(node.typeParameters, file) ||
(isFunctionDeclaration(node) && checkGrammarAwaitIdentifier(node.name)) ||
checkGrammarParameterList(node.parameters) ||
checkGrammarArrowFunction(node, file) ||
(isFunctionLikeDeclaration(node) && checkGrammarForUseStrictSimpleParameterList(node));
}

function checkGrammarClassLikeDeclaration(node: ClassLikeDeclaration): boolean {
const file = getSourceFileOfNode(node);
return checkGrammarClassDeclarationHeritageClauses(node) || checkGrammarTypeParameterList(node.typeParameters, file);
return (isClassDeclaration(node) && checkGrammarAwaitIdentifier(node.name)) ||
checkGrammarClassDeclarationHeritageClauses(node) ||
checkGrammarTypeParameterList(node.typeParameters, file);
}

function checkGrammarArrowFunction(node: Node, file: SourceFile): boolean {
Expand Down Expand Up @@ -38195,11 +38211,15 @@ namespace ts {
if (node.propertyName) {
return grammarErrorOnNode(node.name, Diagnostics.A_rest_element_cannot_have_a_property_name);
}
}

if (node.initializer) {
// Error on equals token which immediately precedes the initializer
return grammarErrorAtPos(node, node.initializer.pos - 1, 1, Diagnostics.A_rest_element_cannot_have_an_initializer);
}
if (isIdentifier(node.name) && checkGrammarAwaitIdentifier(node.name)) {
return true;
}

if (node.dotDotDotToken && node.initializer) {
// Error on equals token which immediately precedes the initializer
return grammarErrorAtPos(node, node.initializer.pos - 1, 1, Diagnostics.A_rest_element_cannot_have_an_initializer);
}
}

Expand Down Expand Up @@ -38260,6 +38280,9 @@ namespace ts {
}
}
}
if (isIdentifier(node.name) && checkGrammarAwaitIdentifier(node.name)) {
return true;
}

if (node.exclamationToken && (node.parent.parent.kind !== SyntaxKind.VariableStatement || !node.type || node.initializer || node.flags & NodeFlags.Ambient)) {
return grammarErrorOnNode(node.exclamationToken, Diagnostics.Definite_assignment_assertions_can_only_be_used_along_with_a_type_annotation);
Expand Down
5 changes: 5 additions & 0 deletions src/compiler/diagnosticMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -867,6 +867,11 @@
"category": "Error",
"code": 1261
},
"Identifier expected. '{0}' is a reserved word at the top-level of a module.": {
"category": "Error",
"code": 1262
},

"'with' statements are not allowed in an async function block.": {
"category": "Error",
"code": 1300
Expand Down
71 changes: 61 additions & 10 deletions src/compiler/factory/nodeFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -556,8 +556,29 @@ namespace ts {
decorators,
modifiers
);
node.name = asName(name);
node.transformFlags |= propagateChildFlags(node.name);
name = asName(name);
node.name = name;

// The PropertyName of a member is allowed to be `await`.
// We don't need to exclude `await` for type signatures since types
// don't propagate child flags.
if (name) {
switch (node.kind) {
case SyntaxKind.MethodDeclaration:
case SyntaxKind.GetAccessor:
case SyntaxKind.SetAccessor:
case SyntaxKind.PropertyDeclaration:
case SyntaxKind.PropertyAssignment:
if (isIdentifier(name)) {
node.transformFlags |= propagateIdentifierNameFlags(name);
break;
}
// fall through
default:
node.transformFlags |= propagateChildFlags(name);
break;
}
}
return node;
}

Expand Down Expand Up @@ -631,7 +652,7 @@ namespace ts {
type
);
node.body = body;
node.transformFlags |= propagateChildFlags(node.body);
node.transformFlags |= propagateChildFlags(node.body) & ~TransformFlags.ContainsPossibleTopLevelAwait;
if (!body) node.transformFlags |= TransformFlags.ContainsTypeScript;
return node;
}
Expand Down Expand Up @@ -824,6 +845,9 @@ namespace ts {
// NOTE: we do not use `setChildren` here because typeArguments in an identifier do not contribute to transformations
node.typeArguments = createNodeArray(typeArguments);
}
if (node.originalKeywordKind === SyntaxKind.AwaitKeyword) {
node.transformFlags |= TransformFlags.ContainsPossibleTopLevelAwait;
}
return node;
}

Expand Down Expand Up @@ -1013,7 +1037,7 @@ namespace ts {
node.right = asName(right);
node.transformFlags |=
propagateChildFlags(node.left) |
propagateChildFlags(node.right);
propagateIdentifierNameFlags(node.right);
return node;
}

Expand Down Expand Up @@ -2028,9 +2052,13 @@ namespace ts {
node.propertyName = asName(propertyName);
node.dotDotDotToken = dotDotDotToken;
node.transformFlags |=
propagateChildFlags(node.propertyName) |
propagateChildFlags(node.dotDotDotToken) |
TransformFlags.ContainsES2015;
if (node.propertyName) {
node.transformFlags |= isIdentifier(node.propertyName) ?
propagateIdentifierNameFlags(node.propertyName) :
propagateChildFlags(node.propertyName);
}
if (dotDotDotToken) node.transformFlags |= TransformFlags.ContainsRestOrSpread;
return node;
}
Expand Down Expand Up @@ -2094,7 +2122,9 @@ namespace ts {
node.name = asName(name);
node.transformFlags =
propagateChildFlags(node.expression) |
propagateChildFlags(node.name);
(isIdentifier(node.name) ?
propagateIdentifierNameFlags(node.name) :
propagateChildFlags(node.name));
if (isSuperKeyword(expression)) {
// super method calls require a lexical 'this'
// super method calls require 'super' hoisting in ES2017 and ES2018 async functions and async generators
Expand Down Expand Up @@ -2124,10 +2154,12 @@ namespace ts {
node.questionDotToken = questionDotToken;
node.name = asName(name);
node.transformFlags |=
TransformFlags.ContainsES2020 |
propagateChildFlags(node.expression) |
propagateChildFlags(node.questionDotToken) |
propagateChildFlags(node.name) |
TransformFlags.ContainsES2020;
(isIdentifier(node.name) ?
propagateIdentifierNameFlags(node.name) :
propagateChildFlags(node.name));
return node;
}

Expand Down Expand Up @@ -3556,6 +3588,7 @@ namespace ts {
node.transformFlags |=
propagateChildrenFlags(node.members) |
TransformFlags.ContainsTypeScript;
node.transformFlags &= ~TransformFlags.ContainsPossibleTopLevelAwait; // Enum declarations cannot contain `await`
return node;
}

Expand Down Expand Up @@ -3599,6 +3632,7 @@ namespace ts {
propagateChildFlags(node.body) |
TransformFlags.ContainsTypeScript;
}
node.transformFlags &= ~TransformFlags.ContainsPossibleTopLevelAwait; // Module declarations cannot contain `await`.
return node;
}

Expand Down Expand Up @@ -3683,6 +3717,7 @@ namespace ts {
node.moduleReference = moduleReference;
node.transformFlags |= propagateChildFlags(node.moduleReference);
if (!isExternalModuleReference(node.moduleReference)) node.transformFlags |= TransformFlags.ContainsTypeScript;
node.transformFlags &= ~TransformFlags.ContainsPossibleTopLevelAwait; // Import= declaration is always parsed in an Await context
return node;
}

Expand Down Expand Up @@ -3719,6 +3754,7 @@ namespace ts {
node.transformFlags |=
propagateChildFlags(node.importClause) |
propagateChildFlags(node.moduleSpecifier);
node.transformFlags &= ~TransformFlags.ContainsPossibleTopLevelAwait; // always parsed in an Await context
return node;
}

Expand Down Expand Up @@ -3750,6 +3786,7 @@ namespace ts {
if (isTypeOnly) {
node.transformFlags |= TransformFlags.ContainsTypeScript;
}
node.transformFlags &= ~TransformFlags.ContainsPossibleTopLevelAwait; // always parsed in an Await context
return node;
}

Expand All @@ -3767,6 +3804,7 @@ namespace ts {
const node = createBaseNode<NamespaceImport>(SyntaxKind.NamespaceImport);
node.name = name;
node.transformFlags |= propagateChildFlags(node.name);
node.transformFlags &= ~TransformFlags.ContainsPossibleTopLevelAwait; // always parsed in an Await context
return node;
}

Expand All @@ -3784,6 +3822,7 @@ namespace ts {
node.transformFlags |=
propagateChildFlags(node.name) |
TransformFlags.ContainsESNext;
node.transformFlags &= ~TransformFlags.ContainsPossibleTopLevelAwait; // always parsed in an Await context
return node;
}

Expand All @@ -3799,6 +3838,7 @@ namespace ts {
const node = createBaseNode<NamedImports>(SyntaxKind.NamedImports);
node.elements = createNodeArray(elements);
node.transformFlags |= propagateChildrenFlags(node.elements);
node.transformFlags &= ~TransformFlags.ContainsPossibleTopLevelAwait; // always parsed in an Await context
return node;
}

Expand All @@ -3817,6 +3857,7 @@ namespace ts {
node.transformFlags |=
propagateChildFlags(node.propertyName) |
propagateChildFlags(node.name);
node.transformFlags &= ~TransformFlags.ContainsPossibleTopLevelAwait; // always parsed in an Await context
return node;
}

Expand Down Expand Up @@ -3845,6 +3886,7 @@ namespace ts {
? parenthesizerRules().parenthesizeRightSideOfBinary(SyntaxKind.EqualsToken, /*leftSide*/ undefined, expression)
: parenthesizerRules().parenthesizeExpressionOfExportDefault(expression);
node.transformFlags |= propagateChildFlags(node.expression);
node.transformFlags &= ~TransformFlags.ContainsPossibleTopLevelAwait; // always parsed in an Await context
return node;
}

Expand Down Expand Up @@ -3881,6 +3923,7 @@ namespace ts {
node.transformFlags |=
propagateChildFlags(node.exportClause) |
propagateChildFlags(node.moduleSpecifier);
node.transformFlags &= ~TransformFlags.ContainsPossibleTopLevelAwait; // always parsed in an Await context
return node;
}

Expand All @@ -3907,6 +3950,7 @@ namespace ts {
const node = createBaseNode<NamedExports>(SyntaxKind.NamedExports);
node.elements = createNodeArray(elements);
node.transformFlags |= propagateChildrenFlags(node.elements);
node.transformFlags &= ~TransformFlags.ContainsPossibleTopLevelAwait; // always parsed in an Await context
return node;
}

Expand All @@ -3925,6 +3969,7 @@ namespace ts {
node.transformFlags |=
propagateChildFlags(node.propertyName) |
propagateChildFlags(node.name);
node.transformFlags &= ~TransformFlags.ContainsPossibleTopLevelAwait; // always parsed in an Await context
return node;
}

Expand Down Expand Up @@ -3955,6 +4000,7 @@ namespace ts {
const node = createBaseNode<ExternalModuleReference>(SyntaxKind.ExternalModuleReference);
node.expression = expression;
node.transformFlags |= propagateChildFlags(node.expression);
node.transformFlags &= ~TransformFlags.ContainsPossibleTopLevelAwait; // always parsed in an Await context
return node;
}

Expand Down Expand Up @@ -5777,14 +5823,19 @@ namespace ts {
return tokenValue;
}

function propagatePropertyNameFlags(node: PropertyName, transformFlags: TransformFlags) {
function propagateIdentifierNameFlags(node: Identifier) {
// An IdentifierName is allowed to be `await`
return propagateChildFlags(node) & ~TransformFlags.ContainsPossibleTopLevelAwait;
}

function propagatePropertyNameFlagsOfChild(node: PropertyName, transformFlags: TransformFlags) {
return transformFlags | (node.transformFlags & TransformFlags.PropertyNamePropagatingFlags);
}

function propagateChildFlags(child: Node | undefined): TransformFlags {
if (!child) return TransformFlags.None;
const childFlags = child.transformFlags & ~getTransformFlagsSubtreeExclusions(child.kind);
return isNamedDeclaration(child) && isPropertyName(child.name) ? propagatePropertyNameFlags(child.name, childFlags) : childFlags;
return isNamedDeclaration(child) && isPropertyName(child.name) ? propagatePropertyNameFlagsOfChild(child.name, childFlags) : childFlags;
}

function propagateChildrenFlags(children: NodeArray<Node> | undefined): TransformFlags {
Expand Down
5 changes: 5 additions & 0 deletions src/compiler/factory/utilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -811,6 +811,11 @@ namespace ts {
|| kind === SyntaxKind.ExportDeclaration;
}

/* @internal */
export function isExportModifier(node: Modifier): node is ExportKeyword {
return node.kind === SyntaxKind.ExportKeyword;
}

/* @internal */
export function isAsyncModifier(node: Modifier): node is AsyncKeyword {
return node.kind === SyntaxKind.AsyncKeyword;
Expand Down
Loading