From d19a200750ab16720d0a383b3cc1c85d3c38ddb4 Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Tue, 5 May 2015 17:57:21 -0700 Subject: [PATCH] Added types, scan, parse, and check for async functions --- src/compiler/checker.ts | 447 +++++++++++++++++- .../diagnosticInformationMap.generated.ts | 29 +- src/compiler/diagnosticMessages.json | 101 +++- src/compiler/parser.ts | 425 +++++++++++------ src/compiler/scanner.ts | 2 + src/compiler/types.ts | 54 ++- src/compiler/utilities.ts | 19 + src/lib/core.d.ts | 12 + src/lib/es6.d.ts | 10 - 9 files changed, 906 insertions(+), 193 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 04766f22c4e..13f527bd9f8 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -89,7 +89,7 @@ module ts { let nullType = createIntrinsicType(TypeFlags.Null | TypeFlags.ContainsUndefinedOrNull, "null"); let unknownType = createIntrinsicType(TypeFlags.Any, "unknown"); let resolvingType = createIntrinsicType(TypeFlags.Any, "__resolving__"); - + let emptyObjectType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, undefined, undefined); let anyFunctionType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, undefined, undefined); let noConstraintType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, undefined, undefined); @@ -101,6 +101,8 @@ module ts { let globalArraySymbol: Symbol; let globalESSymbolConstructorSymbol: Symbol; + + let getGlobalPromiseSymbol: () => Symbol; let globalObjectType: ObjectType; let globalFunctionType: ObjectType; @@ -118,6 +120,11 @@ module ts { let getGlobalParameterDecoratorType: () => ObjectType; let getGlobalPropertyDecoratorType: () => ObjectType; let getGlobalMethodDecoratorType: () => ObjectType; + let getGlobalPromiseType: () => ObjectType; + let getGlobalPromiseLikeType: () => ObjectType; + let getInstantiatedGlobalPromiseLikeType: () => ObjectType; + let getGlobalPromiseConstructorLikeType: () => ObjectType; + let getGlobalThenableType: () => ObjectType; let tupleTypes: Map = {}; let unionTypes: Map = {}; @@ -125,11 +132,14 @@ module ts { let emitExtends = false; let emitDecorate = false; let emitParam = false; + let emitAwaiter = false; + let emitGenerator = false; let mergedSymbols: Symbol[] = []; let symbolLinks: SymbolLinks[] = []; let nodeLinks: NodeLinks[] = []; let potentialThisCollisions: Node[] = []; + var potentialArgumentsCollisions: Node[] = []; let diagnostics = createDiagnosticCollection(); @@ -5146,9 +5156,11 @@ module ts { case SyntaxKind.ParenthesizedExpression: case SyntaxKind.PrefixUnaryExpression: case SyntaxKind.DeleteExpression: + case SyntaxKind.AwaitExpression: case SyntaxKind.TypeOfExpression: case SyntaxKind.VoidExpression: case SyntaxKind.PostfixUnaryExpression: + case SyntaxKind.YieldExpression: case SyntaxKind.ConditionalExpression: case SyntaxKind.SpreadElementExpression: case SyntaxKind.Block: @@ -5406,8 +5418,14 @@ module ts { // will be bound to non-arrow function that contain this arrow function. This results in inconsistent behavior. // To avoid that we will give an error to users if they use arguments objects in arrow function so that they // can explicitly bound arguments objects - if (symbol === argumentsSymbol && getContainingFunction(node).kind === SyntaxKind.ArrowFunction && languageVersion < ScriptTarget.ES6) { - error(node, Diagnostics.The_arguments_object_cannot_be_referenced_in_an_arrow_function_in_ES3_and_ES5_Consider_using_a_standard_function_expression); + let container = getContainingFunction(node); + if (symbol === argumentsSymbol) { + if (container.kind === SyntaxKind.ArrowFunction && languageVersion < ScriptTarget.ES6) { + error(node, Diagnostics.The_arguments_object_cannot_be_referenced_in_an_arrow_function_in_ES3_and_ES5_Consider_using_a_standard_function_expression); + } + else if (node.parserContextFlags & ParserContextFlags.Await) { + captureLexicalArguments(node, container); + } } if (symbol.flags & SymbolFlags.Alias && !isInTypeQuery(node) && !isConstEnumOrConstEnumOnlyModule(resolveAlias(symbol))) { @@ -5416,6 +5434,7 @@ module ts { checkCollisionWithCapturedSuperVariable(node, node); checkCollisionWithCapturedThisVariable(node, node); + checkCollisionWithCapturedArgumentsVariable(node, node); checkBlockScopedBindingCapturedInLoop(node, symbol); return getNarrowedTypeOfSymbol(getExportSymbolOfValueSymbolIfExported(symbol), node); @@ -5497,6 +5516,11 @@ module ts { // When targeting es6, arrow function lexically bind "this" so we do not need to do the work of binding "this" in emitted code needToCaptureLexicalThis = (languageVersion < ScriptTarget.ES6); } + + if (node.parserContextFlags & ParserContextFlags.Await) { + // if 'this' is part of an async function, we will need to capture 'this' + needToCaptureLexicalThis = true; + } switch (container.kind) { case SyntaxKind.ModuleDeclaration: @@ -5537,6 +5561,14 @@ module ts { return anyType; } + function captureLexicalArguments(node: Node, container: Node): void { + if (node.parent.kind !== SyntaxKind.Parameter) { + getNodeLinks(node).flags |= NodeCheckFlags.LexicalArguments; + } + + getNodeLinks(container).flags |= NodeCheckFlags.CaptureArguments; + } + function isInConstructorArgumentInitializer(node: Node, constructorDecl: Node): boolean { for (let n = node; n && n !== constructorDecl; n = n.parent) { if (n.kind === SyntaxKind.Parameter) { @@ -7220,22 +7252,42 @@ module ts { links.type = instantiateType(getTypeOfSymbol(lastOrUndefined(context.parameters)), mapper); } } + + function createPromiseType(promisedType: Type, location: Node): Type { + // creates a `Promise` type where `T` is the promisedType argument + let globalPromiseType = getGlobalPromiseType(); + if (globalPromiseType !== emptyObjectType) { + // if the promised type is itself a promise, get the underlying type; otherwise, fallback to the promised type + promisedType = getAwaitedType(promisedType); + return createTypeReference(globalPromiseType, [promisedType]); + } + + error(location, Diagnostics.An_async_function_or_method_must_have_a_valid_awaitable_return_type); + return unknownType; + } function getReturnTypeFromBody(func: FunctionLikeDeclaration, contextualMapper?: TypeMapper): Type { let contextualSignature = getContextualSignatureForFunctionLikeDeclaration(func); if (!func.body) { return unknownType; } + + let isAsync = isAsyncFunctionLike(func); let type: Type; if (func.body.kind !== SyntaxKind.Block) { - type = checkExpressionCached(func.body, contextualMapper); + type = isAsync + ? checkAwaitedExpressionCached(func.body, contextualMapper) + : checkExpressionCached(func.body, contextualMapper); } else { // Aggregate the types of expressions within all the return statements. let types = checkAndAggregateReturnExpressionTypes(func.body, contextualMapper); if (types.length === 0) { - return voidType; + return isAsync + ? createPromiseType(voidType, func) + : voidType; } + // When return statements are contextually typed we allow the return type to be a union type. Otherwise we require the // return expressions to have a best common supertype. type = contextualSignature ? getUnionType(types) : getCommonSupertype(types); @@ -7247,17 +7299,23 @@ module ts { if (!contextualSignature) { reportErrorsFromWidening(func, type); } - return getWidenedType(type); + + let widenedType = getWidenedType(type); + return isAsync + ? createPromiseType(widenedType, func) + : type; } /// Returns a set of types relating to every return expression relating to a function block. - function checkAndAggregateReturnExpressionTypes(body: Block, contextualMapper?: TypeMapper): Type[] { + function checkAndAggregateReturnExpressionTypes(body: Block, contextualMapper?: TypeMapper, isAsync?: boolean): Type[] { let aggregatedTypes: Type[] = []; forEachReturnStatement(body, returnStatement => { let expr = returnStatement.expression; if (expr) { - let type = checkExpressionCached(expr, contextualMapper); + let type = isAsync + ? checkAwaitedExpressionCached(expr, contextualMapper) + : checkExpressionCached(expr, contextualMapper); if (!contains(aggregatedTypes, type)) { aggregatedTypes.push(type); } @@ -7327,6 +7385,12 @@ module ts { if (contextualMapper === identityMapper && isContextSensitive(node)) { return anyFunctionType; } + + let isAsync = isAsyncFunctionLike(node); + if (isAsync) { + emitAwaiter = true; + } + let links = getNodeLinks(node); let type = getTypeOfSymbol(node.symbol); // Check if function expression is contextually typed and assign parameter types if so @@ -7357,6 +7421,7 @@ module ts { if (produceDiagnostics && node.kind !== SyntaxKind.MethodDeclaration && node.kind !== SyntaxKind.MethodSignature) { checkCollisionWithCapturedSuperVariable(node, (node).name); checkCollisionWithCapturedThisVariable(node, (node).name); + checkCollisionWithCapturedArgumentsVariable(node, (node).name); } return type; @@ -7364,8 +7429,21 @@ module ts { function checkFunctionExpressionOrObjectLiteralMethodBody(node: FunctionExpression | MethodDeclaration) { Debug.assert(node.kind !== SyntaxKind.MethodDeclaration || isObjectLiteralMethod(node)); - if (node.type && !node.asteriskToken) { - checkIfNonVoidFunctionHasReturnExpressionsOrSingleThrowStatment(node, getTypeFromTypeNode(node.type)); + + let isAsync = isAsyncFunctionLike(node); + let returnType = node.type && getTypeFromTypeNode(node.type); + let promisedType: Type; + if (returnType && isAsync) { + // From within an async function you can return either a non-promise value or a promise. Any + // Promise/A+ compatible implementation will always assimilate any foreign promise, so we + // should not be checking assignability of a promise to the return type. Instead, we need to + // check assignability of the awaited type of the concise body against the promised type of + // its return type annotation. + promisedType = checkAsyncFunctionReturnType(node, returnType); + } + + if (returnType && !node.asteriskToken) { + checkIfNonVoidFunctionHasReturnExpressionsOrSingleThrowStatment(node, isAsync ? promisedType : returnType); } if (node.body) { @@ -7374,8 +7452,11 @@ module ts { } else { let exprType = checkExpression(node.body); - if (node.type) { - checkTypeAssignableTo(exprType, getTypeFromTypeNode(node.type), node.body, /*headMessage*/ undefined); + if (returnType) { + checkTypeAssignableTo( + isAsync ? getAwaitedType(exprType) : exprType, + isAsync ? promisedType : returnType, + node.body); } checkFunctionExpressionBodies(node.body); } @@ -7490,6 +7571,22 @@ module ts { return undefinedType; } + function checkAwaitExpression(node: AwaitExpression): Type { + // Grammar checking + if (!(node.parserContextFlags & ParserContextFlags.Await)) { + var parameter = getContainingParameter(node); + if (parameter && parameter.parserContextFlags & ParserContextFlags.Await) { + grammarErrorAfterFirstToken(node, Diagnostics._0_expression_is_not_allowed_in_an_initializer, "await"); + } + else { + grammarErrorOnFirstToken(node, Diagnostics.await_expression_must_be_contained_within_an_async_function); + } + } + + var operandType = checkExpression(node.expression); + return getAwaitedType(operandType); + } + function checkPrefixUnaryExpression(node: PrefixUnaryExpression): Type { // Grammar checking // The identifier eval or arguments may not appear as the LeftHandSideExpression of an @@ -7897,7 +7994,13 @@ module ts { function checkYieldExpression(node: YieldExpression): void { // Grammar checking if (!(node.parserContextFlags & ParserContextFlags.Yield)) { - grammarErrorOnFirstToken(node, Diagnostics.yield_expression_must_be_contained_within_a_generator_declaration); + let parameter = getContainingParameter(node); + if (parameter && (parameter.parserContextFlags & ParserContextFlags.GeneratorParameter)) { + grammarErrorAfterFirstToken(node, Diagnostics._0_expression_is_not_allowed_in_an_initializer, "yield"); + } + else { + grammarErrorOnFirstToken(node, Diagnostics.yield_expression_must_be_contained_within_a_generator_declaration); + } } else { grammarErrorOnFirstToken(node, Diagnostics.yield_expressions_are_not_currently_supported); @@ -7931,6 +8034,15 @@ module ts { node.contextualType = saveContextualType; return result; } + + function checkAwaitedExpressionCached(node: Expression, contextualMapper?: TypeMapper): Type { + let links = getNodeLinks(node); + if (!links.resolvedAwaitedType) { + links.resolvedAwaitedType = getAwaitedType(checkExpressionCached(node, contextualMapper)); + } + + return links.resolvedAwaitedType; + } function checkExpressionCached(node: Expression, contextualMapper?: TypeMapper): Type { let links = getNodeLinks(node); @@ -8078,6 +8190,8 @@ module ts { return checkDeleteExpression(node); case SyntaxKind.VoidExpression: return checkVoidExpression(node); + case SyntaxKind.AwaitExpression: + return checkAwaitExpression(node); case SyntaxKind.PrefixUnaryExpression: return checkPrefixUnaryExpression(node); case SyntaxKind.PostfixUnaryExpression: @@ -8166,6 +8280,7 @@ module ts { } if (produceDiagnostics) { + checkCollisionWithAwaiterVariablesInGeneratedCode(node, node.name); checkCollisionWithArgumentsInGeneratedCode(node); if (compilerOptions.noImplicitAny && !node.type) { switch (node.kind) { @@ -8177,6 +8292,22 @@ module ts { break; } } + if (isAsyncFunctionLike(node)) { + var promiseConstructor = getPromiseConstructor(node); + if (promiseConstructor) { + var promiseIdentifier = getFirstIdentifier(promiseConstructor); + var promiseName = promiseIdentifier.text; + var typeSymbol = resolveName(node, promiseName, SymbolFlags.Type | SymbolFlags.Module, undefined, undefined); + var valueSymbol = resolveName(node, promiseName, SymbolFlags.Value, undefined, undefined); + if (typeSymbol !== valueSymbol) { + var valueLinks = getNodeLinks(valueSymbol.valueDeclaration); + if (!(valueLinks.flags & NodeCheckFlags.PromiseCollision)) { + valueLinks.flags |= NodeCheckFlags.PromiseCollision; + error(valueSymbol.valueDeclaration, Diagnostics.Duplicate_identifier_0_Compiler_uses_declaration_1_to_support_async_functions, promiseName, getTextOfNode(promiseConstructor)); + } + } + } + } } checkSpecializedSignatureDeclaration(node); @@ -8772,6 +8903,113 @@ module ts { } } + function getPromisedType(type: Type): Type { + // the "promised type" of a type is the type of the "value" argument of the "onfulfilled" callback. + let globalPromiseLikeType = getInstantiatedGlobalPromiseLikeType(); + if (globalPromiseLikeType !== emptyObjectType && isTypeAssignableTo(type, globalPromiseLikeType)) { + let awaitedTypes: Type[] = []; + let thenProp = getPropertyOfType(type, "then"); + let thenType = getTypeOfSymbol(thenProp); + let thenSignatures = getSignaturesOfType(thenType, SignatureKind.Call); + for (let thenSignature of thenSignatures) { + thenSignature = getErasedSignature(thenSignature); + let onfulfilledParameterType = getTypeAtPosition(thenSignature, 0); + let onfulfilledParameterSignatures = getSignaturesOfType(onfulfilledParameterType, SignatureKind.Call); + for (let onfulfilledParameterSignature of onfulfilledParameterSignatures) { + let valueParameterType = getTypeAtPosition(onfulfilledParameterSignature, 0); + if (valueParameterType !== type) { + awaitedTypes.push(valueParameterType); + } + } + } + + return getUnionType(awaitedTypes); + } + + return emptyObjectType; + } + + function getAwaitedType(type: Type): Type { + // The "awaited type" of an expression is its "promised type" if the expression is a `Promise`; otherwise, it is the type of the expression. + + let promisedType = getPromisedType(type); + if (promisedType === emptyObjectType) { + return type; + } + + // if we have a bad actor in the form of a promise whose promised type is the same promise, return the empty type. + // if this were the actual case in the JavaScript, this Promise would never resolve. + if (promisedType === type) { + return emptyObjectType; + } + + // unwrap any nested promises + let seen: boolean[]; + while (true) { + let nestedPromisedType = getPromisedType(promisedType); + if (nestedPromisedType === emptyObjectType) { + // if this could not be unwrapped further, return the promised type + return promisedType; + } + + if (!seen) { + // `seen` keeps track of types we've tried to await to avoid cycles + seen = []; + seen[type.id] = true; + seen[promisedType.id] = true; + } + else if (seen[nestedPromisedType.id]) { + // if we've already seen this type, this is a promise that would never resolve. As above, we return the empty type. + return emptyObjectType; + } + + seen[nestedPromisedType.id] = true; + promisedType = nestedPromisedType; + } + + return promisedType; + + // if we didn't get a promised type, check the type does not have a callable then member. + if (isTypeAssignableTo(type, getGlobalThenableType())) { + error(null, Diagnostics.Type_for_await_does_not_have_a_valid_callable_then_member); + return emptyObjectType; + } + + // if the type was not a "promise" or a "thenable", return the type. + return type; + } + + function checkAsyncFunctionReturnType(node: SignatureDeclaration, returnType: Type): Type { + // This checks that an async function has a valid Promise-compatible return type, and returns the *awaited type* of the promise. + // An async function has a valid Promise-compatible return type if the resolved value of the return type has a construct + // signature that takes in an `initializer` function that in turn supplies a `resolve` function as one of its arguments + // and results in an object with a callable `then` signature. + let globalPromiseConstructorLikeType = getGlobalPromiseConstructorLikeType(); + if (globalPromiseConstructorLikeType !== emptyObjectType) { + if (!returnType) { + returnType = getReturnTypeOfSignature(getSignatureFromDeclaration(node)); + } + + // get the constructor type of the return type + var declaredType = returnType.symbol ? getTypeOfSymbol(returnType.symbol) : emptyObjectType; + if (isTypeAssignableTo(declaredType, globalPromiseConstructorLikeType)) { + var promisedType = getPromisedType(returnType); + if (promisedType !== emptyObjectType) { + // unwrap the promised type + var promiseConstructor = getPromiseConstructor(node); + if (promiseConstructor) { + emitAwaiter = true; + checkExpressionOrQualifiedName(promiseConstructor); + return getAwaitedType(promisedType); + } + } + } + } + + error(node, ts.Diagnostics.An_async_function_or_method_must_have_a_valid_awaitable_return_type); + return emptyObjectType; + } + /** Check a decorator */ function checkDecorator(node: Decorator): void { let expression: Expression = node.expression; @@ -8904,6 +9142,7 @@ module ts { checkCollisionWithCapturedSuperVariable(node, node.name); checkCollisionWithCapturedThisVariable(node, node.name); + checkCollisionWithCapturedArgumentsVariable(node, node.name); checkCollisionWithRequireExportsInGeneratedCode(node, node.name); } } @@ -8946,7 +9185,14 @@ module ts { checkSourceElement(node.body); if (node.type && !isAccessor(node.kind) && !node.asteriskToken) { - checkIfNonVoidFunctionHasReturnExpressionsOrSingleThrowStatment(node, getTypeFromTypeNode(node.type)); + let returnType = getTypeFromTypeNode(node.type); + let isAsync = isAsyncFunctionLike(node); + let promisedType: Type; + if (isAsync) { + promisedType = checkAsyncFunctionReturnType(node, returnType); + } + + checkIfNonVoidFunctionHasReturnExpressionsOrSingleThrowStatment(node, isAsync ? promisedType : returnType); } // Report an implicit any error if there is no body, no explicit return type, and node is not a private method @@ -9016,6 +9262,12 @@ module ts { } } + function checkCollisionWithCapturedArgumentsVariable(node: Node, name: Identifier): void { + if (needCollisionCheckForIdentifier(node, name, "_arguments")) { + potentialArgumentsCollisions.push(node); + } + } + // this function will run after checking the source file so 'CaptureThis' is correct for all nodes function checkIfThisIsCapturedInEnclosingScope(node: Node): void { let current = node; @@ -9034,6 +9286,25 @@ module ts { } } + function checkIfArgumentsIsCapturedInEnclosingScope(node: Node): void { + let current = node; + while (current) { + if (getNodeCheckFlags(current) & NodeCheckFlags.CaptureArguments) { + let isDeclaration = node.kind !== SyntaxKind.Identifier; + if (isDeclaration) { + error((node).name, Diagnostics.Duplicate_identifier_arguments_Compiler_uses_variable_declaration_arguments_to_capture_arguments_reference); + } + else { + error(node, Diagnostics.Expression_resolves_to_variable_declaration_arguments_that_compiler_uses_to_capture_arguments_reference); + } + + return; + } + + current = current.parent; + } + } + function checkCollisionWithCapturedSuperVariable(node: Node, name: Identifier) { if (!needCollisionCheckForIdentifier(node, name, "_super")) { return; @@ -9149,6 +9420,34 @@ module ts { } } + function getPromiseConstructor(node: SignatureDeclaration): EntityName { + if (node.type && node.type.kind === SyntaxKind.TypeReference) { + return (node.type).typeName; + } + + let globalPromiseSymbol = getGlobalPromiseSymbol(); + if (globalPromiseSymbol && globalPromiseSymbol === resolveName(node, "Promise", SymbolFlags.Value, undefined, undefined)) { + return globalPromiseSymbol.valueDeclaration.name; + } + + return undefined; + } + + function checkCollisionWithAwaiterVariablesInGeneratedCode(node: Node, name: DeclarationName): void { + if (!name || name.kind !== SyntaxKind.Identifier || isTypeNode(name)) { + return; + } + + let identifier = name; + let container = getContainingFunction(name); + if (container && isAsyncFunctionLike(container) && node.kind !== SyntaxKind.Identifier) { + var promiseConstructor = getPromiseConstructor(container); + if (promiseConstructor && promiseConstructor.kind === SyntaxKind.Identifier && (promiseConstructor).text === identifier.text) { + error(node, Diagnostics.Duplicate_identifier_0_Compiler_uses_declaration_1_to_support_async_functions, identifier.text, getTextOfNode(promiseConstructor)); + } + } + } + function isParameterDeclaration(node: VariableLikeDeclaration) { while (node.kind === SyntaxKind.BindingElement) { node = node.parent.parent; @@ -9251,8 +9550,14 @@ module ts { } checkCollisionWithCapturedSuperVariable(node, node.name); checkCollisionWithCapturedThisVariable(node, node.name); + checkCollisionWithCapturedArgumentsVariable(node, node.name); checkCollisionWithRequireExportsInGeneratedCode(node, node.name); } + + if (symbol === argumentsSymbol && (node.parserContextFlags & ParserContextFlags.Await)) { + let container = getContainingFunction(node); + captureLexicalArguments(node.name, container); + } } function checkVariableDeclaration(node: VariableDeclaration) { @@ -9275,7 +9580,10 @@ module ts { function checkGrammarDisallowedModifiersInBlockOrObjectLiteralExpression(node: Node) { if (node.modifiers) { if (inBlockOrObjectLiteralExpression(node)) { - return grammarErrorOnFirstToken(node, Diagnostics.Modifiers_cannot_appear_here); + // disallow all but the `async` modifier here + if (isAccessor(node.kind) || !isFunctionLike(node) || (node.modifiers.flags & ~NodeFlags.Async)) { + return grammarErrorOnFirstToken(node, Diagnostics.Modifiers_cannot_appear_here); + } } } } @@ -9288,6 +9596,8 @@ module ts { node = node.parent; } + + return false; } function checkExpressionStatement(node: ExpressionStatement) { @@ -9667,8 +9977,11 @@ module ts { error(node.expression, Diagnostics.Return_type_of_constructor_signature_must_be_assignable_to_the_instance_type_of_the_class); } } + else if (func.type && !isAccessor(func.kind) && isAsyncFunctionLike(func)) { + checkTypeAssignableTo(getAwaitedType(exprType), getPromisedType(returnType), node.expression); + } else if (func.type || isGetAccessorWithAnnotatatedSetAccessor(func)) { - checkTypeAssignableTo(exprType, returnType, node.expression, /*headMessage*/ undefined); + checkTypeAssignableTo(exprType, returnType, node.expression); } } } @@ -9943,6 +10256,7 @@ module ts { checkTypeNameIsReserved(node.name, Diagnostics.Class_name_cannot_be_0); checkCollisionWithCapturedThisVariable(node, node.name); checkCollisionWithRequireExportsInGeneratedCode(node, node.name); + checkCollisionWithAwaiterVariablesInGeneratedCode(node, node.name); } checkTypeParameters(node.typeParameters); checkExportsOnMergedDeclarations(node); @@ -10386,6 +10700,7 @@ module ts { checkTypeNameIsReserved(node.name, Diagnostics.Enum_name_cannot_be_0); checkCollisionWithCapturedThisVariable(node, node.name); checkCollisionWithRequireExportsInGeneratedCode(node, node.name); + checkCollisionWithAwaiterVariablesInGeneratedCode(node, node.name); checkExportsOnMergedDeclarations(node); computeEnumMemberValues(node); @@ -10885,12 +11200,14 @@ module ts { case SyntaxKind.ParenthesizedExpression: case SyntaxKind.TypeOfExpression: case SyntaxKind.VoidExpression: + case SyntaxKind.AwaitExpression: case SyntaxKind.DeleteExpression: case SyntaxKind.PrefixUnaryExpression: case SyntaxKind.PostfixUnaryExpression: case SyntaxKind.BinaryExpression: case SyntaxKind.ConditionalExpression: case SyntaxKind.SpreadElementExpression: + case SyntaxKind.YieldExpression: case SyntaxKind.Block: case SyntaxKind.ModuleBlock: case SyntaxKind.VariableStatement: @@ -10941,6 +11258,7 @@ module ts { emitDecorate = false; emitParam = false; potentialThisCollisions.length = 0; + potentialArgumentsCollisions.length = 0; forEach(node.statements, checkSourceElement); checkFunctionExpressionBodies(node); @@ -10953,6 +11271,11 @@ module ts { forEach(potentialThisCollisions, checkIfThisIsCapturedInEnclosingScope); potentialThisCollisions.length = 0; } + + if (potentialArgumentsCollisions.length) { + forEach(potentialArgumentsCollisions, checkIfArgumentsIsCapturedInEnclosingScope); + potentialArgumentsCollisions.length = 0; + } if (emitExtends) { links.flags |= NodeCheckFlags.EmitExtends; @@ -10965,6 +11288,14 @@ module ts { if (emitParam) { links.flags |= NodeCheckFlags.EmitParam; } + + if (emitAwaiter) { + links.flags |= NodeCheckFlags.EmitAwaiter; + } + + if (emitGenerator || (emitAwaiter && languageVersion < ScriptTarget.ES6)) { + links.flags |= NodeCheckFlags.EmitGenerator; + } links.flags |= NodeCheckFlags.TypeChecked; } @@ -11982,6 +12313,12 @@ module ts { getGlobalPropertyDecoratorType = memoize(() => getGlobalType("PropertyDecorator")); getGlobalMethodDecoratorType = memoize(() => getGlobalType("MethodDecorator")); getGlobalParameterDecoratorType = memoize(() => getGlobalType("ParameterDecorator")); + getGlobalPromiseSymbol = memoize(() => getGlobalTypeSymbol("Promise")); + getGlobalPromiseType = memoize(() => getTypeOfGlobalSymbol(getGlobalPromiseSymbol(), /*arity*/ 1)); + getGlobalPromiseLikeType = memoize(() => getGlobalType("PromiseLike", /*arity*/ 1)); + getInstantiatedGlobalPromiseLikeType = memoize(createInstantiatedPromiseLikeType); + getGlobalPromiseConstructorLikeType = memoize(() => getGlobalType("PromiseConstructorLike")); + getGlobalThenableType = memoize(createThenableType); // If we're in ES6 mode, load the TemplateStringsArray. // Otherwise, default to 'unknown' for the purposes of type checking in LS scenarios. @@ -12003,6 +12340,26 @@ module ts { anyArrayType = createArrayType(anyType); } + + function createInstantiatedPromiseLikeType(): ObjectType { + let promiseLikeType = getGlobalPromiseLikeType(); + if (promiseLikeType !== emptyObjectType) { + return createTypeReference(promiseLikeType, [anyType]); + } + + return emptyObjectType; + } + + function createThenableType() { + // build the thenable type that is used to verify against a non-promise "thenable" operand to `await`. + let thenPropertySymbol = createSymbol(SymbolFlags.Transient | SymbolFlags.Property, "then"); + getSymbolLinks(thenPropertySymbol).type = globalFunctionType; + + let thenableType = createObjectType(TypeFlags.ObjectType); + thenableType.properties = [thenPropertySymbol]; + thenableType.members = createSymbolTable(thenableType.properties); + return thenableType; + } // GRAMMAR CHECKING function isReservedWordInStrictMode(node: Identifier): boolean { @@ -12203,7 +12560,7 @@ module ts { return; } - let lastStatic: Node, lastPrivate: Node, lastProtected: Node, lastDeclare: Node; + let lastStatic: Node, lastPrivate: Node, lastProtected: Node, lastDeclare: Node, lastAsync: Node; let flags = 0; for (let modifier of node.modifiers) { switch (modifier.kind) { @@ -12229,6 +12586,9 @@ module ts { else if (flags & NodeFlags.Static) { return grammarErrorOnNode(modifier, Diagnostics._0_modifier_must_precede_1_modifier, text, "static"); } + else if (flags & NodeFlags.Async) { + return grammarErrorOnNode(modifier, Diagnostics._0_modifier_must_precede_1_modifier, text, "async"); + } else if (node.parent.kind === SyntaxKind.ModuleBlock || node.parent.kind === SyntaxKind.SourceFile) { return grammarErrorOnNode(modifier, Diagnostics._0_modifier_cannot_appear_on_a_module_element, text); } @@ -12239,6 +12599,9 @@ module ts { if (flags & NodeFlags.Static) { return grammarErrorOnNode(modifier, Diagnostics._0_modifier_already_seen, "static"); } + else if (flags & NodeFlags.Async) { + return grammarErrorOnNode(modifier, Diagnostics._0_modifier_must_precede_1_modifier, "static", "async"); + } else if (node.parent.kind === SyntaxKind.ModuleBlock || node.parent.kind === SyntaxKind.SourceFile) { return grammarErrorOnNode(modifier, Diagnostics._0_modifier_cannot_appear_on_a_module_element, "static"); } @@ -12256,6 +12619,9 @@ module ts { else if (flags & NodeFlags.Ambient) { return grammarErrorOnNode(modifier, Diagnostics._0_modifier_must_precede_1_modifier, "export", "declare"); } + else if (flags & NodeFlags.Async) { + return grammarErrorOnNode(modifier, Diagnostics._0_modifier_must_precede_1_modifier, "export", "async"); + } else if (node.parent.kind === SyntaxKind.ClassDeclaration) { return grammarErrorOnNode(modifier, Diagnostics._0_modifier_cannot_appear_on_a_class_element, "export"); } @@ -12269,6 +12635,9 @@ module ts { if (flags & NodeFlags.Ambient) { return grammarErrorOnNode(modifier, Diagnostics._0_modifier_already_seen, "declare"); } + else if (flags & NodeFlags.Async) { + return grammarErrorOnNode(modifier, Diagnostics._0_modifier_cannot_be_used_in_an_ambient_context, "async"); + } else if (node.parent.kind === SyntaxKind.ClassDeclaration) { return grammarErrorOnNode(modifier, Diagnostics._0_modifier_cannot_appear_on_a_class_element, "declare"); } @@ -12281,6 +12650,20 @@ module ts { flags |= NodeFlags.Ambient; lastDeclare = modifier break; + + case SyntaxKind.AsyncKeyword: + if (flags & NodeFlags.Async) { + return grammarErrorOnNode(modifier, Diagnostics._0_modifier_already_seen, "async"); + } + else if (flags & NodeFlags.Ambient || isInAmbientContext(node.parent)) { + return grammarErrorOnNode(modifier, Diagnostics._0_modifier_cannot_be_used_in_an_ambient_context, "async"); + } + else if (node.kind === SyntaxKind.Parameter) { + return grammarErrorOnNode(modifier, Diagnostics._0_modifier_cannot_appear_on_a_parameter, "async"); + } + flags |= NodeFlags.Async; + lastAsync = modifier; + break; } } @@ -12294,16 +12677,42 @@ module ts { else if (flags & NodeFlags.Private) { return grammarErrorOnNode(lastPrivate, Diagnostics._0_modifier_cannot_appear_on_a_constructor_declaration, "private"); } + else if (flags & NodeFlags.Async) { + return grammarErrorOnNode(lastAsync, Diagnostics._0_modifier_cannot_appear_on_a_constructor_declaration, "async"); + } + return; } else if ((node.kind === SyntaxKind.ImportDeclaration || node.kind === SyntaxKind.ImportEqualsDeclaration) && flags & NodeFlags.Ambient) { - return grammarErrorOnNode(lastDeclare, Diagnostics.A_declare_modifier_cannot_be_used_with_an_import_declaration, "declare"); + return grammarErrorOnNode(lastDeclare, Diagnostics.A_0_modifier_cannot_be_used_with_an_import_declaration, "declare"); } else if (node.kind === SyntaxKind.InterfaceDeclaration && flags & NodeFlags.Ambient) { - return grammarErrorOnNode(lastDeclare, Diagnostics.A_declare_modifier_cannot_be_used_with_an_interface_declaration, "declare"); + return grammarErrorOnNode(lastDeclare, Diagnostics.A_0_modifier_cannot_be_used_with_an_interface_declaration, "declare"); } else if (node.kind === SyntaxKind.Parameter && (flags & NodeFlags.AccessibilityModifier) && isBindingPattern((node).name)) { return grammarErrorOnNode(node, Diagnostics.A_parameter_property_may_not_be_a_binding_pattern); } + if (flags & NodeFlags.Async) { + return checkGrammarAsyncModifier(node, lastAsync); + } + } + + function checkGrammarAsyncModifier(node: Node, asyncModifier: Node): boolean { + if (languageVersion < ScriptTarget.ES6) { + return grammarErrorOnNode(asyncModifier, Diagnostics.Async_functions_are_only_available_when_targeting_ECMAScript_6_and_higher); + } + + switch (node.kind) { + case SyntaxKind.MethodDeclaration: + case SyntaxKind.FunctionDeclaration: + case SyntaxKind.FunctionExpression: + case SyntaxKind.ArrowFunction: + if (!(node).asteriskToken) { + return false; + } + break; + } + + return grammarErrorOnNode(asyncModifier, Diagnostics._0_modifier_cannot_be_used_here, "async"); } function checkGrammarForDisallowedTrailingComma(list: NodeArray): boolean { diff --git a/src/compiler/diagnosticInformationMap.generated.ts b/src/compiler/diagnosticInformationMap.generated.ts index 14163e81195..091cabb4480 100644 --- a/src/compiler/diagnosticInformationMap.generated.ts +++ b/src/compiler/diagnosticInformationMap.generated.ts @@ -34,8 +34,12 @@ module ts { Statements_are_not_allowed_in_ambient_contexts: { code: 1036, category: DiagnosticCategory.Error, key: "Statements are not allowed in ambient contexts." }, A_declare_modifier_cannot_be_used_in_an_already_ambient_context: { code: 1038, category: DiagnosticCategory.Error, key: "A 'declare' modifier cannot be used in an already ambient context." }, Initializers_are_not_allowed_in_ambient_contexts: { code: 1039, category: DiagnosticCategory.Error, key: "Initializers are not allowed in ambient contexts." }, + _0_modifier_cannot_be_used_in_an_ambient_context: { code: 1040, category: DiagnosticCategory.Error, key: "'{0}' modifier cannot be used in an ambient context." }, + _0_modifier_cannot_be_used_with_a_class_declaration: { code: 1041, category: DiagnosticCategory.Error, key: "'{0}' modifier cannot be used with a class declaration." }, + _0_modifier_cannot_be_used_here: { code: 1042, category: DiagnosticCategory.Error, key: "'{0}' modifier cannot be used here." }, + _0_modifier_cannot_appear_on_a_data_property: { code: 1043, category: DiagnosticCategory.Error, key: "'{0}' modifier cannot appear on a data property." }, _0_modifier_cannot_appear_on_a_module_element: { code: 1044, category: DiagnosticCategory.Error, key: "'{0}' modifier cannot appear on a module element." }, - A_declare_modifier_cannot_be_used_with_an_interface_declaration: { code: 1045, category: DiagnosticCategory.Error, key: "A 'declare' modifier cannot be used with an interface declaration." }, + A_0_modifier_cannot_be_used_with_an_interface_declaration: { code: 1045, category: DiagnosticCategory.Error, key: "A '{0}' modifier cannot be used with an interface declaration." }, A_declare_modifier_is_required_for_a_top_level_declaration_in_a_d_ts_file: { code: 1046, category: DiagnosticCategory.Error, key: "A 'declare' modifier is required for a top level declaration in a .d.ts file." }, A_rest_parameter_cannot_be_optional: { code: 1047, category: DiagnosticCategory.Error, key: "A rest parameter cannot be optional." }, A_rest_parameter_cannot_have_an_initializer: { code: 1048, category: DiagnosticCategory.Error, key: "A rest parameter cannot have an initializer." }, @@ -45,11 +49,13 @@ module ts { A_set_accessor_cannot_have_rest_parameter: { code: 1053, category: DiagnosticCategory.Error, key: "A 'set' accessor cannot have rest parameter." }, A_get_accessor_cannot_have_parameters: { code: 1054, category: DiagnosticCategory.Error, key: "A 'get' accessor cannot have parameters." }, Accessors_are_only_available_when_targeting_ECMAScript_5_and_higher: { code: 1056, category: DiagnosticCategory.Error, key: "Accessors are only available when targeting ECMAScript 5 and higher." }, + An_async_function_or_method_must_have_a_valid_awaitable_return_type: { code: 1057, category: DiagnosticCategory.Error, key: "An async function or method must have a valid awaitable return type." }, + Type_for_await_does_not_have_a_valid_callable_then_member: { code: 1058, category: DiagnosticCategory.Error, key: "Type for 'await' does not have a valid callable 'then' member." }, Enum_member_must_have_initializer: { code: 1061, category: DiagnosticCategory.Error, key: "Enum member must have initializer." }, An_export_assignment_cannot_be_used_in_a_namespace: { code: 1063, category: DiagnosticCategory.Error, key: "An export assignment cannot be used in a namespace." }, Ambient_enum_elements_can_only_have_integer_literal_initializers: { code: 1066, category: DiagnosticCategory.Error, key: "Ambient enum elements can only have integer literal initializers." }, Unexpected_token_A_constructor_method_accessor_or_property_was_expected: { code: 1068, category: DiagnosticCategory.Error, key: "Unexpected token. A constructor, method, accessor, or property was expected." }, - A_declare_modifier_cannot_be_used_with_an_import_declaration: { code: 1079, category: DiagnosticCategory.Error, key: "A 'declare' modifier cannot be used with an import declaration." }, + A_0_modifier_cannot_be_used_with_an_import_declaration: { code: 1079, category: DiagnosticCategory.Error, key: "A '{0}' modifier cannot be used with an import declaration." }, Invalid_reference_directive_syntax: { code: 1084, category: DiagnosticCategory.Error, key: "Invalid 'reference' directive syntax." }, Octal_literals_are_not_available_when_targeting_ECMAScript_5_and_higher: { code: 1085, category: DiagnosticCategory.Error, key: "Octal literals are not available when targeting ECMAScript 5 and higher." }, An_accessor_cannot_be_declared_in_an_ambient_context: { code: 1086, category: DiagnosticCategory.Error, key: "An accessor cannot be declared in an ambient context." }, @@ -120,7 +126,7 @@ module ts { Unterminated_template_literal: { code: 1160, category: DiagnosticCategory.Error, key: "Unterminated template literal." }, Unterminated_regular_expression_literal: { code: 1161, category: DiagnosticCategory.Error, key: "Unterminated regular expression literal." }, An_object_member_cannot_be_declared_optional: { code: 1162, category: DiagnosticCategory.Error, key: "An object member cannot be declared optional." }, - yield_expression_must_be_contained_within_a_generator_declaration: { code: 1163, category: DiagnosticCategory.Error, key: "'yield' expression must be contained_within a generator declaration." }, + yield_expression_must_be_contained_within_a_generator_declaration: { code: 1163, category: DiagnosticCategory.Error, key: "'yield' expression must be contained within a generator declaration." }, Computed_property_names_are_not_allowed_in_enums: { code: 1164, category: DiagnosticCategory.Error, key: "Computed property names are not allowed in enums." }, A_computed_property_name_in_an_ambient_context_must_directly_refer_to_a_built_in_symbol: { code: 1165, category: DiagnosticCategory.Error, key: "A computed property name in an ambient context must directly refer to a built-in symbol." }, A_computed_property_name_in_a_class_property_declaration_must_directly_refer_to_a_built_in_symbol: { code: 1166, category: DiagnosticCategory.Error, key: "A computed property name in a class property declaration must directly refer to a built-in symbol." }, @@ -174,6 +180,18 @@ module ts { Type_expected_0_is_a_reserved_word_in_strict_mode: { code: 1215, category: DiagnosticCategory.Error, key: "Type expected. '{0}' is a reserved word in strict mode" }, Type_expected_0_is_a_reserved_word_in_strict_mode_Class_definitions_are_automatically_in_strict_mode: { code: 1216, category: DiagnosticCategory.Error, key: "Type expected. '{0}' is a reserved word in strict mode. Class definitions are automatically in strict mode." }, Export_assignment_is_not_supported_when_module_flag_is_system: { code: 1218, category: DiagnosticCategory.Error, key: "Export assignment is not supported when '--module' flag is 'system'." }, + with_statements_are_not_allowed_in_an_async_function_block: { code: 1300, category: DiagnosticCategory.Error, key: "'with' statements are not allowed in an async function block." }, + _0_modifier_cannot_be_used_with_a_generator: { code: 1301, category: DiagnosticCategory.Error, key: "'{0}' modifier cannot be used with a generator." }, + _0_modifier_cannot_be_used_with_a_module_declaration: { code: 1302, category: DiagnosticCategory.Error, key: "'{0}' modifier cannot be used with a module declaration." }, + _0_modifier_cannot_be_used_with_an_enum_declaration: { code: 1303, category: DiagnosticCategory.Error, key: "'{0}' modifier cannot be used with an enum declaration." }, + _0_modifier_cannot_be_used_with_an_export_assignment_declaration: { code: 1304, category: DiagnosticCategory.Error, key: "'{0}' modifier cannot be used with an export assignment declaration." }, + _0_modifier_cannot_be_used_on_a_variable_statement: { code: 1305, category: DiagnosticCategory.Error, key: "'{0}' modifier cannot be used on a variable statement." }, + _0_modifier_cannot_be_used_with_a_type_declaration: { code: 1306, category: DiagnosticCategory.Error, key: "'{0}' modifier cannot be used with a type declaration." }, + _0_modifier_cannot_be_used_with_a_parameter_declaration: { code: 1307, category: DiagnosticCategory.Error, key: "'{0}' modifier cannot be used with a parameter declaration." }, + await_expression_must_be_contained_within_an_async_function: { code: 1308, category: DiagnosticCategory.Error, key: "'await' expression must be contained within an async function." }, + _0_modifier_cannot_be_used_on_an_object_literal_element: { code: 1309, category: DiagnosticCategory.Error, key: "'{0}' modifier cannot be used on an object literal element." }, + _0_expression_is_not_allowed_in_an_initializer: { code: 1310, category: DiagnosticCategory.Error, key: "'{0}' expression is not allowed in an initializer." }, + Async_functions_are_only_available_when_targeting_ECMAScript_6_and_higher: { code: 1311, category: DiagnosticCategory.Error, key: "Async functions are only available when targeting ECMAScript 6 and higher." }, Duplicate_identifier_0: { code: 2300, category: DiagnosticCategory.Error, key: "Duplicate identifier '{0}'." }, Initializer_of_instance_member_variable_0_cannot_reference_identifier_1_declared_in_the_constructor: { code: 2301, category: DiagnosticCategory.Error, key: "Initializer of instance member variable '{0}' cannot reference identifier '{1}' declared in the constructor." }, Static_members_cannot_reference_class_type_parameters: { code: 2302, category: DiagnosticCategory.Error, key: "Static members cannot reference class type parameters." }, @@ -363,6 +381,10 @@ module ts { An_interface_can_only_extend_an_identifier_Slashqualified_name_with_optional_type_arguments: { code: 2499, category: DiagnosticCategory.Error, key: "An interface can only extend an identifier/qualified-name with optional type arguments." }, A_class_can_only_implement_an_identifier_Slashqualified_name_with_optional_type_arguments: { code: 2500, category: DiagnosticCategory.Error, key: "A class can only implement an identifier/qualified-name with optional type arguments." }, A_rest_element_cannot_contain_a_binding_pattern: { code: 2501, category: DiagnosticCategory.Error, key: "A rest element cannot contain a binding pattern." }, + Duplicate_identifier_0_Compiler_uses_declaration_1_to_support_async_functions: { code: 2520, category: DiagnosticCategory.Error, key: "Duplicate identifier '{0}'. Compiler uses declaration '{1}' to support async functions." }, + Expression_resolves_to_variable_declaration_0_that_compiler_uses_to_support_async_functions: { code: 2521, category: DiagnosticCategory.Error, key: "Expression resolves to variable declaration '{0}' that compiler uses to support async functions." }, + Duplicate_identifier_arguments_Compiler_uses_variable_declaration_arguments_to_capture_arguments_reference: { code: 2522, category: DiagnosticCategory.Error, key: "Duplicate identifier '_arguments'. Compiler uses variable declaration '_arguments' to capture 'arguments' reference." }, + Expression_resolves_to_variable_declaration_arguments_that_compiler_uses_to_capture_arguments_reference: { code: 2523, category: DiagnosticCategory.Error, key: "Expression resolves to variable declaration '_arguments' that compiler uses to capture 'arguments' reference." }, Import_declaration_0_is_using_private_name_1: { code: 4000, category: DiagnosticCategory.Error, key: "Import declaration '{0}' is using private name '{1}'." }, Type_parameter_0_of_exported_class_has_or_is_using_private_name_1: { code: 4002, category: DiagnosticCategory.Error, key: "Type parameter '{0}' of exported class has or is using private name '{1}'." }, Type_parameter_0_of_exported_interface_has_or_is_using_private_name_1: { code: 4004, category: DiagnosticCategory.Error, key: "Type parameter '{0}' of exported interface has or is using private name '{1}'." }, @@ -505,6 +527,7 @@ module ts { Specifies_the_end_of_line_sequence_to_be_used_when_emitting_files_Colon_CRLF_dos_or_LF_unix: { code: 6060, category: DiagnosticCategory.Message, key: "Specifies the end of line sequence to be used when emitting files: 'CRLF' (dos) or 'LF' (unix)." }, NEWLINE: { code: 6061, category: DiagnosticCategory.Message, key: "NEWLINE" }, Argument_for_newLine_option_must_be_CRLF_or_LF: { code: 6062, category: DiagnosticCategory.Error, key: "Argument for '--newLine' option must be 'CRLF' or 'LF'." }, + Emit_async_functions_when_ECMAScript_target_version_is_lower_than_ES6: { code: 6063, category: DiagnosticCategory.Message, key: "Emit async functions when ECMAScript target version is lower than 'ES6'." }, Variable_0_implicitly_has_an_1_type: { code: 7005, category: DiagnosticCategory.Error, key: "Variable '{0}' implicitly has an '{1}' type." }, Parameter_0_implicitly_has_an_1_type: { code: 7006, category: DiagnosticCategory.Error, key: "Parameter '{0}' implicitly has an '{1}' type." }, Member_0_implicitly_has_an_1_type: { code: 7008, category: DiagnosticCategory.Error, key: "Member '{0}' implicitly has an '{1}' type." }, diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 18c98928dda..431aec3f531 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -123,11 +123,27 @@ "category": "Error", "code": 1039 }, + "'{0}' modifier cannot be used in an ambient context.": { + "category": "Error", + "code": 1040 + }, + "'{0}' modifier cannot be used with a class declaration.": { + "category": "Error", + "code": 1041 + }, + "'{0}' modifier cannot be used here.": { + "category": "Error", + "code": 1042 + }, + "'{0}' modifier cannot appear on a data property.": { + "category": "Error", + "code": 1043 + }, "'{0}' modifier cannot appear on a module element.": { "category": "Error", "code": 1044 }, - "A 'declare' modifier cannot be used with an interface declaration.": { + "A '{0}' modifier cannot be used with an interface declaration.": { "category": "Error", "code": 1045 }, @@ -167,6 +183,14 @@ "category": "Error", "code": 1056 }, + "An async function or method must have a valid awaitable return type.": { + "category": "Error", + "code": 1057 + }, + "Type for 'await' does not have a valid callable 'then' member.": { + "category": "Error", + "code": 1058 + }, "Enum member must have initializer.": { "category": "Error", "code": 1061 @@ -183,7 +207,7 @@ "category": "Error", "code": 1068 }, - "A 'declare' modifier cannot be used with an import declaration.": { + "A '{0}' modifier cannot be used with an import declaration.": { "category": "Error", "code": 1079 }, @@ -467,7 +491,7 @@ "category": "Error", "code": 1162 }, - "'yield' expression must be contained_within a generator declaration.": { + "'yield' expression must be contained within a generator declaration.": { "category": "Error", "code": 1163 }, @@ -684,6 +708,55 @@ "code": 1218 }, + "'with' statements are not allowed in an async function block.": { + "category": "Error", + "code": 1300 + }, + "'{0}' modifier cannot be used with a generator.": { + "category": "Error", + "code": 1301 + }, + "'{0}' modifier cannot be used with a module declaration.": { + "category": "Error", + "code": 1302 + }, + "'{0}' modifier cannot be used with an enum declaration.": { + "category": "Error", + "code": 1303 + }, + "'{0}' modifier cannot be used with an export assignment declaration.": { + "category": "Error", + "code": 1304 + }, + "'{0}' modifier cannot be used on a variable statement.": { + "category": "Error", + "code": 1305 + }, + "'{0}' modifier cannot be used with a type declaration.": { + "category": "Error", + "code": 1306 + }, + "'{0}' modifier cannot be used with a parameter declaration.": { + "category": "Error", + "code": 1307 + }, + "'await' expression must be contained within an async function.": { + "category": "Error", + "code": 1308 + }, + "'{0}' modifier cannot be used on an object literal element.": { + "category": "Error", + "code": 1309 + }, + "'{0}' expression is not allowed in an initializer.": { + "category": "Error", + "code": 1310 + }, + "Async functions are only available when targeting ECMAScript 6 and higher.": { + "category": "Error", + "code": 1311 + }, + "Duplicate identifier '{0}'.": { "category": "Error", "code": 2300 @@ -1441,6 +1514,23 @@ "code": 2501 }, + "Duplicate identifier '{0}'. Compiler uses declaration '{1}' to support async functions.": { + "category": "Error", + "code": 2520 + }, + "Expression resolves to variable declaration '{0}' that compiler uses to support async functions.": { + "category": "Error", + "code": 2521 + }, + "Duplicate identifier '_arguments'. Compiler uses variable declaration '_arguments' to capture 'arguments' reference.": { + "category": "Error", + "code": 2522 + }, + "Expression resolves to variable declaration '_arguments' that compiler uses to capture 'arguments' reference.": { + "category": "Error", + "code": 2523 + }, + "Import declaration '{0}' is using private name '{1}'.": { "category": "Error", "code": 4000 @@ -2010,7 +2100,10 @@ "category": "Error", "code": 6062 }, - + "Emit async functions when ECMAScript target version is lower than 'ES6'.": { + "category": "Message", + "code": 6063 + }, "Variable '{0}' implicitly has an '{1}' type.": { "category": "Error", diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 055fc1b6f74..db958087178 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -153,6 +153,8 @@ module ts { case SyntaxKind.YieldExpression: return visitNode(cbNode, (node).asteriskToken) || visitNode(cbNode, (node).expression); + case SyntaxKind.AwaitExpression: + return visitNode(cbNode, (node).expression); case SyntaxKind.PostfixUnaryExpression: return visitNode(cbNode, (node).operand); case SyntaxKind.BinaryExpression: @@ -556,9 +558,17 @@ module ts { function setDecoratorContext(val: boolean) { setContextFlag(val, ParserContextFlags.Decorator); } - - function doOutsideOfContext(flags: ParserContextFlags, func: () => T): T { - let currentContextFlags = contextFlags & flags; + + function setAwaitContext(val: boolean) { + setContextFlag(val, ParserContextFlags.Await); + } + + function setAsyncParameterContext(val: boolean) { + setContextFlag(val, ParserContextFlags.AsyncParameter); + } + + function doOutsideOfContext(context: ParserContextFlags, func: () => T): T { + let currentContextFlags = contextFlags & context; if (currentContextFlags) { setContextFlag(false, currentContextFlags); let result = func(); @@ -569,85 +579,86 @@ module ts { // no need to do anything special as we are not in any of the requested contexts return func(); } - - function allowInAnd(func: () => T): T { - if (contextFlags & ParserContextFlags.DisallowIn) { - setDisallowInContext(false); + + function doInsideOfContext(context: ParserContextFlags, func: () => T): T { + let unsetContextFlags = context & ~contextFlags; + if (!unsetContextFlags) { + setContextFlag(true, unsetContextFlags); let result = func(); - setDisallowInContext(true); + setContextFlag(false, unsetContextFlags); return result; } - - // no need to do anything special if 'in' is already allowed. + + // no need to do anything special as we are already in all of the requested contexts return func(); } + function allowInAnd(func: () => T): T { + return doInsideOfContext(ParserContextFlags.DisallowIn, func); + } + function disallowInAnd(func: () => T): T { - if (contextFlags & ParserContextFlags.DisallowIn) { - // no need to do anything special if 'in' is already disallowed. - return func(); - } - - setDisallowInContext(true); - let result = func(); - setDisallowInContext(false); - return result; + return doOutsideOfContext(ParserContextFlags.DisallowIn, func); } - + function doInYieldContext(func: () => T): T { - if (contextFlags & ParserContextFlags.Yield) { - // no need to do anything special if we're already in the [Yield] context. - return func(); - } - - setYieldContext(true); - let result = func(); - setYieldContext(false); - return result; + return doInsideOfContext(ParserContextFlags.Yield, func); } function doOutsideOfYieldContext(func: () => T): T { - if (contextFlags & ParserContextFlags.Yield) { - setYieldContext(false); - let result = func(); - setYieldContext(true); - return result; - } - - // no need to do anything special if we're not in the [Yield] context. - return func(); + return doOutsideOfContext(ParserContextFlags.Yield, func); } function doInDecoratorContext(func: () => T): T { - if (contextFlags & ParserContextFlags.Decorator) { - // no need to do anything special if we're already in the [Decorator] context. - return func(); - } - - setDecoratorContext(true); - let result = func(); - setDecoratorContext(false); - return result; + return doInsideOfContext(ParserContextFlags.Decorator, func); + } + + function doInAwaitContext(func: () => T): T { + return doInsideOfContext(ParserContextFlags.Await, func); + } + + function doOutsideOfAwaitContext(func: () => T): T { + return doOutsideOfContext(ParserContextFlags.Await, func); + } + + function doOutsideOfYieldAndAwaitContext(func: () => T): T { + return doOutsideOfContext(ParserContextFlags.Yield | ParserContextFlags.Await, func); + } + + function inContext(flags: ParserContextFlags) { + return (contextFlags & flags) === flags; } function inYieldContext() { - return (contextFlags & ParserContextFlags.Yield) !== 0; + return inContext(ParserContextFlags.Yield); } function inStrictModeContext() { - return (contextFlags & ParserContextFlags.StrictMode) !== 0; + return inContext(ParserContextFlags.StrictMode); } function inGeneratorParameterContext() { - return (contextFlags & ParserContextFlags.GeneratorParameter) !== 0; + return inContext(ParserContextFlags.GeneratorParameter); } function inDisallowInContext() { - return (contextFlags & ParserContextFlags.DisallowIn) !== 0; + return inContext(ParserContextFlags.DisallowIn); } function inDecoratorContext() { - return (contextFlags & ParserContextFlags.Decorator) !== 0; + return inContext(ParserContextFlags.Decorator); + } + + function inAwaitContext() { + return inContext(ParserContextFlags.Await); + } + + function inAsyncParameterContext() { + return inContext(ParserContextFlags.AsyncParameter); + } + + function inGeneratorParameterOrAsyncParameterContext() { + return inContext(ParserContextFlags.GeneratorParameter | ParserContextFlags.AsyncParameter); } function parseErrorAtCurrentToken(message: DiagnosticMessage, arg0?: any): void { @@ -927,7 +938,8 @@ module ts { // PropertyName[Yield,GeneratorParameter] : // LiteralPropertyName // [+GeneratorParameter] ComputedPropertyName - // [~GeneratorParameter] ComputedPropertyName[?Yield] + // [+AsyncParameter] ComputedPropertyName + // [~GeneratorParameter,~AsyncParameter] ComputedPropertyName[?Yield,?Await] // // ComputedPropertyName[Yield] : // [ AssignmentExpression[In, ?Yield] ] @@ -938,34 +950,40 @@ module ts { // We parse any expression (including a comma expression). But the grammar // says that only an assignment expression is allowed, so the grammar checker // will error if it sees a comma expression. - let yieldContext = inYieldContext(); - if (inGeneratorParameterContext()) { - setYieldContext(false); - } - - node.expression = allowInAnd(parseExpression); - if (inGeneratorParameterContext()) { - setYieldContext(yieldContext); - } + node.expression = inGeneratorParameterOrAsyncParameterContext() + ? doOutsideOfYieldAndAwaitContext(parseExpression) + : parseExpression(); parseExpected(SyntaxKind.CloseBracketToken); return finishNode(node); } - function parseContextualModifier(t: SyntaxKind): boolean { - return token === t && tryParse(nextTokenCanFollowModifier); + function parseContextualModifier(t: SyntaxKind, isArrowFunction?: boolean): boolean { + return token === t && tryParse(isArrowFunction + ? nextTokenCanFollowModifierForArrowFunction + : nextTokenCanFollowModifier); + } + + function nextTokenCanFollowModifierForArrowFunction() { + return nextTokenCanFollowModifier(/*isArrowFunction*/ true); } - function nextTokenCanFollowModifier() { + function nextTokenCanFollowModifier(isArrowFunction?: boolean) { nextToken(); - return canFollowModifier(); + return canFollowModifier(isArrowFunction); } - function parseAnyContextualModifier(): boolean { - return isModifier(token) && tryParse(nextTokenCanFollowContextualModifier); + function parseAnyContextualModifier(isArrowFunction?: boolean): boolean { + return isModifier(token) && tryParse(isArrowFunction + ? nextTokenCanFollowContextualModifierForArrowFunction + : nextTokenCanFollowContextualModifier); + } + + function nextTokenCanFollowContextualModifierForArrowFunction() { + return nextTokenCanFollowContextualModifier(/*isArrowFunction*/ true); } - function nextTokenCanFollowContextualModifier() { + function nextTokenCanFollowContextualModifier(isArrowFunction?: boolean) { if (token === SyntaxKind.ConstKeyword) { // 'const' is only a modifier if followed by 'enum'. return nextToken() === SyntaxKind.EnumKeyword; @@ -981,10 +999,19 @@ module ts { return nextTokenIsClassOrFunction(); } nextToken(); - return canFollowModifier(); + return canFollowModifier(isArrowFunction); } - function canFollowModifier(): boolean { + function canFollowModifier(isArrowFunction?: boolean): boolean { + if (isArrowFunction) { + if (scanner.hasPrecedingLineBreak()) { + return false; + } + + return token === SyntaxKind.OpenParenToken + || token === SyntaxKind.LessThanToken; + } + return token === SyntaxKind.OpenBracketToken || token === SyntaxKind.OpenBraceToken || token === SyntaxKind.AsteriskToken @@ -1827,11 +1854,10 @@ module ts { setModifiers(node, parseModifiers()); node.dotDotDotToken = parseOptionalToken(SyntaxKind.DotDotDotToken); - // SingleNameBinding[Yield,GeneratorParameter] : See 13.2.3 - // [+GeneratorParameter]BindingIdentifier[Yield]Initializer[In]opt - // [~GeneratorParameter]BindingIdentifier[?Yield]Initializer[In, ?Yield]opt + // FormalParameter[Yield,GeneratorParameter,Await,AsyncParameter] : (Modified) See 14.1 + // BindingElement[?Yield,?GeneratorParameter,?Await,?AsyncParameter] - node.name = inGeneratorParameterContext() ? doInYieldContext(parseIdentifierOrPattern) : parseIdentifierOrPattern(); + node.name = parseIdentifierOrPattern(); if (getFullWidth(node.name) === 0 && node.flags === 0 && isModifier(token)) { // in cases like @@ -1847,7 +1873,7 @@ module ts { node.questionToken = parseOptionalToken(SyntaxKind.QuestionToken); node.type = parseParameterType(); - node.initializer = inGeneratorParameterContext() ? doOutsideOfYieldContext(parseParameterInitializer) : parseParameterInitializer(); + node.initializer = parseBindingElementInitializer(/*inParameter*/ true); // Do not check for initializers in an ambient context for parameters. This is not // a grammar error because the grammar allows arbitrary call signatures in @@ -1859,19 +1885,36 @@ module ts { // ambient contexts. return finishNode(node); } + + function parseBindingElementInitializer(inParameter: boolean) { + // BindingElement[Yield,GeneratorParameter,Await,AsyncParameter] : + // [+GeneratorParameter] BindingPattern[?Yield,?Await,GeneratorParameter] Initializer[In]opt + // [+AsyncParameter] BindingPattern[?Yield,?Await,AsyncParameter] Initializer[In]opt + // [~GeneratorParameter,~AsyncParameter] BindingPattern[?Yield,?Await] Initializer[In,?Yield,?Await]opt + // SingleNameBinding[Yield,GeneratorParameter,Await,AsyncParameter] : + // [+GeneratorParameter] BindingIdentifier[Yield] Initializer[In]opt + // [+AsyncParameter] BindingIdentifier[Await] Initializer[In]opt + // [~GeneratorParameter,~AsyncParameter] BindingIdentifier[?Yield,?Await] Initializer[In,?Yield,?Await]opt + + let parseInitializer = inParameter ? parseParameterInitializer : parseNonParameterInitializer; + return inGeneratorParameterOrAsyncParameterContext() + ? doOutsideOfYieldAndAwaitContext(parseInitializer) + : parseInitializer(); + } function parseParameterInitializer() { return parseInitializer(/*inParameter*/ true); } - + function fillSignature( returnToken: SyntaxKind, yieldAndGeneratorParameterContext: boolean, + awaitAndAsyncParameterContext: boolean, requireCompleteParameterList: boolean, signature: SignatureDeclaration): void { let returnTokenRequired = returnToken === SyntaxKind.EqualsGreaterThanToken; signature.typeParameters = parseTypeParameters(); - signature.parameters = parseParameterList(yieldAndGeneratorParameterContext, requireCompleteParameterList); + signature.parameters = parseParameterList(yieldAndGeneratorParameterContext, awaitAndAsyncParameterContext, requireCompleteParameterList); if (returnTokenRequired) { parseExpected(returnToken); @@ -1886,32 +1929,48 @@ module ts { // have 'Yield' And 'GeneratorParameter' not in sync. i.e. any production calling // this FormalParameters production either always sets both to true, or always sets // both to false. As such we only have a single parameter to represent both. - function parseParameterList(yieldAndGeneratorParameterContext: boolean, requireCompleteParameterList: boolean) { - // FormalParameters[Yield,GeneratorParameter] : + function parseParameterList(yieldAndGeneratorParameterContext: boolean, awaitAndAsyncParameterContext: boolean, requireCompleteParameterList: boolean) { + // FormalParameters[Yield,GeneratorParameter,Await,AsyncParameter] : (Modified) // ... // - // FormalParameter[Yield,GeneratorParameter] : - // BindingElement[?Yield, ?GeneratorParameter] + // FormalParameter[Yield,GeneratorParameter,Await,AsyncParameter] : (Modified) + // BindingElement[?Yield,?GeneratorParameter,?Await,?AsyncParameter] // - // BindingElement[Yield, GeneratorParameter ] : See 13.2.3 - // SingleNameBinding[?Yield, ?GeneratorParameter] - // [+GeneratorParameter]BindingPattern[?Yield, GeneratorParameter]Initializer[In]opt - // [~GeneratorParameter]BindingPattern[?Yield]Initializer[In, ?Yield]opt + // BindingElement[Yield,GeneratorParameter,Await,AsyncParameter] : (Modified) See 13.2.3 + // SingleNameBinding[?Yield,?GeneratorParameter,?Await,?AsyncParameter] + // [+GeneratorParameter]BindingPattern[?Yield,?Await,?AsyncParameter,GeneratorParameter] Initializer[In]opt + // [+AsyncParameter]BindingPattern[?Yield,?Await,?GeneratorParameter,AsyncParameter] Initializer[In]opt + // [~GeneratorParameter,~AsyncParameter]BindingPattern[?Yield,?Await]Initializer[In,?Yield,?Await]opt // - // SingleNameBinding[Yield, GeneratorParameter] : See 13.2.3 + // SingleNameBinding[Yield,GeneratorParameter,Await,AsyncParameter] : (Modified) See 13.2.3 // [+GeneratorParameter]BindingIdentifier[Yield]Initializer[In]opt - // [~GeneratorParameter]BindingIdentifier[?Yield]Initializer[In, ?Yield]opt + // [+AsyncParameter]BindingIdentifier[Await]Initializer[In]opt + // [~GeneratorParameter,~AsyncParameter]BindingIdentifier[?Yield,?Await]Initializer[In,?Yield,?Await]opt if (parseExpected(SyntaxKind.OpenParenToken)) { let savedYieldContext = inYieldContext(); let savedGeneratorParameterContext = inGeneratorParameterContext(); + let savedAwaitContext = inAwaitContext(); + let savedAsyncParameterContext = inAsyncParameterContext(); - setYieldContext(yieldAndGeneratorParameterContext); + if (yieldAndGeneratorParameterContext) { + setYieldContext(true); + } + setGeneratorParameterContext(yieldAndGeneratorParameterContext); + + if (awaitAndAsyncParameterContext) { + setAwaitContext(true); + } + + setAsyncParameterContext(awaitAndAsyncParameterContext); let result = parseDelimitedList(ParsingContext.Parameters, parseParameter); setYieldContext(savedYieldContext); setGeneratorParameterContext(savedGeneratorParameterContext); + + setAwaitContext(savedAwaitContext); + setAsyncParameterContext(savedAsyncParameterContext); if (!parseExpected(SyntaxKind.CloseParenToken) && requireCompleteParameterList) { // Caller insisted that we had to end with a ) We didn't. So just return @@ -1944,7 +2003,7 @@ module ts { if (kind === SyntaxKind.ConstructSignature) { parseExpected(SyntaxKind.NewKeyword); } - fillSignature(SyntaxKind.ColonToken, /*yieldAndGeneratorParameterContext:*/ false, /*requireCompleteParameterList:*/ false, node); + fillSignature(SyntaxKind.ColonToken, /*yieldAndGeneratorParameterContext:*/ false, /*awaitAndAsyncParameterContext*/ false, /*requireCompleteParameterList:*/ false, node); parseTypeMemberSemicolon(); return finishNode(node); } @@ -2034,7 +2093,7 @@ module ts { // Method signatues don't exist in expression contexts. So they have neither // [Yield] nor [GeneratorParameter] - fillSignature(SyntaxKind.ColonToken, /*yieldAndGeneratorParameterContext:*/ false, /*requireCompleteParameterList:*/ false, method); + fillSignature(SyntaxKind.ColonToken, /*yieldAndGeneratorParameterContext:*/ false, /*awaitAndAsyncParameterContext*/ false, /*requireCompleteParameterList:*/ false, method); parseTypeMemberSemicolon(); return finishNode(method); } @@ -2173,7 +2232,7 @@ module ts { if (kind === SyntaxKind.ConstructorType) { parseExpected(SyntaxKind.NewKeyword); } - fillSignature(SyntaxKind.EqualsGreaterThanToken, /*yieldAndGeneratorParameterContext:*/ false, /*requireCompleteParameterList:*/ false, node); + fillSignature(SyntaxKind.EqualsGreaterThanToken, /*yieldAndGeneratorParameterContext:*/ false, /*awaitAndAsyncParameterContext*/ false, /*requireCompleteParameterList:*/ false, node); return finishNode(node); } @@ -2302,19 +2361,8 @@ module ts { function parseType(): TypeNode { // The rules about 'yield' only apply to actual code/expression contexts. They don't - // apply to 'type' contexts. So we disable these parameters here before moving on. - let savedYieldContext = inYieldContext(); - let savedGeneratorParameterContext = inGeneratorParameterContext(); - - setYieldContext(false); - setGeneratorParameterContext(false); - - let result = parseTypeWorker(); - - setYieldContext(savedYieldContext); - setGeneratorParameterContext(savedGeneratorParameterContext); - - return result; + // apply to 'type' contexts. So we disable these parameters here before moving on. + return doOutsideOfContext(ParserContextFlags.ContextParameterFlags, parseTypeWorker); } function parseTypeWorker(): TypeNode { @@ -2374,6 +2422,7 @@ module ts { case SyntaxKind.PlusPlusToken: case SyntaxKind.MinusMinusToken: case SyntaxKind.LessThanToken: + case SyntaxKind.AwaitKeyword: case SyntaxKind.YieldKeyword: // Yield always starts an expression. Either it is an identifier (in which case // it is definitely an expression). Or it's a keyword (either because we're in @@ -2586,7 +2635,7 @@ module ts { node.parameters.end = parameter.end; node.equalsGreaterThanToken = parseExpectedToken(SyntaxKind.EqualsGreaterThanToken, false, Diagnostics._0_expected, "=>"); - node.body = parseArrowFunctionExpressionBody(); + node.body = parseArrowFunctionExpressionBody(/*isAsync*/ false); return finishNode(node); } @@ -2617,7 +2666,7 @@ module ts { var lastToken = token; arrowFunction.equalsGreaterThanToken = parseExpectedToken(SyntaxKind.EqualsGreaterThanToken, /*reportAtCurrentPosition:*/false, Diagnostics._0_expected, "=>"); arrowFunction.body = (lastToken === SyntaxKind.EqualsGreaterThanToken || lastToken === SyntaxKind.OpenBraceToken) - ? parseArrowFunctionExpressionBody() + ? parseArrowFunctionExpressionBody(/*isAsync*/ isAsyncFunctionLike(arrowFunction)) : parseIdentifier(); return finishNode(arrowFunction); @@ -2628,7 +2677,7 @@ module ts { // Unknown -> There *might* be a parenthesized arrow function here. // Speculatively look ahead to be sure, and rollback if not. function isParenthesizedArrowFunctionExpression(): Tristate { - if (token === SyntaxKind.OpenParenToken || token === SyntaxKind.LessThanToken) { + if (token === SyntaxKind.OpenParenToken || token === SyntaxKind.LessThanToken || token === SyntaxKind.AsyncKeyword) { return lookAhead(isParenthesizedArrowFunctionExpressionWorker); } @@ -2643,6 +2692,10 @@ module ts { } function isParenthesizedArrowFunctionExpressionWorker() { + if (token === SyntaxKind.AsyncKeyword && !parseContextualModifier(SyntaxKind.AsyncKeyword, /*isArrowFunction*/ true)) { + return Tristate.False; + } + let first = token; let second = nextToken(); @@ -2718,6 +2771,9 @@ module ts { function parseParenthesizedArrowFunctionExpressionHead(allowAmbiguity: boolean): ArrowFunction { let node = createNode(SyntaxKind.ArrowFunction); + setModifiers(node, parseModifiers(/*isArrowFunction*/ true)); + let isAsync = isAsyncFunctionLike(node); + // Arrow functions are never generators. // // If we're speculatively parsing a signature for a parenthesized arrow function, then @@ -2725,7 +2781,7 @@ module ts { // a => (b => c) // And think that "(b =>" was actually a parenthesized arrow function with a missing // close paren. - fillSignature(SyntaxKind.ColonToken, /*yieldAndGeneratorParameterContext:*/ false, /*requireCompleteParameterList:*/ !allowAmbiguity, node); + fillSignature(SyntaxKind.ColonToken, /*yieldAndGeneratorParameterContext:*/ false, /*awaitAndAsyncParameterContext*/ isAsync, /*requireCompleteParameterList:*/ !allowAmbiguity, node); // If we couldn't get parameters, we definitely could not parse out an arrow function. if (!node.parameters) { @@ -2748,9 +2804,9 @@ module ts { return node; } - function parseArrowFunctionExpressionBody(): Block | Expression { + function parseArrowFunctionExpressionBody(isAsync: boolean): Block | Expression { if (token === SyntaxKind.OpenBraceToken) { - return parseFunctionBlock(/*allowYield:*/ false, /* ignoreMissingOpenBrace */ false); + return parseFunctionBlock(/*allowYield*/ false, /*allowAwait*/ isAsync, /*ignoreMissingOpenBrace*/ false); } if (isStartOfStatement(/*inErrorRecovery:*/ true) && @@ -2772,10 +2828,12 @@ module ts { // up preemptively closing the containing construct. // // Note: even when 'ignoreMissingOpenBrace' is passed as true, parseBody will still error. - return parseFunctionBlock(/*allowYield:*/ false, /* ignoreMissingOpenBrace */ true); + return parseFunctionBlock(/*allowYield*/ false, /*allowAwait*/ isAsync, /*ignoreMissingOpenBrace*/ true); } - - return parseAssignmentExpressionOrHigher(); + + return isAsync + ? doInAwaitContext(parseAssignmentExpressionOrHigher) + : doOutsideOfAwaitContext(parseAssignmentExpressionOrHigher); } function parseConditionalExpressionRest(leftOperand: Expression): Expression { @@ -2915,8 +2973,32 @@ module ts { node.expression = parseUnaryExpressionOrHigher(); return finishNode(node); } + + function isAwaitExpression(): boolean { + if (token === SyntaxKind.AwaitKeyword) { + if (inAwaitContext()) { + return true; + } + + // here we are using similar heuristics as 'isYieldExpression' + return lookAhead(nextTokenIsIdentifierOnSameLine); + } + + return false; + } + + function parseAwaitExpression() { + var node = createNode(SyntaxKind.AwaitExpression); + nextToken(); + node.expression = parseUnaryExpressionOrHigher(); + return finishNode(node); + } function parseUnaryExpressionOrHigher(): UnaryExpression { + if (isAwaitExpression()) { + return parseAwaitExpression(); + } + switch (token) { case SyntaxKind.PlusToken: case SyntaxKind.MinusToken: @@ -3230,6 +3312,11 @@ module ts { return parseArrayLiteralExpression(); case SyntaxKind.OpenBraceToken: return parseObjectLiteralExpression(); + case SyntaxKind.AsyncKeyword: + if (!lookAhead(nextTokenIsFunctionKeywordOnSameLine)) { + break; + } + return parseFunctionExpression(); case SyntaxKind.ClassKeyword: return parseClassExpression(); case SyntaxKind.FunctionKeyword: @@ -3353,15 +3440,32 @@ module ts { if (saveDecoratorContext) { setDecoratorContext(false); } + let node = createNode(SyntaxKind.FunctionExpression); + setModifiers(node, parseModifiers()); parseExpected(SyntaxKind.FunctionKeyword); - node.asteriskToken = parseOptionalToken(SyntaxKind.AsteriskToken); - node.name = node.asteriskToken ? doInYieldContext(parseOptionalIdentifier) : parseOptionalIdentifier(); - fillSignature(SyntaxKind.ColonToken, /*yieldAndGeneratorParameterContext:*/ !!node.asteriskToken, /*requireCompleteParameterList:*/ false, node); - node.body = parseFunctionBlock(/*allowYield:*/ !!node.asteriskToken, /* ignoreMissingOpenBrace */ false); + node.asteriskToken = parseOptionalToken(SyntaxKind.AsteriskToken); + + let savedYieldContext = inYieldContext(); + let isGenerator = node.asteriskToken != undefined; + setYieldContext(isGenerator); + + let savedAwaitContext = inAwaitContext(); + let isAsync = isAsyncFunctionLike(node); + setAwaitContext(isAsync); + + node.name = parseOptionalIdentifier(); + + setYieldContext(savedYieldContext); + setAwaitContext(savedAwaitContext); + + fillSignature(SyntaxKind.ColonToken, /*yieldAndGeneratorParameterContext*/ isGenerator, /*awaitAndAsyncParameterContext*/ isAsync, /*requireCompleteParameterList*/ false, node); + node.body = parseFunctionBlock(/*allowYield*/ isGenerator, /*allowAwait*/ isAsync, /*ignoreMissingOpenBrace*/ false); + if (saveDecoratorContext) { setDecoratorContext(true); } + return finishNode(node); } @@ -3394,9 +3498,12 @@ module ts { return finishNode(node); } - function parseFunctionBlock(allowYield: boolean, ignoreMissingOpenBrace: boolean, diagnosticMessage?: DiagnosticMessage): Block { + function parseFunctionBlock(allowYield: boolean, allowAwait: boolean, ignoreMissingOpenBrace: boolean, diagnosticMessage?: DiagnosticMessage): Block { let savedYieldContext = inYieldContext(); setYieldContext(allowYield); + + let savedAwaitContext = inAwaitContext(); + setAwaitContext(allowAwait); // We may be in a [Decorator] context when parsing a function expression or // arrow function. The body of the function is not in [Decorator] context. @@ -3412,6 +3519,7 @@ module ts { } setYieldContext(savedYieldContext); + setAwaitContext(savedAwaitContext); return block; } @@ -3702,6 +3810,15 @@ module ts { // In ES 6 'enum' is a future reserved keyword, so it should not be used as identifier let isConstEnum = lookAhead(nextTokenIsEnumKeyword); return !isConstEnum; + + case SyntaxKind.AsyncKeyword: + // When followed by the function keyword on the same line, this is a statement + if (lookAhead(nextTokenIsFunctionKeywordOnSameLine)) { + return true; + } + + return isStartOfExpression(); + case SyntaxKind.InterfaceKeyword: case SyntaxKind.ModuleKeyword: case SyntaxKind.NamespaceKeyword: @@ -3835,13 +3952,13 @@ module ts { return undefined; } - function parseFunctionBlockOrSemicolon(isGenerator: boolean, diagnosticMessage?: DiagnosticMessage): Block { + function parseFunctionBlockOrSemicolon(isGenerator: boolean, isAsync: boolean, diagnosticMessage?: DiagnosticMessage): Block { if (token !== SyntaxKind.OpenBraceToken && canParseSemicolon()) { parseSemicolon(); return; } - return parseFunctionBlock(isGenerator, /*ignoreMissingOpenBrace:*/ false, diagnosticMessage); + return parseFunctionBlock(isGenerator, isAsync, /*ignoreMissingOpenBrace:*/ false, diagnosticMessage); } // DECLARATIONS @@ -3853,7 +3970,7 @@ module ts { let node = createNode(SyntaxKind.BindingElement); node.dotDotDotToken = parseOptionalToken(SyntaxKind.DotDotDotToken); node.name = parseIdentifierOrPattern(); - node.initializer = parseInitializer(/*inParameter*/ false); + node.initializer = parseBindingElementInitializer(/*inParameter*/ false); return finishNode(node); } @@ -3870,7 +3987,7 @@ module ts { node.propertyName = propertyName; node.name = parseIdentifierOrPattern(); } - node.initializer = parseInitializer(/*inParameter*/ false); + node.initializer = parseBindingElementInitializer(/*inParameter*/ false); return finishNode(node); } @@ -3976,8 +4093,10 @@ module ts { parseExpected(SyntaxKind.FunctionKeyword); node.asteriskToken = parseOptionalToken(SyntaxKind.AsteriskToken); node.name = node.flags & NodeFlags.Default ? parseOptionalIdentifier() : parseIdentifier(); - fillSignature(SyntaxKind.ColonToken, /*yieldAndGeneratorParameterContext:*/ !!node.asteriskToken, /*requireCompleteParameterList:*/ false, node); - node.body = parseFunctionBlockOrSemicolon(!!node.asteriskToken, Diagnostics.or_expected); + let isGenerator = node.asteriskToken != undefined; + let isAsync = isAsyncFunctionLike(node); + fillSignature(SyntaxKind.ColonToken, /*yieldAndGeneratorParameterContext*/ isGenerator, /*awaitAndAsyncParameterContext*/ isAsync, /*requireCompleteParameterList:*/ false, node); + node.body = parseFunctionBlockOrSemicolon(isGenerator, isAsync, Diagnostics.or_expected); return finishNode(node); } @@ -3986,8 +4105,8 @@ module ts { node.decorators = decorators; setModifiers(node, modifiers); parseExpected(SyntaxKind.ConstructorKeyword); - fillSignature(SyntaxKind.ColonToken, /*yieldAndGeneratorParameterContext:*/ false, /*requireCompleteParameterList:*/ false, node); - node.body = parseFunctionBlockOrSemicolon(/*isGenerator:*/ false, Diagnostics.or_expected); + fillSignature(SyntaxKind.ColonToken, /*yieldAndGeneratorParameterContext*/ false, /*awaitAndAsyncParameterContext*/ false, /*requireCompleteParameterList*/ false, node); + node.body = parseFunctionBlockOrSemicolon(/*isGenerator*/ false, /*isAsync*/ false, Diagnostics.or_expected); return finishNode(node); } @@ -3998,8 +4117,10 @@ module ts { method.asteriskToken = asteriskToken; method.name = name; method.questionToken = questionToken; - fillSignature(SyntaxKind.ColonToken, /*yieldAndGeneratorParameterContext:*/ !!asteriskToken, /*requireCompleteParameterList:*/ false, method); - method.body = parseFunctionBlockOrSemicolon(!!asteriskToken, diagnosticMessage); + let isGenerator = asteriskToken != undefined; + let isAsync = isAsyncFunctionLike(method); + fillSignature(SyntaxKind.ColonToken, /*yieldAndGeneratorParameterContext:*/ isGenerator, /*awaitAndAsyncParameterContext*/ isAsync, /*requireCompleteParameterList:*/ false, method); + method.body = parseFunctionBlockOrSemicolon(isGenerator, isAsync, diagnosticMessage); return finishNode(method); } @@ -4039,8 +4160,8 @@ module ts { node.decorators = decorators; setModifiers(node, modifiers); node.name = parsePropertyName(); - fillSignature(SyntaxKind.ColonToken, /*yieldAndGeneratorParameterContext:*/ false, /*requireCompleteParameterList:*/ false, node); - node.body = parseFunctionBlockOrSemicolon(/*isGenerator:*/ false); + fillSignature(SyntaxKind.ColonToken, /*yieldAndGeneratorParameterContext*/ false, /*awaitAndAsyncParameterContext*/ false, /*requireCompleteParameterList*/ false, node); + node.body = parseFunctionBlockOrSemicolon(/*isGenerator*/ false, /*isAsync*/ false); return finishNode(node); } @@ -4147,14 +4268,14 @@ module ts { return decorators; } - function parseModifiers(): ModifiersArray { + function parseModifiers(isArrowFunction?: boolean): ModifiersArray { let flags = 0; let modifiers: ModifiersArray; while (true) { let modifierStart = scanner.getStartPos(); let modifierKind = token; - if (!parseAnyContextualModifier()) { + if (!parseAnyContextualModifier(isArrowFunction)) { break; } @@ -4228,7 +4349,7 @@ module ts { function parseClassDeclaration(fullStart: number, decorators: NodeArray, modifiers: ModifiersArray): ClassDeclaration { return parseClassDeclarationOrExpression(fullStart, decorators, modifiers, SyntaxKind.ClassDeclaration); } - + function parseClassDeclarationOrExpression(fullStart: number, decorators: NodeArray, modifiers: ModifiersArray, kind: SyntaxKind): ClassLikeDeclaration { // In ES6 specification, All parts of a ClassDeclaration or a ClassExpression are strict mode code let savedStrictModeContext = inStrictModeContext(); @@ -4243,13 +4364,15 @@ module ts { node.heritageClauses = parseHeritageClauses(/*isClassHeritageClause:*/ true); if (parseExpected(SyntaxKind.OpenBraceToken)) { - // ClassTail[Yield,GeneratorParameter] : See 14.5 - // [~GeneratorParameter]ClassHeritage[?Yield]opt { ClassBody[?Yield]opt } + // ClassTail[Yield,GeneratorParameter,Await,AsyncParameter] : (Modified) See 14.5 + // [~GeneratorParameter,~AsyncParameter]ClassHeritage[?Yield,?Await]opt { ClassBody[?Yield,?Await]opt } // [+GeneratorParameter] ClassHeritageopt { ClassBodyopt } - - node.members = inGeneratorParameterContext() - ? doOutsideOfYieldContext(parseClassMembers) + // [+AsyncParameter] ClassHeritageopt { ClassBodyopt } + + node.members = inGeneratorParameterOrAsyncParameterContext() + ? doOutsideOfYieldAndAwaitContext(parseClassMembers) : parseClassMembers(); + parseExpected(SyntaxKind.CloseBraceToken); } else { @@ -4262,13 +4385,14 @@ module ts { } function parseHeritageClauses(isClassHeritageClause: boolean): NodeArray { - // ClassTail[Yield,GeneratorParameter] : See 14.5 - // [~GeneratorParameter]ClassHeritage[?Yield]opt { ClassBody[?Yield]opt } + // ClassTail[Yield,GeneratorParameter,Await,AsyncParameter] : (Modified) See 14.5 + // [~GeneratorParameter,~AsyncParameter]ClassHeritage[?Yield,?Await]opt { ClassBody[?Yield,?Await]opt } // [+GeneratorParameter] ClassHeritageopt { ClassBodyopt } + // [+AsyncParameter] ClassHeritageopt { ClassBodyopt } if (isHeritageClause()) { - return isClassHeritageClause && inGeneratorParameterContext() - ? doOutsideOfYieldContext(parseHeritageClausesWorker) + return isClassHeritageClause && inGeneratorParameterOrAsyncParameterContext() + ? doOutsideOfYieldAndAwaitContext(parseHeritageClausesWorker) : parseHeritageClausesWorker(); } @@ -4652,6 +4776,11 @@ module ts { case SyntaxKind.StaticKeyword: // Check for modifier on source element return lookAhead(nextTokenIsDeclarationStart); + + case SyntaxKind.AsyncKeyword: + // Check for modifier on source element + return lookAhead(nextTokenIsDeclarationStartOnSameLine); + case SyntaxKind.AtToken: // a lookahead here is too costly, and decorators are only valid on a declaration. // We will assume we are parsing a declaration here and report an error later @@ -4690,6 +4819,16 @@ module ts { return isDeclarationStart(/*followsModifier*/ true); } + function nextTokenIsDeclarationStartOnSameLine() { + nextToken(); + return !scanner.hasPrecedingLineBreak() && isDeclarationStart(); + } + + function nextTokenIsFunctionKeywordOnSameLine() { + nextToken(); + return !scanner.hasPrecedingLineBreak() && token === SyntaxKind.FunctionKeyword; + } + function nextTokenIsAsKeyword() { return nextToken() === SyntaxKind.AsKeyword; } diff --git a/src/compiler/scanner.ts b/src/compiler/scanner.ts index 1fc8bff92e6..2dc85174cd9 100644 --- a/src/compiler/scanner.ts +++ b/src/compiler/scanner.ts @@ -101,6 +101,8 @@ module ts { "while": SyntaxKind.WhileKeyword, "with": SyntaxKind.WithKeyword, "yield": SyntaxKind.YieldKeyword, + "async": SyntaxKind.AsyncKeyword, + "await": SyntaxKind.AwaitKeyword, "of": SyntaxKind.OfKeyword, "{": SyntaxKind.OpenBraceToken, "}": SyntaxKind.CloseBraceToken, diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 86e680ca8b0..b23b46bc9bd 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -133,6 +133,8 @@ module ts { // Contextual keywords AsKeyword, AnyKeyword, + AsyncKeyword, + AwaitKeyword, BooleanKeyword, ConstructorKeyword, DeclareKeyword, @@ -197,6 +199,7 @@ module ts { DeleteExpression, TypeOfExpression, VoidExpression, + AwaitExpression, PrefixUnaryExpression, PostfixUnaryExpression, BinaryExpression, @@ -316,8 +319,9 @@ module ts { OctalLiteral = 0x00004000, // Octal numeric literal Namespace = 0x00008000, // Namespace declaration ExportContext = 0x00010000, // Export context (initialized by binding) + Async = 0x00020000, // Property/Method/Function - Modifier = Export | Ambient | Public | Private | Protected | Static | Default, + Modifier = Export | Ambient | Public | Private | Protected | Static | Default | Async, AccessibilityModifier = Public | Private | Protected, BlockScoped = Let | Const } @@ -345,17 +349,29 @@ module ts { // error. ThisNodeHasError = 1 << 5, - // Context flags set directly by the parser. - ParserGeneratedFlags = StrictMode | DisallowIn | Yield | GeneratorParameter | Decorator | ThisNodeHasError, + // If this node was parsed in the parameters of an async function. + AsyncParameter = 1 << 6, + // If this node was parsed in the 'await' context created when parsing an async function. + Await = 1 << 7, + + // Context flags set directly by the parser. + ParserGeneratedFlags = StrictMode | DisallowIn | Yield | GeneratorParameter | Decorator | ThisNodeHasError | AsyncParameter | Await, + + // Context flags passed as part of the modified ES6 grammar. + ContextParameterFlags = Yield | GeneratorParameter | AsyncParameter | Await, + // Context flags computed by aggregating child flags upwards. // Used during incremental parsing to determine if this node or any of its children had an // error. Computed only once and then cached. - ThisNodeOrAnySubNodesHasError = 1 << 6, + ThisNodeOrAnySubNodesHasError = 1 << 8, + + // Used to know if we've computed whether any children of this node are or contain an 'await' or 'yield' expression. + ThisNodeOrAnySubNodesHasAwaitOrYield = 1 << 9, // Used to know if we've computed data from children and cached it in this node. - HasAggregatedChildData = 1 << 7 + HasAggregatedChildData = 1 << 10 } /* @internal */ @@ -658,6 +674,10 @@ module ts { expression: UnaryExpression; } + export interface AwaitExpression extends UnaryExpression { + expression: UnaryExpression; + } + export interface YieldExpression extends Expression { asteriskToken?: Node; expression: Expression; @@ -1403,22 +1423,28 @@ module ts { TypeChecked = 0x00000001, // Node has been type checked LexicalThis = 0x00000002, // Lexical 'this' reference CaptureThis = 0x00000004, // Lexical 'this' used in body - EmitExtends = 0x00000008, // Emit __extends - SuperInstance = 0x00000010, // Instance 'super' reference - SuperStatic = 0x00000020, // Static 'super' reference - ContextChecked = 0x00000040, // Contextual types have been assigned + LexicalArguments = 0x00000008, // Lexical 'arguments' reference + CaptureArguments = 0x00000010, // Lexical 'arguments' used in body + EmitExtends = 0x00000020, // Emit __extends + EmitDecorate = 0x00000040, // Emit __decorate + EmitParam = 0x00000080, // Emit __param helper for decorators + EmitAwaiter = 0x00000100, // Emit __awaiter + EmitGenerator = 0x00000200, // Emit __generator + SuperInstance = 0x00000400, // Instance 'super' reference + SuperStatic = 0x00000800, // Static 'super' reference + ContextChecked = 0x00001000, // Contextual types have been assigned + PromiseCollision = 0x00002000, // Declaration collides with the global 'Promise' // Values for enum members have been computed, and any errors have been reported for them. - EnumValuesComputed = 0x00000080, - BlockScopedBindingInLoop = 0x00000100, - EmitDecorate = 0x00000200, // Emit __decorate - EmitParam = 0x00000400, // Emit __param helper for decorators - LexicalModuleMergesWithClass = 0x00000800, // Instantiated lexical module declaration is merged with a previous class declaration. + EnumValuesComputed = 0x00004000, + BlockScopedBindingInLoop = 0x00008000, + LexicalModuleMergesWithClass= 0x00010000, // Instantiated lexical module declaration is merged with a previous class declaration. } /* @internal */ export interface NodeLinks { resolvedType?: Type; // Cached type of type node + resolvedAwaitedType?: Type; // Cached awaited type of type node resolvedSignature?: Signature; // Cached signature of signature node or call expression resolvedSymbol?: Symbol; // Cached name resolution result flags?: NodeCheckFlags; // Set of flags specific to Node diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 3e825c6802c..e5856077edc 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -511,6 +511,19 @@ module ts { } } + export function getContainingParameter(node: Node): ParameterDeclaration { + while (true) { + node = node.parent; + if (!node || isFunctionLike(node)) { + return undefined; + } + + if (node.kind === SyntaxKind.Parameter) { + return node; + } + } + } + export function getThisContainer(node: Node, includeArrowFunctions: boolean): Node { while (true) { node = node.parent; @@ -1087,6 +1100,10 @@ module ts { return SyntaxKind.FirstTriviaToken <= token && token <= SyntaxKind.LastTriviaToken; } + export function isAsyncFunctionLike(node: Node): boolean { + return isFunctionLike(node) && !isAccessor(node) && (node.flags & NodeFlags.Async) === NodeFlags.Async; + } + /** * A declaration has a dynamic name if both of the following are true: * 1. The declaration has a computed property name @@ -1145,6 +1162,7 @@ module ts { case SyntaxKind.DeclareKeyword: case SyntaxKind.ConstKeyword: case SyntaxKind.DefaultKeyword: + case SyntaxKind.AsyncKeyword: return true; } return false; @@ -1630,6 +1648,7 @@ module ts { case SyntaxKind.DeclareKeyword: return NodeFlags.Ambient; case SyntaxKind.ConstKeyword: return NodeFlags.Const; case SyntaxKind.DefaultKeyword: return NodeFlags.Default; + case SyntaxKind.AsyncKeyword: return NodeFlags.Async; } return 0; } diff --git a/src/lib/core.d.ts b/src/lib/core.d.ts index c03ab344ab2..0405774d99d 100644 --- a/src/lib/core.d.ts +++ b/src/lib/core.d.ts @@ -1169,3 +1169,15 @@ declare type ClassDecorator = (target: TFunction) => declare type PropertyDecorator = (target: Object, propertyKey: string | symbol) => void; declare type MethodDecorator = (target: Object, propertyKey: string | symbol, descriptor: TypedPropertyDescriptor) => TypedPropertyDescriptor | void; declare type ParameterDecorator = (target: Object, propertyKey: string | symbol, parameterIndex: number) => void; + +declare type PromiseConstructorLike = new (executor: (resolve: (value?: T | PromiseLike) => void, reject: (reason?: any) => void) => void) => PromiseLike; + +interface PromiseLike { + /** + * Attaches callbacks for the resolution and/or rejection of the Promise. + * @param onfulfilled The callback to execute when the Promise is resolved. + * @param onrejected The callback to execute when the Promise is rejected. + * @returns A Promise for the completion of which ever callback is executed. + */ + then(onfulfilled?: (value: T) => TResult | PromiseLike, onrejected?: (reason: any) => TResult | PromiseLike): PromiseLike; +} \ No newline at end of file diff --git a/src/lib/es6.d.ts b/src/lib/es6.d.ts index fb0c64f8495..ff3c517fe45 100644 --- a/src/lib/es6.d.ts +++ b/src/lib/es6.d.ts @@ -3572,16 +3572,6 @@ declare module Reflect { function setPrototypeOf(target: any, proto: any): boolean; } -interface PromiseLike { - /** - * Attaches callbacks for the resolution and/or rejection of the Promise. - * @param onfulfilled The callback to execute when the Promise is resolved. - * @param onrejected The callback to execute when the Promise is rejected. - * @returns A Promise for the completion of which ever callback is executed. - */ - then(onfulfilled?: (value: T) => TResult | PromiseLike, onrejected?: (reason: any) => TResult | PromiseLike): PromiseLike; -} - /** * Represents the completion of an asynchronous operation */