Skip to content

Commit

Permalink
Fix #11396: Make error message referene Promise explicitly (#11982)
Browse files Browse the repository at this point in the history
* Simplify the checking for async function return type

* Fix #11396: Make error message referene `Promise` explicitly
  • Loading branch information
mhegazy authored Nov 2, 2016
1 parent 137c99b commit 6b94bae
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 82 deletions.
131 changes: 51 additions & 80 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down Expand Up @@ -15515,36 +15515,6 @@ namespace ts {
}
}

/**
* Checks that the return type provided is an instantiation of the global Promise<T> 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.
Expand All @@ -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
Expand All @@ -15588,61 +15553,67 @@ namespace ts {
// then<U>(...): Promise<U>;
// }
//
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 */
Expand Down
4 changes: 4 additions & 0 deletions src/compiler/diagnosticMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
Original file line number Diff line number Diff line change
@@ -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.
Expand All @@ -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)
Expand Down

0 comments on commit 6b94bae

Please sign in to comment.