From 6b94bae437b96614266c38b653aee3aad541d865 Mon Sep 17 00:00:00 2001 From: Mohamed Hegazy Date: Tue, 1 Nov 2016 17:03:56 -0700 Subject: [PATCH] Fix #11396: Make error message referene `Promise` explicitly (#11982) * Simplify the checking for async function return type * Fix https://github.com/Microsoft/TypeScript/issues/11396: Make error message referene `Promise` explicitly --- src/compiler/checker.ts | 131 +++++++----------- src/compiler/diagnosticMessages.json | 4 + .../asyncFunctionNoReturnType.errors.txt | 4 +- 3 files changed, 57 insertions(+), 82 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 627bf3bb2e297..744d23a3657a1 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -13073,7 +13073,7 @@ namespace ts { function createPromiseReturnType(func: FunctionLikeDeclaration, promisedType: Type) { const promiseType = createPromiseType(promisedType); if (promiseType === emptyObjectType) { - error(func, Diagnostics.An_async_function_or_method_must_have_a_valid_awaitable_return_type); + error(func, Diagnostics.An_async_function_or_method_must_return_a_Promise_Make_sure_you_have_a_declaration_for_Promise_or_include_ES2015_in_your_lib_option); return unknownType; } @@ -15515,36 +15515,6 @@ namespace ts { } } - /** - * Checks that the return type provided is an instantiation of the global Promise type - * and returns the awaited type of the return type. - * - * @param returnType The return type of a FunctionLikeDeclaration - * @param location The node on which to report the error. - */ - function checkCorrectPromiseType(returnType: Type, location: Node, diagnostic: DiagnosticMessage, typeName?: string) { - if (returnType === unknownType) { - // The return type already had some other error, so we ignore and return - // the unknown type. - return unknownType; - } - - const globalPromiseType = getGlobalPromiseType(); - if (globalPromiseType === emptyGenericType - || globalPromiseType === getTargetType(returnType)) { - // Either we couldn't resolve the global promise type, which would have already - // reported an error, or we could resolve it and the return type is a valid type - // reference to the global type. In either case, we return the awaited type for - // the return type. - return checkAwaitedType(returnType, location, Diagnostics.An_async_function_or_method_must_have_a_valid_awaitable_return_type); - } - - // The promise type was not a valid type reference to the global promise type, so we - // report an error and return the unknown type. - error(location, diagnostic, typeName); - return unknownType; - } - /** * Checks the return type of an async function to ensure it is a compatible * Promise implementation. @@ -15559,11 +15529,6 @@ namespace ts { * @param node The signature to check */ function checkAsyncFunctionReturnType(node: FunctionLikeDeclaration): Type { - if (languageVersion >= ScriptTarget.ES2015) { - const returnType = getTypeFromTypeNode(node.type); - return checkCorrectPromiseType(returnType, node.type, Diagnostics.The_return_type_of_an_async_function_or_method_must_be_the_global_Promise_T_type); - } - // 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 @@ -15588,61 +15553,67 @@ namespace ts { // then(...): Promise; // } // + const returnType = getTypeFromTypeNode(node.type); - // Always mark the type node as referenced if it points to a value - markTypeNodeAsReferenced(node.type); - - const promiseConstructorName = getEntityNameFromTypeNode(node.type); - const promiseType = getTypeFromTypeNode(node.type); - if (promiseType === unknownType) { - if (!compilerOptions.isolatedModules) { - if (promiseConstructorName) { - error(node.type, Diagnostics.Type_0_is_not_a_valid_async_function_return_type_in_ES5_SlashES3_because_it_does_not_refer_to_a_Promise_compatible_constructor_value, entityNameToString(promiseConstructorName)); - } - else { - error(node.type, Diagnostics.An_async_function_or_method_must_have_a_valid_awaitable_return_type); - } + if (languageVersion >= ScriptTarget.ES2015) { + if (returnType === unknownType) { + return unknownType; + } + const globalPromiseType = getGlobalPromiseType(); + if (globalPromiseType !== emptyGenericType && globalPromiseType !== getTargetType(returnType)) { + // The promise type was not a valid type reference to the global promise type, so we + // report an error and return the unknown type. + error(node.type, Diagnostics.The_return_type_of_an_async_function_or_method_must_be_the_global_Promise_T_type); + return unknownType; } - return unknownType; } + else { + // Always mark the type node as referenced if it points to a value + markTypeNodeAsReferenced(node.type); - if (promiseConstructorName === undefined) { - error(node.type, Diagnostics.Type_0_is_not_a_valid_async_function_return_type_in_ES5_SlashES3_because_it_does_not_refer_to_a_Promise_compatible_constructor_value, typeToString(promiseType)); - return unknownType; - } + if (returnType === unknownType) { + return unknownType; + } - const promiseConstructorSymbol = resolveEntityName(promiseConstructorName, SymbolFlags.Value, /*ignoreErrors*/ true); - const promiseConstructorType = promiseConstructorSymbol ? getTypeOfSymbol(promiseConstructorSymbol) : unknownType; - if (promiseConstructorType === unknownType) { - error(node.type, Diagnostics.Type_0_is_not_a_valid_async_function_return_type_in_ES5_SlashES3_because_it_does_not_refer_to_a_Promise_compatible_constructor_value, entityNameToString(promiseConstructorName)); - return unknownType; - } + const promiseConstructorName = getEntityNameFromTypeNode(node.type); + if (promiseConstructorName === undefined) { + error(node.type, Diagnostics.Type_0_is_not_a_valid_async_function_return_type_in_ES5_SlashES3_because_it_does_not_refer_to_a_Promise_compatible_constructor_value, typeToString(returnType)); + return unknownType; + } - const globalPromiseConstructorLikeType = getGlobalPromiseConstructorLikeType(); - if (globalPromiseConstructorLikeType === emptyObjectType) { - // If we couldn't resolve the global PromiseConstructorLike type we cannot verify - // compatibility with __awaiter. - error(node.type, Diagnostics.Type_0_is_not_a_valid_async_function_return_type_in_ES5_SlashES3_because_it_does_not_refer_to_a_Promise_compatible_constructor_value, entityNameToString(promiseConstructorName)); - return unknownType; - } + const promiseConstructorSymbol = resolveEntityName(promiseConstructorName, SymbolFlags.Value, /*ignoreErrors*/ true); + const promiseConstructorType = promiseConstructorSymbol ? getTypeOfSymbol(promiseConstructorSymbol) : unknownType; + if (promiseConstructorType === unknownType) { + error(node.type, Diagnostics.Type_0_is_not_a_valid_async_function_return_type_in_ES5_SlashES3_because_it_does_not_refer_to_a_Promise_compatible_constructor_value, entityNameToString(promiseConstructorName)); + return unknownType; + } - if (!checkTypeAssignableTo(promiseConstructorType, globalPromiseConstructorLikeType, node.type, - Diagnostics.Type_0_is_not_a_valid_async_function_return_type_in_ES5_SlashES3_because_it_does_not_refer_to_a_Promise_compatible_constructor_value)) { - return unknownType; - } + const globalPromiseConstructorLikeType = getGlobalPromiseConstructorLikeType(); + if (globalPromiseConstructorLikeType === emptyObjectType) { + // If we couldn't resolve the global PromiseConstructorLike type we cannot verify + // compatibility with __awaiter. + error(node.type, Diagnostics.Type_0_is_not_a_valid_async_function_return_type_in_ES5_SlashES3_because_it_does_not_refer_to_a_Promise_compatible_constructor_value, entityNameToString(promiseConstructorName)); + return unknownType; + } - // Verify there is no local declaration that could collide with the promise constructor. - const rootName = promiseConstructorName && getFirstIdentifier(promiseConstructorName); - const collidingSymbol = getSymbol(node.locals, rootName.text, SymbolFlags.Value); - if (collidingSymbol) { - error(collidingSymbol.valueDeclaration, Diagnostics.Duplicate_identifier_0_Compiler_uses_declaration_1_to_support_async_functions, - rootName.text, - entityNameToString(promiseConstructorName)); - return unknownType; + if (!checkTypeAssignableTo(promiseConstructorType, globalPromiseConstructorLikeType, node.type, + Diagnostics.Type_0_is_not_a_valid_async_function_return_type_in_ES5_SlashES3_because_it_does_not_refer_to_a_Promise_compatible_constructor_value)) { + return unknownType; + } + + // Verify there is no local declaration that could collide with the promise constructor. + const rootName = promiseConstructorName && getFirstIdentifier(promiseConstructorName); + const collidingSymbol = getSymbol(node.locals, rootName.text, SymbolFlags.Value); + if (collidingSymbol) { + error(collidingSymbol.valueDeclaration, Diagnostics.Duplicate_identifier_0_Compiler_uses_declaration_1_to_support_async_functions, + rootName.text, + entityNameToString(promiseConstructorName)); + return unknownType; + } } // Get and return the awaited type of the return type. - return checkAwaitedType(promiseType, node, Diagnostics.An_async_function_or_method_must_have_a_valid_awaitable_return_type); + return checkAwaitedType(returnType, node, Diagnostics.An_async_function_or_method_must_have_a_valid_awaitable_return_type); } /** Check a decorator */ diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 7d5a141884371..3b873cf35c04e 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -1967,6 +1967,10 @@ "category": "Error", "code": 2696 }, + "An async function or method must return a 'Promise'. Make sure you have a declaration for 'Promise' or include 'ES2015' in your `--lib` option.": { + "category": "Error", + "code": 2697 + }, "Import declaration '{0}' is using private name '{1}'.": { "category": "Error", diff --git a/tests/baselines/reference/asyncFunctionNoReturnType.errors.txt b/tests/baselines/reference/asyncFunctionNoReturnType.errors.txt index 599bf41a7f2b5..89a61ff648fd3 100644 --- a/tests/baselines/reference/asyncFunctionNoReturnType.errors.txt +++ b/tests/baselines/reference/asyncFunctionNoReturnType.errors.txt @@ -1,5 +1,5 @@ error TS2318: Cannot find global type 'Promise'. -tests/cases/compiler/asyncFunctionNoReturnType.ts(1,1): error TS1057: An async function or method must have a valid awaitable return type. +tests/cases/compiler/asyncFunctionNoReturnType.ts(1,1): error TS2697: An async function or method must return a 'Promise'. Make sure you have a declaration for 'Promise' or include 'ES2015' in your `--lib` option. tests/cases/compiler/asyncFunctionNoReturnType.ts(1,1): error TS7030: Not all code paths return a value. tests/cases/compiler/asyncFunctionNoReturnType.ts(2,9): error TS2304: Cannot find name 'window'. tests/cases/compiler/asyncFunctionNoReturnType.ts(3,9): error TS7030: Not all code paths return a value. @@ -9,7 +9,7 @@ tests/cases/compiler/asyncFunctionNoReturnType.ts(3,9): error TS7030: Not all co ==== tests/cases/compiler/asyncFunctionNoReturnType.ts (4 errors) ==== async () => { ~~~~~~~~~~~~~ -!!! error TS1057: An async function or method must have a valid awaitable return type. +!!! error TS2697: An async function or method must return a 'Promise'. Make sure you have a declaration for 'Promise' or include 'ES2015' in your `--lib` option. ~~~~~~~~~~~~~ !!! error TS7030: Not all code paths return a value. if (window)