Skip to content

Commit b40079e

Browse files
committed
Merge pull request #5824 from Microsoft/implicitReturnInferredVoid
do not report 'noImplicitReturns' error if inferred return type of th…
2 parents 74d92f0 + 6ff5679 commit b40079e

File tree

5 files changed

+103
-27
lines changed

5 files changed

+103
-27
lines changed

src/compiler/checker.ts

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9871,31 +9871,39 @@ namespace ts {
98719871
}
98729872

98739873
// Functions with with an explicitly specified 'void' or 'any' return type don't need any return expressions.
9874-
if (returnType && (returnType === voidType || isTypeAny(returnType))) {
9875-
return;
9876-
}
9877-
9878-
// if return type is not specified then we'll do the check only if 'noImplicitReturns' option is set
9879-
if (!returnType && !compilerOptions.noImplicitReturns) {
9874+
if (returnType === voidType || isTypeAny(returnType)) {
98809875
return;
98819876
}
98829877

98839878
// If all we have is a function signature, or an arrow function with an expression body, then there is nothing to check.
9884-
// also if HasImplicitReturnValue flags is not set this means that all codepaths in function body end with return of throw
9879+
// also if HasImplicitReturn flag is not set this means that all codepaths in function body end with return or throw
98859880
if (nodeIsMissing(func.body) || func.body.kind !== SyntaxKind.Block || !(func.flags & NodeFlags.HasImplicitReturn)) {
98869881
return;
98879882
}
98889883

9889-
if (!returnType || func.flags & NodeFlags.HasExplicitReturn) {
9890-
if (compilerOptions.noImplicitReturns) {
9891-
error(func.type || func, Diagnostics.Not_all_code_paths_return_a_value);
9892-
}
9893-
}
9894-
else {
9895-
// This function does not conform to the specification.
9884+
const hasExplicitReturn = func.flags & NodeFlags.HasExplicitReturn;
9885+
9886+
if (returnType && !hasExplicitReturn) {
9887+
// minimal check: function has syntactic return type annotation and no explicit return statements in the body
9888+
// this function does not conform to the specification.
98969889
// NOTE: having returnType !== undefined is a precondition for entering this branch so func.type will always be present
98979890
error(func.type, Diagnostics.A_function_whose_declared_type_is_neither_void_nor_any_must_return_a_value);
98989891
}
9892+
else if (compilerOptions.noImplicitReturns) {
9893+
if (!returnType) {
9894+
// If return type annotation is omitted check if function has any explicit return statements.
9895+
// If it does not have any - its inferred return type is void - don't do any checks.
9896+
// Otherwise get inferred return type from function body and report error only if it is not void / anytype
9897+
const inferredReturnType = hasExplicitReturn
9898+
? getReturnTypeOfSignature(getSignatureFromDeclaration(func))
9899+
: voidType;
9900+
9901+
if (inferredReturnType === voidType || isTypeAny(inferredReturnType)) {
9902+
return;
9903+
}
9904+
}
9905+
error(func.type || func, Diagnostics.Not_all_code_paths_return_a_value);
9906+
}
98999907
}
99009908

99019909
function checkFunctionExpressionOrObjectLiteralMethod(node: FunctionExpression | MethodDeclaration, contextualMapper?: TypeMapper): Type {

tests/baselines/reference/reachabilityChecks6.errors.txt

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
tests/cases/compiler/reachabilityChecks6.ts(6,10): error TS7030: Not all code paths return a value.
2-
tests/cases/compiler/reachabilityChecks6.ts(19,10): error TS7030: Not all code paths return a value.
32
tests/cases/compiler/reachabilityChecks6.ts(31,10): error TS7030: Not all code paths return a value.
43
tests/cases/compiler/reachabilityChecks6.ts(41,10): error TS7030: Not all code paths return a value.
54
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
109
tests/cases/compiler/reachabilityChecks6.ts(123,13): error TS7027: Unreachable code detected.
1110

1211

13-
==== tests/cases/compiler/reachabilityChecks6.ts (10 errors) ====
12+
==== tests/cases/compiler/reachabilityChecks6.ts (9 errors) ====
1413

1514
function f0(x) {
1615
while (true);
@@ -32,8 +31,6 @@ tests/cases/compiler/reachabilityChecks6.ts(123,13): error TS7027: Unreachable c
3231
}
3332

3433
function f3(x) {
35-
~~
36-
!!! error TS7030: Not all code paths return a value.
3734
while (x) {
3835
throw new Error();
3936
}
Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,39 @@
1-
tests/cases/compiler/reachabilityChecks7.ts(3,16): error TS7030: Not all code paths return a value.
2-
tests/cases/compiler/reachabilityChecks7.ts(6,9): error TS7030: Not all code paths return a value.
1+
tests/cases/compiler/reachabilityChecks7.ts(14,16): error TS7030: Not all code paths return a value.
2+
tests/cases/compiler/reachabilityChecks7.ts(18,22): error TS2355: A function whose declared type is neither 'void' nor 'any' must return a value.
33

44

55
==== tests/cases/compiler/reachabilityChecks7.ts (2 errors) ====
66

77
// async function without return type annotation - error
88
async function f1() {
9-
~~
10-
!!! error TS7030: Not all code paths return a value.
119
}
1210

1311
let x = async function() {
14-
~~~~~
15-
!!! error TS7030: Not all code paths return a value.
1612
}
1713

1814
// async function with which promised type is void - return can be omitted
1915
async function f2(): Promise<void> {
2016

21-
}
17+
}
18+
19+
async function f3(x) {
20+
~~
21+
!!! error TS7030: Not all code paths return a value.
22+
if (x) return 10;
23+
}
24+
25+
async function f4(): Promise<number> {
26+
~~~~~~~~~~~~~~~
27+
!!! error TS2355: A function whose declared type is neither 'void' nor 'any' must return a value.
28+
29+
}
30+
31+
function voidFunc(): void {
32+
}
33+
34+
function calltoVoidFunc(x) {
35+
if (x) return voidFunc();
36+
}
37+
38+
declare function use(s: string): void;
39+
let x1 = () => { use("Test"); }

tests/baselines/reference/reachabilityChecks7.js

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,25 @@ let x = async function() {
1010
// async function with which promised type is void - return can be omitted
1111
async function f2(): Promise<void> {
1212

13-
}
13+
}
14+
15+
async function f3(x) {
16+
if (x) return 10;
17+
}
18+
19+
async function f4(): Promise<number> {
20+
21+
}
22+
23+
function voidFunc(): void {
24+
}
25+
26+
function calltoVoidFunc(x) {
27+
if (x) return voidFunc();
28+
}
29+
30+
declare function use(s: string): void;
31+
let x1 = () => { use("Test"); }
1432

1533
//// [reachabilityChecks7.js]
1634
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, Promise, generator) {
@@ -40,3 +58,20 @@ function f2() {
4058
return __awaiter(this, void 0, Promise, function* () {
4159
});
4260
}
61+
function f3(x) {
62+
return __awaiter(this, void 0, Promise, function* () {
63+
if (x)
64+
return 10;
65+
});
66+
}
67+
function f4() {
68+
return __awaiter(this, void 0, Promise, function* () {
69+
});
70+
}
71+
function voidFunc() {
72+
}
73+
function calltoVoidFunc(x) {
74+
if (x)
75+
return voidFunc();
76+
}
77+
let x1 = () => { use("Test"); };

tests/cases/compiler/reachabilityChecks7.ts

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,22 @@ let x = async function() {
1111
// async function with which promised type is void - return can be omitted
1212
async function f2(): Promise<void> {
1313

14-
}
14+
}
15+
16+
async function f3(x) {
17+
if (x) return 10;
18+
}
19+
20+
async function f4(): Promise<number> {
21+
22+
}
23+
24+
function voidFunc(): void {
25+
}
26+
27+
function calltoVoidFunc(x) {
28+
if (x) return voidFunc();
29+
}
30+
31+
declare function use(s: string): void;
32+
let x1 = () => { use("Test"); }

0 commit comments

Comments
 (0)