diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 1b6b31f958697..1a0440be2c770 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -9811,17 +9811,25 @@ namespace ts { return aggregatedTypes; } - // TypeScript Specification 1.0 (6.3) - July 2014 - // An explicitly typed function whose return type isn't the Void or the Any type - // must have at least one return statement somewhere in its body. - // An exception to this rule is if the function implementation consists of a single 'throw' statement. + /* + *TypeScript Specification 1.0 (6.3) - July 2014 + * An explicitly typed function whose return type isn't the Void or the Any type + * must have at least one return statement somewhere in its body. + * An exception to this rule is if the function implementation consists of a single 'throw' statement. + * @param returnType - return type of the function, can be undefined if return type is not explicitly specified + */ function checkAllCodePathsInNonVoidFunctionReturnOrThrow(func: FunctionLikeDeclaration, returnType: Type): void { if (!produceDiagnostics) { return; } - // Functions that return 'void' or 'any' don't need any return expressions. - if (returnType === voidType || isTypeAny(returnType)) { + // Functions with with an explicitly specified 'void' or 'any' return type don't need any return expressions. + if (returnType && (returnType === voidType || isTypeAny(returnType))) { + return; + } + + // if return type is not specified then we'll do the check only if 'noImplicitReturns' option is set + if (!returnType && !compilerOptions.noImplicitReturns) { return; } @@ -9831,13 +9839,14 @@ namespace ts { return; } - if (func.flags & NodeFlags.HasExplicitReturn) { + if (!returnType || func.flags & NodeFlags.HasExplicitReturn) { if (compilerOptions.noImplicitReturns) { - error(func.type, Diagnostics.Not_all_code_paths_return_a_value); + error(func.type || func, Diagnostics.Not_all_code_paths_return_a_value); } } else { // This function does not conform to the specification. + // NOTE: having returnType !== undefined is a precondition for entering this branch so func.type will always be present error(func.type, Diagnostics.A_function_whose_declared_type_is_neither_void_nor_any_must_return_a_value); } } @@ -9912,14 +9921,10 @@ namespace ts { emitAwaiter = true; } - const returnType = node.type && getTypeFromTypeNode(node.type); - let promisedType: Type; - if (returnType && isAsync) { - promisedType = checkAsyncFunctionReturnType(node); - } - - if (returnType && !node.asteriskToken) { - checkAllCodePathsInNonVoidFunctionReturnOrThrow(node, isAsync ? promisedType : returnType); + const returnOrPromisedType = node.type && (isAsync ? checkAsyncFunctionReturnType(node) : getTypeFromTypeNode(node.type)); + if (!node.asteriskToken) { + // return is not necessary in the body of generators + checkAllCodePathsInNonVoidFunctionReturnOrThrow(node, returnOrPromisedType); } if (node.body) { @@ -9942,13 +9947,13 @@ namespace ts { // check assignability of the awaited type of the expression body against the promised type of // its return type annotation. const exprType = checkExpression(node.body); - if (returnType) { + if (returnOrPromisedType) { if (isAsync) { const awaitedType = checkAwaitedType(exprType, node.body, Diagnostics.Expression_body_for_async_arrow_function_does_not_have_a_valid_callable_then_member); - checkTypeAssignableTo(awaitedType, promisedType, node.body); + checkTypeAssignableTo(awaitedType, returnOrPromisedType, node.body); } else { - checkTypeAssignableTo(exprType, returnType, node.body); + checkTypeAssignableTo(exprType, returnOrPromisedType, node.body); } } @@ -12088,14 +12093,9 @@ namespace ts { } checkSourceElement(node.body); - if (node.type && !isAccessor(node.kind) && !node.asteriskToken) { - const returnType = getTypeFromTypeNode(node.type); - let promisedType: Type; - if (isAsync) { - promisedType = checkAsyncFunctionReturnType(node); - } - - checkAllCodePathsInNonVoidFunctionReturnOrThrow(node, isAsync ? promisedType : returnType); + if (!isAccessor(node.kind) && !node.asteriskToken) { + const returnOrPromisedType = node.type && (isAsync ? checkAsyncFunctionReturnType(node) : getTypeFromTypeNode(node.type)); + checkAllCodePathsInNonVoidFunctionReturnOrThrow(node, returnOrPromisedType); } if (produceDiagnostics && !node.type) { diff --git a/tests/baselines/reference/reachabilityChecks6.errors.txt b/tests/baselines/reference/reachabilityChecks6.errors.txt new file mode 100644 index 0000000000000..5b78891afb381 --- /dev/null +++ b/tests/baselines/reference/reachabilityChecks6.errors.txt @@ -0,0 +1,162 @@ +tests/cases/compiler/reachabilityChecks6.ts(6,10): error TS7030: Not all code paths return a value. +tests/cases/compiler/reachabilityChecks6.ts(19,10): error TS7030: Not all code paths return a value. +tests/cases/compiler/reachabilityChecks6.ts(31,10): error TS7030: Not all code paths return a value. +tests/cases/compiler/reachabilityChecks6.ts(41,10): error TS7030: Not all code paths return a value. +tests/cases/compiler/reachabilityChecks6.ts(52,10): error TS7030: Not all code paths return a value. +tests/cases/compiler/reachabilityChecks6.ts(80,10): error TS7030: Not all code paths return a value. +tests/cases/compiler/reachabilityChecks6.ts(86,13): error TS7027: Unreachable code detected. +tests/cases/compiler/reachabilityChecks6.ts(94,10): error TS7030: Not all code paths return a value. +tests/cases/compiler/reachabilityChecks6.ts(116,10): error TS7030: Not all code paths return a value. +tests/cases/compiler/reachabilityChecks6.ts(123,13): error TS7027: Unreachable code detected. + + +==== tests/cases/compiler/reachabilityChecks6.ts (10 errors) ==== + + function f0(x) { + while (true); + } + + function f1(x) { + ~~ +!!! error TS7030: Not all code paths return a value. + if (x) { + return 1 + } + } + + function f2(x) { + while (x) { + throw new Error(); + } + return 1; + } + + function f3(x) { + ~~ +!!! error TS7030: Not all code paths return a value. + while (x) { + throw new Error(); + } + } + + function f3_1 (x) { + while (x) { + } + throw new Error(); + } + + function f4(x) { + ~~ +!!! error TS7030: Not all code paths return a value. + try { + if (x) { + return 1; + } + } + catch (e) { + } + } + + function f5(x) { + ~~ +!!! error TS7030: Not all code paths return a value. + try { + if (x) { + return 1; + } + } + catch (e) { + return 2; + } + } + + function f6(x) { + ~~ +!!! error TS7030: Not all code paths return a value. + try { + if (x) { + return 1; + } + else + { + throw new Error(); + } + } + catch (e) { + } + } + + function f7(x) { + try { + if (x) { + return 1; + } + else { + throw new Error(); + } + } + catch (e) { + return 1; + } + } + + function f8(x) { + ~~ +!!! error TS7030: Not all code paths return a value. + try { + if (true) { + x++; + } + else { + return 1; + ~~~~~~ +!!! error TS7027: Unreachable code detected. + } + } + catch (e) { + return 1; + } + } + + function f9(x) { + ~~ +!!! error TS7030: Not all code paths return a value. + try { + while (false) { + return 1; + } + } + catch (e) { + return 1; + } + } + + function f10(x) { + try { + do { + x++; + } while (true); + } + catch (e) { + return 1; + } + } + + function f11(x) { + ~~~ +!!! error TS7030: Not all code paths return a value. + test: + try { + do { + do { + break test; + } while (true); + x++; + ~ +!!! error TS7027: Unreachable code detected. + } while (true); + } + catch (e) { + return 1; + } + } \ No newline at end of file diff --git a/tests/baselines/reference/reachabilityChecks6.js b/tests/baselines/reference/reachabilityChecks6.js new file mode 100644 index 0000000000000..9be23d32048ec --- /dev/null +++ b/tests/baselines/reference/reachabilityChecks6.js @@ -0,0 +1,247 @@ +//// [reachabilityChecks6.ts] + +function f0(x) { + while (true); +} + +function f1(x) { + if (x) { + return 1 + } +} + +function f2(x) { + while (x) { + throw new Error(); + } + return 1; +} + +function f3(x) { + while (x) { + throw new Error(); + } +} + +function f3_1 (x) { + while (x) { + } + throw new Error(); +} + +function f4(x) { + try { + if (x) { + return 1; + } + } + catch (e) { + } +} + +function f5(x) { + try { + if (x) { + return 1; + } + } + catch (e) { + return 2; + } +} + +function f6(x) { + try { + if (x) { + return 1; + } + else + { + throw new Error(); + } + } + catch (e) { + } +} + +function f7(x) { + try { + if (x) { + return 1; + } + else { + throw new Error(); + } + } + catch (e) { + return 1; + } +} + +function f8(x) { + try { + if (true) { + x++; + } + else { + return 1; + } + } + catch (e) { + return 1; + } +} + +function f9(x) { + try { + while (false) { + return 1; + } + } + catch (e) { + return 1; + } +} + +function f10(x) { + try { + do { + x++; + } while (true); + } + catch (e) { + return 1; + } +} + +function f11(x) { + test: + try { + do { + do { + break test; + } while (true); + x++; + } while (true); + } + catch (e) { + return 1; + } +} + +//// [reachabilityChecks6.js] +function f0(x) { + while (true) + ; +} +function f1(x) { + if (x) { + return 1; + } +} +function f2(x) { + while (x) { + throw new Error(); + } + return 1; +} +function f3(x) { + while (x) { + throw new Error(); + } +} +function f3_1(x) { + while (x) { + } + throw new Error(); +} +function f4(x) { + try { + if (x) { + return 1; + } + } + catch (e) { + } +} +function f5(x) { + try { + if (x) { + return 1; + } + } + catch (e) { + return 2; + } +} +function f6(x) { + try { + if (x) { + return 1; + } + else { + throw new Error(); + } + } + catch (e) { + } +} +function f7(x) { + try { + if (x) { + return 1; + } + else { + throw new Error(); + } + } + catch (e) { + return 1; + } +} +function f8(x) { + try { + if (true) { + x++; + } + else { + return 1; + } + } + catch (e) { + return 1; + } +} +function f9(x) { + try { + while (false) { + return 1; + } + } + catch (e) { + return 1; + } +} +function f10(x) { + try { + do { + x++; + } while (true); + } + catch (e) { + return 1; + } +} +function f11(x) { + test: try { + do { + do { + break test; + } while (true); + x++; + } while (true); + } + catch (e) { + return 1; + } +} diff --git a/tests/baselines/reference/reachabilityChecks7.errors.txt b/tests/baselines/reference/reachabilityChecks7.errors.txt new file mode 100644 index 0000000000000..9e8f803f957eb --- /dev/null +++ b/tests/baselines/reference/reachabilityChecks7.errors.txt @@ -0,0 +1,21 @@ +tests/cases/compiler/reachabilityChecks7.ts(3,16): error TS7030: Not all code paths return a value. +tests/cases/compiler/reachabilityChecks7.ts(6,9): error TS7030: Not all code paths return a value. + + +==== tests/cases/compiler/reachabilityChecks7.ts (2 errors) ==== + + // async function without return type annotation - error + async function f1() { + ~~ +!!! error TS7030: Not all code paths return a value. + } + + let x = async function() { + ~~~~~ +!!! error TS7030: Not all code paths return a value. + } + + // async function with which promised type is void - return can be omitted + async function f2(): Promise { + + } \ No newline at end of file diff --git a/tests/baselines/reference/reachabilityChecks7.js b/tests/baselines/reference/reachabilityChecks7.js new file mode 100644 index 0000000000000..f9579d5828d93 --- /dev/null +++ b/tests/baselines/reference/reachabilityChecks7.js @@ -0,0 +1,42 @@ +//// [reachabilityChecks7.ts] + +// async function without return type annotation - error +async function f1() { +} + +let x = async function() { +} + +// async function with which promised type is void - return can be omitted +async function f2(): Promise { + +} + +//// [reachabilityChecks7.js] +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promise, generator) { + return new Promise(function (resolve, reject) { + generator = generator.call(thisArg, _arguments); + function cast(value) { return value instanceof Promise && value.constructor === Promise ? value : new Promise(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); } } + function step(verb, value) { + var result = generator[verb](value); + result.done ? resolve(result.value) : cast(result.value).then(onfulfill, onreject); + } + step("next", void 0); + }); +}; +// async function without return type annotation - error +function f1() { + return __awaiter(this, void 0, Promise, function* () { + }); +} +let x = function () { + return __awaiter(this, void 0, Promise, function* () { + }); +}; +// async function with which promised type is void - return can be omitted +function f2() { + return __awaiter(this, void 0, Promise, function* () { + }); +} diff --git a/tests/cases/compiler/reachabilityChecks6.ts b/tests/cases/compiler/reachabilityChecks6.ts new file mode 100644 index 0000000000000..725563a44885a --- /dev/null +++ b/tests/cases/compiler/reachabilityChecks6.ts @@ -0,0 +1,131 @@ +// @allowUnreachableCode: false +// @noImplicitReturns: true + +function f0(x) { + while (true); +} + +function f1(x) { + if (x) { + return 1 + } +} + +function f2(x) { + while (x) { + throw new Error(); + } + return 1; +} + +function f3(x) { + while (x) { + throw new Error(); + } +} + +function f3_1 (x) { + while (x) { + } + throw new Error(); +} + +function f4(x) { + try { + if (x) { + return 1; + } + } + catch (e) { + } +} + +function f5(x) { + try { + if (x) { + return 1; + } + } + catch (e) { + return 2; + } +} + +function f6(x) { + try { + if (x) { + return 1; + } + else + { + throw new Error(); + } + } + catch (e) { + } +} + +function f7(x) { + try { + if (x) { + return 1; + } + else { + throw new Error(); + } + } + catch (e) { + return 1; + } +} + +function f8(x) { + try { + if (true) { + x++; + } + else { + return 1; + } + } + catch (e) { + return 1; + } +} + +function f9(x) { + try { + while (false) { + return 1; + } + } + catch (e) { + return 1; + } +} + +function f10(x) { + try { + do { + x++; + } while (true); + } + catch (e) { + return 1; + } +} + +function f11(x) { + test: + try { + do { + do { + break test; + } while (true); + x++; + } while (true); + } + catch (e) { + return 1; + } +} \ No newline at end of file diff --git a/tests/cases/compiler/reachabilityChecks7.ts b/tests/cases/compiler/reachabilityChecks7.ts new file mode 100644 index 0000000000000..11febb320d639 --- /dev/null +++ b/tests/cases/compiler/reachabilityChecks7.ts @@ -0,0 +1,14 @@ +// @target: ES6 +// @noImplicitReturns: true + +// async function without return type annotation - error +async function f1() { +} + +let x = async function() { +} + +// async function with which promised type is void - return can be omitted +async function f2(): Promise { + +} \ No newline at end of file