Cleaned up async return type check

This commit is contained in:
Ron Buckton
2015-06-18 11:31:03 -07:00
parent 7443ecc6a5
commit 2891a1d1b7
34 changed files with 534 additions and 172 deletions
+32 -54
View File
@@ -824,7 +824,7 @@ namespace ts {
}
// Resolves a qualified name and any involved aliases
function resolveEntityName(name: EntityName | Expression, meaning: SymbolFlags, location?: Node): Symbol {
function resolveEntityName(name: EntityName | Expression, meaning: SymbolFlags): Symbol {
if (nodeIsMissing(name)) {
return undefined;
}
@@ -833,7 +833,7 @@ namespace ts {
if (name.kind === SyntaxKind.Identifier) {
let message = meaning === SymbolFlags.Namespace ? Diagnostics.Cannot_find_namespace_0 : Diagnostics.Cannot_find_name_0;
symbol = resolveName(location || name, (<Identifier>name).text, meaning, message, <Identifier>name);
symbol = resolveName(name, (<Identifier>name).text, meaning, message, <Identifier>name);
if (!symbol) {
return undefined;
}
@@ -842,7 +842,7 @@ namespace ts {
let left = name.kind === SyntaxKind.QualifiedName ? (<QualifiedName>name).left : (<PropertyAccessExpression>name).expression;
let right = name.kind === SyntaxKind.QualifiedName ? (<QualifiedName>name).right : (<PropertyAccessExpression>name).name;
let namespace = resolveEntityName(left, SymbolFlags.Namespace, location);
let namespace = resolveEntityName(left, SymbolFlags.Namespace);
if (!namespace || namespace === unknownSymbol || nodeIsMissing(right)) {
return undefined;
}
@@ -8839,7 +8839,6 @@ namespace ts {
}
if (produceDiagnostics) {
checkCollisionWithAwaiterVariablesInGeneratedCode(node, node.name);
checkCollisionWithArgumentsInGeneratedCode(node);
if (compilerOptions.noImplicitAny && !node.type) {
switch (node.kind) {
@@ -9632,7 +9631,7 @@ namespace ts {
* a `resolve` function as one of its arguments and results in an object with a
* callable `then` signature.
*/
function checkAsyncFunctionReturnType(node: SignatureDeclaration): Type {
function checkAsyncFunctionReturnType(node: FunctionLikeDeclaration): Type {
let globalPromiseConstructorLikeType = getGlobalPromiseConstructorLikeType();
if (globalPromiseConstructorLikeType === emptyObjectType) {
// If we couldn't resolve the global PromiseConstructorLike type we cannot verify
@@ -9640,10 +9639,10 @@ namespace ts {
return unknownType;
}
// The return type of an async function will be the type of the instance. For this
// to be a type compatible with our async function emit, we must also check that
// the type of the declaration (e.g. the static side or "constructor" type of the
// promise) is a compatible `PromiseConstructorLike`.
// As part of our emit for an async function, we will need to emit the entity name of
// the return type annotation as an expression. To meet the necessary runtime semantics
// for __awaiter, we must also check that the type of the declaration (e.g. the static
// side or "constructor" of the promise type) is compatible `PromiseConstructorLike`.
//
// An example might be (from lib.es6.d.ts):
//
@@ -9666,36 +9665,33 @@ namespace ts {
//
// When we get the type of the `Promise` symbol here, we get the type of the static
// side of the `Promise` class, which would be `{ new <T>(...): Promise<T> }`.
let returnType = getTypeFromTypeNode(node.type);
let entityName = getEntityNameFromTypeNode(node.type);
let resolvedName = entityName ? resolveEntityName(entityName, SymbolFlags.Value, node) : undefined;
if (!resolvedName || !returnType.symbol) {
error(node, Diagnostics.An_async_function_or_method_must_have_a_valid_awaitable_return_type);
return unknownType;
}
if (getMergedSymbol(resolvedName) !== getMergedSymbol(returnType.symbol)) {
// If we were unable to resolve the return type as a value, report an error.
let identifier = getFirstIdentifier(entityName);
error(resolvedName.valueDeclaration, Diagnostics.Duplicate_identifier_0_Compiler_uses_declaration_1_to_support_async_functions,
identifier.text,
identifier.text);
return unknownType;
}
// When we emit the async function, we need to ensure we emit any imports that might
// otherwise have been elided if the return type were only ever referenced in a type
// position. As such, we check the entity name as an expression.
let declaredType = checkExpression(entityName);
if (!isTypeAssignableTo(declaredType, globalPromiseConstructorLikeType)) {
// If the declared type of the return type is not assignable to a PromiseConstructorLike, report an error.
error(node, ts.Diagnostics.An_async_function_or_method_must_have_a_valid_awaitable_return_type);
return unknownType;
let promiseType = getTypeFromTypeNode(node.type);
let promiseConstructor = getMergedSymbol(promiseType.symbol);
if (!promiseConstructor || !symbolIsValue(promiseConstructor)) {
error(node, Diagnostics.Type_0_is_not_a_valid_async_function_return_type, typeToString(promiseType));
return unknownType
}
// Validate the promise constructor type.
let promiseConstructorType = getTypeOfSymbol(promiseConstructor);
if (!checkTypeAssignableTo(promiseConstructorType, globalPromiseConstructorLikeType, node, Diagnostics.Type_0_is_not_a_valid_async_function_return_type)) {
return unknownType;
}
// Verify there is no local declaration that could collide with the promise constructor.
let promiseName = getEntityNameFromTypeNode(node.type);
let root = getFirstIdentifier(promiseName);
let rootSymbol = getSymbol(node.locals, root.text, SymbolFlags.Value);
if (rootSymbol) {
error(rootSymbol.valueDeclaration, Diagnostics.Duplicate_identifier_0_Compiler_uses_declaration_1_to_support_async_functions,
root.text,
getFullyQualifiedName(promiseConstructor));
return unknownType;
}
// Get and return the awaited type of the return type.
return getAwaitedType(returnType, node, Diagnostics.An_async_function_or_method_must_have_a_valid_awaitable_return_type);
return getAwaitedType(promiseType, node, Diagnostics.An_async_function_or_method_must_have_a_valid_awaitable_return_type);
}
/** Check a decorator */
@@ -10093,22 +10089,6 @@ namespace ts {
}
}
}
function checkCollisionWithAwaiterVariablesInGeneratedCode(node: Node, name: DeclarationName): void {
if (!name || name.kind !== SyntaxKind.Identifier || isTypeNode(name)) {
return;
}
let identifier = <Identifier>name;
let container = getContainingFunction(name);
if (container && isAsyncFunctionLike(container) && node.kind !== SyntaxKind.Identifier) {
let promiseConstructorName = getEntityNameFromTypeNode(container.type);
let firstIdentifier = promiseConstructorName ? getFirstIdentifier(promiseConstructorName) : undefined;
if (firstIdentifier && firstIdentifier.text === identifier.text) {
error(node, Diagnostics.Duplicate_identifier_0_Compiler_uses_declaration_1_to_support_async_functions, identifier.text, getTextOfNode(promiseConstructorName));
}
}
}
// Check that a parameter initializer contains no references to parameters declared to the right of itself
function checkParameterInitializer(node: VariableLikeDeclaration): void {
@@ -10954,7 +10934,6 @@ namespace 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);
@@ -11398,7 +11377,6 @@ namespace 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);
@@ -48,6 +48,7 @@ namespace ts {
A_set_accessor_parameter_cannot_have_an_initializer: { code: 1052, category: DiagnosticCategory.Error, key: "A 'set' accessor parameter cannot have an initializer." },
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." },
Type_0_is_not_a_valid_async_function_return_type: { code: 1055, category: DiagnosticCategory.Error, key: "Type '{0}' is not a valid async function return type." },
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." },
Operand_for_await_does_not_have_a_valid_callable_then_member: { code: 1058, category: DiagnosticCategory.Error, key: "Operand for 'await' does not have a valid callable 'then' member." },
+4
View File
@@ -179,6 +179,10 @@
"category": "Error",
"code": 1054
},
"Type '{0}' is not a valid async function return type.": {
"category": "Error",
"code": 1055
},
"Accessors are only available when targeting ECMAScript 5 and higher.": {
"category": "Error",
"code": 1056
+37 -36
View File
@@ -49,10 +49,10 @@ var __param = (this && this.__param) || function (paramIndex, decorator) {
};`;
const awaiterHelper = `
var __awaiter = (this && this.__awaiter) || function (generator, thisArg, args, PromiseConstructor) {
PromiseConstructor || (PromiseConstructor = Promise);
var __awaiter = (this && this.__awaiter) || function (args, generator) {
var PromiseConstructor = args[1] || Promise;
return new PromiseConstructor(function (resolve, reject) {
generator = generator.call(thisArg, args);
generator = generator.call(args[0], args[2]);
function cast(value) { return value instanceof PromiseConstructor ? value : new PromiseConstructor(function (resolve) { resolve(value); }); }
function onfulfill(value) { try { step("next", value); } catch (e) { reject(e); } }
function onreject(value) { try { step("throw", value); } catch (e) { reject(e); } }
@@ -3359,6 +3359,7 @@ var __awaiter = (this && this.__awaiter) || function (generator, thisArg, args,
function emitAsyncFunctionBodyForES6(node: FunctionLikeDeclaration) {
let promiseConstructor = getEntityNameFromTypeNode(node.type);
let isArrowFunction = node.kind === SyntaxKind.ArrowFunction;
let hasLexicalArguments = (resolver.getNodeCheckFlags(node) & NodeCheckFlags.CaptureArguments) !== 0;
let args: string;
// An async function is emit as an outer function that calls an inner
@@ -3373,7 +3374,7 @@ var __awaiter = (this && this.__awaiter) || function (generator, thisArg, args,
// let a = async (b) => { await b; }
//
// // output
// let a = (b) => __awaiter(function* (b) {
// let a = (b) => __awaiter([this], function* (b) {
// yield b;
// }, this);
//
@@ -3383,9 +3384,9 @@ var __awaiter = (this && this.__awaiter) || function (generator, thisArg, args,
// let a = async (b) => { await arguments[0]; }
//
// // output
// let a = (b) => __awaiter(function* (arguments) {
// let a = (b) => __awaiter([this, arguments], function* (arguments) {
// yield arguments[0];
// }, this, arguments);
// });
//
// The emit for an async function expression without a lexical `arguments` binding
// might be:
@@ -3397,7 +3398,7 @@ var __awaiter = (this && this.__awaiter) || function (generator, thisArg, args,
//
// // output
// let a = function (b) {
// return __awaiter(function* () {
// return __awaiter([this], function* () {
// yield b;
// }, this);
// }
@@ -3412,9 +3413,24 @@ var __awaiter = (this && this.__awaiter) || function (generator, thisArg, args,
//
// // output
// let a = function (b) {
// return __awaiter(function* (arguments) {
// return __awaiter([this, arguments], function* (arguments) {
// yield arguments[0];
// }, this, arguments);
// });
// }
//
// The emit for an async function expression with a lexical `arguments` binding
// and a return type annotation might be:
//
// // input
// let a = async function (b): MyPromise<any> {
// await arguments[0];
// }
//
// // output
// let a = function (b) {
// return __awaiter([this, arguments, MyPromise], function* (arguments) {
// yield arguments[0];
// });
// }
//
@@ -3427,42 +3443,27 @@ var __awaiter = (this && this.__awaiter) || function (generator, thisArg, args,
write("return");
}
write(" __awaiter([this");
if (promiseConstructor || hasLexicalArguments) {
write(", ");
if (promiseConstructor) {
emitNodeWithoutSourceMap(promiseConstructor);
}
if (hasLexicalArguments) {
write(", arguments");
}
}
// Emit the call to __awaiter.
let hasLexicalArguments = (resolver.getNodeCheckFlags(node) & NodeCheckFlags.CaptureArguments) !== 0;
if (hasLexicalArguments) {
write(" __awaiter(function* (arguments)");
write("], function* (arguments)");
}
else {
write(" __awaiter(function* ()");
write("], function* ()");
}
// Emit the signature and body for the inner generator function.
emitFunctionBody(node);
// Emit the current `this` binding.
write(",");
writeLine();
write("this");
// Optionally emit the lexical arguments.
if (hasLexicalArguments) {
write(", arguments");
}
// If the function has an explicit type annotation for a promise, emit the
// constructor.
if (promiseConstructor) {
// If we did not have lexical arguments, supply undefined (void 0) for
// the `arguments` parameter.
if (!hasLexicalArguments) {
write(", void 0");
}
write(", ");
emitNodeWithoutSourceMap(promiseConstructor);
}
write(")");
// If this is not an async arrow, emit the closing brace of the outer function body.