diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 7f20c54212..6677d14b72 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -9871,31 +9871,40 @@ namespace ts { } // 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) { + if (returnType === voidType || isTypeAny(returnType)) { return; } // If all we have is a function signature, or an arrow function with an expression body, then there is nothing to check. - // also if HasImplicitReturnValue flags is not set this means that all codepaths in function body end with return of throw + // also if HasImplicitReturn flag is not set this means that all codepaths in function body end with return of throw if (nodeIsMissing(func.body) || func.body.kind !== SyntaxKind.Block || !(func.flags & NodeFlags.HasImplicitReturn)) { return; } - if (!returnType || func.flags & NodeFlags.HasExplicitReturn) { - if (compilerOptions.noImplicitReturns) { - error(func.type || func, Diagnostics.Not_all_code_paths_return_a_value); - } - } - else { - // This function does not conform to the specification. + const hasExplicitReturn = func.flags & NodeFlags.HasExplicitReturn; + + if (returnType && !hasExplicitReturn) { + // minimal check: function has syntactic return type annotation and no explicit return statements in the body + // 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); } + else if (compilerOptions.noImplicitReturns){ + // errors in this branch should only be reported if CompilerOptions.noImplicitReturns flag is set + if (!returnType) { + // If return type annotation is omitted check if function has any explicit return statements. + // If it does not have any - its inferred return type is void - don't do any checks. + // Otherwise get inferred return type from function body and report error only if it is not void / anytype + const inferredReturnType = hasExplicitReturn + ? getReturnTypeOfSignature(getSignatureFromDeclaration(func)) + : voidType; + + if (inferredReturnType === voidType || isTypeAny(inferredReturnType)) { + return; + } + } + error(func.type || func, Diagnostics.Not_all_code_paths_return_a_value); + } } function checkFunctionExpressionOrObjectLiteralMethod(node: FunctionExpression | MethodDeclaration, contextualMapper?: TypeMapper): Type { diff --git a/tests/baselines/reference/reachabilityChecks6.errors.txt b/tests/baselines/reference/reachabilityChecks6.errors.txt index 5b78891afb..38e72def8c 100644 --- a/tests/baselines/reference/reachabilityChecks6.errors.txt +++ b/tests/baselines/reference/reachabilityChecks6.errors.txt @@ -1,5 +1,4 @@ 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. @@ -10,7 +9,7 @@ tests/cases/compiler/reachabilityChecks6.ts(116,10): error TS7030: Not all code tests/cases/compiler/reachabilityChecks6.ts(123,13): error TS7027: Unreachable code detected. -==== tests/cases/compiler/reachabilityChecks6.ts (10 errors) ==== +==== tests/cases/compiler/reachabilityChecks6.ts (9 errors) ==== function f0(x) { while (true); @@ -32,8 +31,6 @@ tests/cases/compiler/reachabilityChecks6.ts(123,13): error TS7027: Unreachable c } function f3(x) { - ~~ -!!! error TS7030: Not all code paths return a value. while (x) { throw new Error(); } diff --git a/tests/baselines/reference/reachabilityChecks7.errors.txt b/tests/baselines/reference/reachabilityChecks7.errors.txt index 9e8f803f95..1596c7d9a3 100644 --- a/tests/baselines/reference/reachabilityChecks7.errors.txt +++ b/tests/baselines/reference/reachabilityChecks7.errors.txt @@ -1,21 +1,39 @@ -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(14,16): error TS7030: Not all code paths return a value. +tests/cases/compiler/reachabilityChecks7.ts(18,22): error TS2355: A function whose declared type is neither 'void' nor 'any' must 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 + } + + async function f3(x) { + ~~ +!!! error TS7030: Not all code paths return a value. + if (x) return 10; + } + + async function f4(): Promise { + ~~~~~~~~~~~~~~~ +!!! error TS2355: A function whose declared type is neither 'void' nor 'any' must return a value. + + } + + function voidFunc(): void { + } + + function calltoVoidFunc(x) { + if (x) return voidFunc(); + } + + declare function use(s: string): void; + let x1 = () => { use("Test"); } \ No newline at end of file diff --git a/tests/baselines/reference/reachabilityChecks7.js b/tests/baselines/reference/reachabilityChecks7.js index f9579d5828..c78f99953e 100644 --- a/tests/baselines/reference/reachabilityChecks7.js +++ b/tests/baselines/reference/reachabilityChecks7.js @@ -10,7 +10,25 @@ let x = async function() { // async function with which promised type is void - return can be omitted async function f2(): Promise { -} +} + +async function f3(x) { + if (x) return 10; +} + +async function f4(): Promise { + +} + +function voidFunc(): void { +} + +function calltoVoidFunc(x) { + if (x) return voidFunc(); +} + +declare function use(s: string): void; +let x1 = () => { use("Test"); } //// [reachabilityChecks7.js] var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promise, generator) { @@ -40,3 +58,20 @@ function f2() { return __awaiter(this, void 0, Promise, function* () { }); } +function f3(x) { + return __awaiter(this, void 0, Promise, function* () { + if (x) + return 10; + }); +} +function f4() { + return __awaiter(this, void 0, Promise, function* () { + }); +} +function voidFunc() { +} +function calltoVoidFunc(x) { + if (x) + return voidFunc(); +} +let x1 = () => { use("Test"); }; diff --git a/tests/cases/compiler/reachabilityChecks7.ts b/tests/cases/compiler/reachabilityChecks7.ts index 11febb320d..53702037e3 100644 --- a/tests/cases/compiler/reachabilityChecks7.ts +++ b/tests/cases/compiler/reachabilityChecks7.ts @@ -11,4 +11,22 @@ 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 +} + +async function f3(x) { + if (x) return 10; +} + +async function f4(): Promise { + +} + +function voidFunc(): void { +} + +function calltoVoidFunc(x) { + if (x) return voidFunc(); +} + +declare function use(s: string): void; +let x1 = () => { use("Test"); } \ No newline at end of file