From 703af074ff77a10e6468e3c367b2c7a96c93ebb1 Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Tue, 6 Oct 2015 16:44:48 -0700 Subject: [PATCH] Fixes some checker errors regarding async functions. Fixes #5115. --- src/compiler/checker.ts | 28 ++++--- .../asyncFunctionDeclaration15_es6.errors.txt | 56 +++++++++++++ .../asyncFunctionDeclaration15_es6.js | 84 +++++++++++++++++++ .../asyncFunctionDeclaration15_es6.ts | 26 ++++++ 4 files changed, 183 insertions(+), 11 deletions(-) create mode 100644 tests/baselines/reference/asyncFunctionDeclaration15_es6.errors.txt create mode 100644 tests/baselines/reference/asyncFunctionDeclaration15_es6.js create mode 100644 tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration15_es6.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index c1d8bd991c4..551f428ca9a 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -2209,7 +2209,7 @@ namespace ts { /** * Push an entry on the type resolution stack. If an entry with the given target and the given property name - * is already on the stack, and no entries in between already have a type, then a circularity has occurred. + * is already on the stack, and no entries in between already have a type, then a circularity has occurred. * In this case, the result values of the existing entry and all entries pushed after it are changed to false, * and the value false is returned. Otherwise, the new entry is just pushed onto the stack, and true is returned. * In order to see if the same query has already been done before, the target object and the propertyName both @@ -5243,7 +5243,7 @@ namespace ts { // Only want to compare the construct signatures for abstractness guarantees. // Because the "abstractness" of a class is the same across all construct signatures - // (internally we are checking the corresponding declaration), it is enough to perform + // (internally we are checking the corresponding declaration), it is enough to perform // the check and report an error once over all pairs of source and target construct signatures. // // sourceSig and targetSig are (possibly) undefined. @@ -6642,7 +6642,7 @@ namespace ts { let needToCaptureLexicalThis = false; if (!isCallExpression) { - // adjust the container reference in case if super is used inside arrow functions with arbitrary deep nesting + // adjust the container reference in case if super is used inside arrow functions with arbitrary deep nesting while (container && container.kind === SyntaxKind.ArrowFunction) { container = getSuperContainer(container, /*includeFunctions*/ true); needToCaptureLexicalThis = languageVersion < ScriptTarget.ES6; @@ -6652,7 +6652,7 @@ namespace ts { let canUseSuperExpression = isLegalUsageOfSuperExpression(container); let nodeCheckFlag: NodeCheckFlags = 0; - // always set NodeCheckFlags for 'super' expression node + // always set NodeCheckFlags for 'super' expression node if (canUseSuperExpression) { if ((container.flags & NodeFlags.Static) || isCallExpression) { nodeCheckFlag = NodeCheckFlags.SuperStatic; @@ -8568,7 +8568,7 @@ namespace ts { // A method or accessor declaration decorator will have two or three arguments (see // `PropertyDecorator` and `MethodDecorator` in core.d.ts) - // If we are emitting decorators for ES3, we will only pass two arguments. + // If we are emitting decorators for ES3, we will only pass two arguments. if (languageVersion === ScriptTarget.ES3) { return 2; } @@ -11306,7 +11306,8 @@ namespace ts { } function checkNonThenableType(type: Type, location?: Node, message?: DiagnosticMessage) { - if (!(type.flags & TypeFlags.Any) && isTypeAssignableTo(type, getGlobalThenableType())) { + type = getWidenedType(type); + if (!isTypeAny(type) && isTypeAssignableTo(type, getGlobalThenableType())) { if (location) { if (!message) { message = Diagnostics.Operand_for_await_does_not_have_a_valid_callable_then_member; @@ -12544,7 +12545,12 @@ namespace ts { if (isAsyncFunctionLike(func)) { let promisedType = getPromisedType(returnType); let awaitedType = checkAwaitedType(exprType, node.expression, Diagnostics.Return_expression_in_async_function_does_not_have_a_valid_callable_then_member); - checkTypeAssignableTo(awaitedType, promisedType, node.expression); + if (promisedType) { + // If the function has a return type, but promisedType is + // undefined, an error will be reported in checkAsyncFunctionReturnType + // so we don't need to report one here. + checkTypeAssignableTo(awaitedType, promisedType, node.expression); + } } else { checkTypeAssignableTo(exprType, returnType, node.expression); @@ -13154,13 +13160,13 @@ namespace ts { autoValue = computeConstantValueForEnumMemberInitializer(initializer, enumType, enumIsConst, ambient); } else if (ambient && !enumIsConst) { - // In ambient enum declarations that specify no const modifier, enum member declarations + // In ambient enum declarations that specify no const modifier, enum member declarations // that omit a value are considered computed members (as opposed to having auto-incremented values assigned). autoValue = undefined; } else if (previousEnumMemberIsNonConstant) { - // If the member declaration specifies no value, the member is considered a constant enum member. - // If the member is the first member in the enum declaration, it is assigned the value zero. + // If the member declaration specifies no value, the member is considered a constant enum member. + // If the member is the first member in the enum declaration, it is assigned the value zero. // Otherwise, it is assigned the value of the immediately preceding member plus one, // and an error occurs if the immediately preceding member is not a constant enum member error(member.name, Diagnostics.Enum_member_must_have_initializer); @@ -14100,7 +14106,7 @@ namespace ts { case SyntaxKind.ClassDeclaration: case SyntaxKind.InterfaceDeclaration: // If we didn't come from static member of class or interface, - // add the type parameters into the symbol table + // add the type parameters into the symbol table // (type parameters of classDeclaration/classExpression and interface are in member property of the symbol. // Note: that the memberFlags come from previous iteration. if (!(memberFlags & NodeFlags.Static)) { diff --git a/tests/baselines/reference/asyncFunctionDeclaration15_es6.errors.txt b/tests/baselines/reference/asyncFunctionDeclaration15_es6.errors.txt new file mode 100644 index 00000000000..8c029eaaaf4 --- /dev/null +++ b/tests/baselines/reference/asyncFunctionDeclaration15_es6.errors.txt @@ -0,0 +1,56 @@ +tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration15_es6.ts(6,16): error TS1055: Type '{}' is not a valid async function return type. +tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration15_es6.ts(7,16): error TS1055: Type 'any' is not a valid async function return type. +tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration15_es6.ts(8,16): error TS1055: Type 'number' is not a valid async function return type. +tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration15_es6.ts(9,16): error TS1055: Type 'PromiseLike' is not a valid async function return type. +tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration15_es6.ts(10,16): error TS1055: Type 'typeof Thenable' is not a valid async function return type. + Type 'Thenable' is not assignable to type 'PromiseLike'. + Types of property 'then' are incompatible. + Type '() => void' is not assignable to type '{ (onfulfilled?: (value: any) => TResult | PromiseLike, onrejected?: (reason: any) => TResult | PromiseLike): PromiseLike; (onfulfilled?: (value: any) => TResult | PromiseLike, onrejected?: (reason: any) => void): PromiseLike; }'. + Type 'void' is not assignable to type 'PromiseLike'. +tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration15_es6.ts(17,16): error TS1059: Return expression in async function does not have a valid callable 'then' member. +tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration15_es6.ts(23,25): error TS1058: Operand for 'await' does not have a valid callable 'then' member. + + +==== tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration15_es6.ts (7 errors) ==== + declare class Thenable { then(): void; } + declare let a: any; + declare let obj: { then: string; }; + declare let thenable: Thenable; + async function fn1() { } // valid: Promise + async function fn2(): { } { } // error + ~~~ +!!! error TS1055: Type '{}' is not a valid async function return type. + async function fn3(): any { } // error + ~~~ +!!! error TS1055: Type 'any' is not a valid async function return type. + async function fn4(): number { } // error + ~~~ +!!! error TS1055: Type 'number' is not a valid async function return type. + async function fn5(): PromiseLike { } // error + ~~~ +!!! error TS1055: Type 'PromiseLike' is not a valid async function return type. + async function fn6(): Thenable { } // error + ~~~ +!!! error TS1055: Type 'typeof Thenable' is not a valid async function return type. +!!! error TS1055: Type 'Thenable' is not assignable to type 'PromiseLike'. +!!! error TS1055: Types of property 'then' are incompatible. +!!! error TS1055: Type '() => void' is not assignable to type '{ (onfulfilled?: (value: any) => TResult | PromiseLike, onrejected?: (reason: any) => TResult | PromiseLike): PromiseLike; (onfulfilled?: (value: any) => TResult | PromiseLike, onrejected?: (reason: any) => void): PromiseLike; }'. +!!! error TS1055: Type 'void' is not assignable to type 'PromiseLike'. + async function fn7() { return; } // valid: Promise + async function fn8() { return 1; } // valid: Promise + async function fn9() { return null; } // valid: Promise + async function fn10() { return undefined; } // valid: Promise + async function fn11() { return a; } // valid: Promise + async function fn12() { return obj; } // valid: Promise<{ then: string; }> + async function fn13() { return thenable; } // error + ~~~~ +!!! error TS1059: Return expression in async function does not have a valid callable 'then' member. + async function fn14() { await 1; } // valid: Promise + async function fn15() { await null; } // valid: Promise + async function fn16() { await undefined; } // valid: Promise + async function fn17() { await a; } // valid: Promise + async function fn18() { await obj; } // valid: Promise + async function fn19() { await thenable; } // error + ~~~~~~~~~~~~~~ +!!! error TS1058: Operand for 'await' does not have a valid callable 'then' member. + \ No newline at end of file diff --git a/tests/baselines/reference/asyncFunctionDeclaration15_es6.js b/tests/baselines/reference/asyncFunctionDeclaration15_es6.js new file mode 100644 index 00000000000..c72062e24b2 --- /dev/null +++ b/tests/baselines/reference/asyncFunctionDeclaration15_es6.js @@ -0,0 +1,84 @@ +//// [asyncFunctionDeclaration15_es6.ts] +declare class Thenable { then(): void; } +declare let a: any; +declare let obj: { then: string; }; +declare let thenable: Thenable; +async function fn1() { } // valid: Promise +async function fn2(): { } { } // error +async function fn3(): any { } // error +async function fn4(): number { } // error +async function fn5(): PromiseLike { } // error +async function fn6(): Thenable { } // error +async function fn7() { return; } // valid: Promise +async function fn8() { return 1; } // valid: Promise +async function fn9() { return null; } // valid: Promise +async function fn10() { return undefined; } // valid: Promise +async function fn11() { return a; } // valid: Promise +async function fn12() { return obj; } // valid: Promise<{ then: string; }> +async function fn13() { return thenable; } // error +async function fn14() { await 1; } // valid: Promise +async function fn15() { await null; } // valid: Promise +async function fn16() { await undefined; } // valid: Promise +async function fn17() { await a; } // valid: Promise +async function fn18() { await obj; } // valid: Promise +async function fn19() { await thenable; } // error + + +//// [asyncFunctionDeclaration15_es6.js] +function fn1() { + return __awaiter(this, void 0, Promise, function* () { }); +} // valid: Promise +function fn2() { + return __awaiter(this, void 0, Promise, function* () { }); +} // error +function fn3() { + return __awaiter(this, void 0, Promise, function* () { }); +} // error +function fn4() { + return __awaiter(this, void 0, Promise, function* () { }); +} // error +function fn5() { + return __awaiter(this, void 0, PromiseLike, function* () { }); +} // error +function fn6() { + return __awaiter(this, void 0, Thenable, function* () { }); +} // error +function fn7() { + return __awaiter(this, void 0, Promise, function* () { return; }); +} // valid: Promise +function fn8() { + return __awaiter(this, void 0, Promise, function* () { return 1; }); +} // valid: Promise +function fn9() { + return __awaiter(this, void 0, Promise, function* () { return null; }); +} // valid: Promise +function fn10() { + return __awaiter(this, void 0, Promise, function* () { return undefined; }); +} // valid: Promise +function fn11() { + return __awaiter(this, void 0, Promise, function* () { return a; }); +} // valid: Promise +function fn12() { + return __awaiter(this, void 0, Promise, function* () { return obj; }); +} // valid: Promise<{ then: string; }> +function fn13() { + return __awaiter(this, void 0, Promise, function* () { return thenable; }); +} // error +function fn14() { + return __awaiter(this, void 0, Promise, function* () { yield 1; }); +} // valid: Promise +function fn15() { + return __awaiter(this, void 0, Promise, function* () { yield null; }); +} // valid: Promise +function fn16() { + return __awaiter(this, void 0, Promise, function* () { yield undefined; }); +} // valid: Promise +function fn17() { + return __awaiter(this, void 0, Promise, function* () { yield a; }); +} // valid: Promise +function fn18() { + return __awaiter(this, void 0, Promise, function* () { yield obj; }); +} // valid: Promise +function fn19() { + return __awaiter(this, void 0, Promise, function* () { yield thenable; }); +} // error diff --git a/tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration15_es6.ts b/tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration15_es6.ts new file mode 100644 index 00000000000..56bcf84911a --- /dev/null +++ b/tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration15_es6.ts @@ -0,0 +1,26 @@ +// @target: ES6 +// @noEmitHelpers: true +// @experimentalAsyncFunctions: true +declare class Thenable { then(): void; } +declare let a: any; +declare let obj: { then: string; }; +declare let thenable: Thenable; +async function fn1() { } // valid: Promise +async function fn2(): { } { } // error +async function fn3(): any { } // error +async function fn4(): number { } // error +async function fn5(): PromiseLike { } // error +async function fn6(): Thenable { } // error +async function fn7() { return; } // valid: Promise +async function fn8() { return 1; } // valid: Promise +async function fn9() { return null; } // valid: Promise +async function fn10() { return undefined; } // valid: Promise +async function fn11() { return a; } // valid: Promise +async function fn12() { return obj; } // valid: Promise<{ then: string; }> +async function fn13() { return thenable; } // error +async function fn14() { await 1; } // valid: Promise +async function fn15() { await null; } // valid: Promise +async function fn16() { await undefined; } // valid: Promise +async function fn17() { await a; } // valid: Promise +async function fn18() { await obj; } // valid: Promise +async function fn19() { await thenable; } // error