Skip to content

Commit fa99df8

Browse files
committed
Avoid inferring return and yield types from unreachable statements
1 parent 512d632 commit fa99df8

13 files changed

+490
-8
lines changed

src/compiler/checker.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36286,6 +36286,10 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
3628636286
const nextTypes: Type[] = [];
3628736287
const isAsync = (getFunctionFlags(func) & FunctionFlags.Async) !== 0;
3628836288
forEachYieldExpression(func.body as Block, yieldExpression => {
36289+
const statement = findAncestor(yieldExpression, isStatement)!;
36290+
if (canHaveFlowNode(statement) && !statement.flowNode && !compilerOptions.allowUnreachableCode) {
36291+
return;
36292+
}
3628936293
const yieldExpressionType = yieldExpression.expression ? checkExpression(yieldExpression.expression, checkMode) : undefinedWideningType;
3629036294
pushIfUnique(yieldTypes, getYieldedTypeOfYieldExpression(yieldExpression, yieldExpressionType, anyType, isAsync));
3629136295
let nextType: Type | undefined;
@@ -36393,6 +36397,10 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
3639336397
return;
3639436398
}
3639536399

36400+
if (!returnStatement.flowNode && !compilerOptions.allowUnreachableCode) {
36401+
return;
36402+
}
36403+
3639636404
let type = checkExpressionCached(expr, checkMode && checkMode & ~CheckMode.SkipGenericFunctions);
3639736405
if (functionFlags & FunctionFlags.Async) {
3639836406
// From within an async function you can return either a non-promise value or a promise. Any

tests/baselines/reference/plainJSBinderErrors.types

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ class C {
8282
}
8383
}
8484
label() {
85-
>label : () => number
85+
>label : () => void
8686

8787
for(;;) {
8888
label: var x = 1

tests/baselines/reference/recursiveNamedLambdaCall.types

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,10 @@ var promise = function( obj ) {
1414
>doScroll : any
1515

1616
(function doScrollCheck() {
17-
>(function doScrollCheck() { if ( false ) { try { top.doScroll("left"); } catch(e) { return setTimeout( doScrollCheck, 50 ); } // detach all dom ready events detach(); } })() : any
18-
>(function doScrollCheck() { if ( false ) { try { top.doScroll("left"); } catch(e) { return setTimeout( doScrollCheck, 50 ); } // detach all dom ready events detach(); } }) : () => any
19-
>function doScrollCheck() { if ( false ) { try { top.doScroll("left"); } catch(e) { return setTimeout( doScrollCheck, 50 ); } // detach all dom ready events detach(); } } : () => any
20-
>doScrollCheck : () => any
17+
>(function doScrollCheck() { if ( false ) { try { top.doScroll("left"); } catch(e) { return setTimeout( doScrollCheck, 50 ); } // detach all dom ready events detach(); } })() : void
18+
>(function doScrollCheck() { if ( false ) { try { top.doScroll("left"); } catch(e) { return setTimeout( doScrollCheck, 50 ); } // detach all dom ready events detach(); } }) : () => void
19+
>function doScrollCheck() { if ( false ) { try { top.doScroll("left"); } catch(e) { return setTimeout( doScrollCheck, 50 ); } // detach all dom ready events detach(); } } : () => void
20+
>doScrollCheck : () => void
2121

2222
if ( false ) {
2323
>false : false
@@ -36,7 +36,7 @@ var promise = function( obj ) {
3636
return setTimeout( doScrollCheck, 50 );
3737
>setTimeout( doScrollCheck, 50 ) : any
3838
>setTimeout : any
39-
>doScrollCheck : () => any
39+
>doScrollCheck : () => void
4040
>50 : 50
4141
}
4242

tests/baselines/reference/unreachableJavascriptChecked.types

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
=== unreachable.js ===
44
function unreachable() {
5-
>unreachable : () => void | 2 | 3 | 4
5+
>unreachable : () => void
66

77
return f();
88
>f() : void

tests/baselines/reference/unreachableJavascriptUnchecked.types

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
=== unreachable.js ===
44
function unreachable() {
5-
>unreachable : () => 1 | 2
5+
>unreachable : () => number
66

77
return 1;
88
>1 : 1
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
//// [tests/cases/compiler/unreachableReturnStatementsVsInferredReturnTypes.ts] ////
2+
3+
//// [unreachableReturnStatementsVsInferredReturnTypes.ts]
4+
export function g() {
5+
let x;
6+
x = 1;
7+
return x;
8+
return x;
9+
}
10+
11+
export function h() {
12+
return 1;
13+
let y;
14+
y = 1;
15+
return y;
16+
}
17+
18+
export function i() {
19+
let x: string | number | boolean;
20+
x = 1;
21+
return x;
22+
23+
x = "foo";
24+
return x;
25+
}
26+
27+
28+
//// [unreachableReturnStatementsVsInferredReturnTypes.js]
29+
"use strict";
30+
Object.defineProperty(exports, "__esModule", { value: true });
31+
exports.i = exports.h = exports.g = void 0;
32+
function g() {
33+
var x;
34+
x = 1;
35+
return x;
36+
return x;
37+
}
38+
exports.g = g;
39+
function h() {
40+
return 1;
41+
var y;
42+
y = 1;
43+
return y;
44+
}
45+
exports.h = h;
46+
function i() {
47+
var x;
48+
x = 1;
49+
return x;
50+
x = "foo";
51+
return x;
52+
}
53+
exports.i = i;
54+
55+
56+
//// [unreachableReturnStatementsVsInferredReturnTypes.d.ts]
57+
export declare function g(): number;
58+
export declare function h(): number;
59+
export declare function i(): number;
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
//// [tests/cases/compiler/unreachableReturnStatementsVsInferredReturnTypes.ts] ////
2+
3+
=== unreachableReturnStatementsVsInferredReturnTypes.ts ===
4+
export function g() {
5+
>g : Symbol(g, Decl(unreachableReturnStatementsVsInferredReturnTypes.ts, 0, 0))
6+
7+
let x;
8+
>x : Symbol(x, Decl(unreachableReturnStatementsVsInferredReturnTypes.ts, 1, 5))
9+
10+
x = 1;
11+
>x : Symbol(x, Decl(unreachableReturnStatementsVsInferredReturnTypes.ts, 1, 5))
12+
13+
return x;
14+
>x : Symbol(x, Decl(unreachableReturnStatementsVsInferredReturnTypes.ts, 1, 5))
15+
16+
return x;
17+
>x : Symbol(x, Decl(unreachableReturnStatementsVsInferredReturnTypes.ts, 1, 5))
18+
}
19+
20+
export function h() {
21+
>h : Symbol(h, Decl(unreachableReturnStatementsVsInferredReturnTypes.ts, 5, 1))
22+
23+
return 1;
24+
let y;
25+
>y : Symbol(y, Decl(unreachableReturnStatementsVsInferredReturnTypes.ts, 9, 5))
26+
27+
y = 1;
28+
>y : Symbol(y, Decl(unreachableReturnStatementsVsInferredReturnTypes.ts, 9, 5))
29+
30+
return y;
31+
>y : Symbol(y, Decl(unreachableReturnStatementsVsInferredReturnTypes.ts, 9, 5))
32+
}
33+
34+
export function i() {
35+
>i : Symbol(i, Decl(unreachableReturnStatementsVsInferredReturnTypes.ts, 12, 1))
36+
37+
let x: string | number | boolean;
38+
>x : Symbol(x, Decl(unreachableReturnStatementsVsInferredReturnTypes.ts, 15, 5))
39+
40+
x = 1;
41+
>x : Symbol(x, Decl(unreachableReturnStatementsVsInferredReturnTypes.ts, 15, 5))
42+
43+
return x;
44+
>x : Symbol(x, Decl(unreachableReturnStatementsVsInferredReturnTypes.ts, 15, 5))
45+
46+
x = "foo";
47+
>x : Symbol(x, Decl(unreachableReturnStatementsVsInferredReturnTypes.ts, 15, 5))
48+
49+
return x;
50+
>x : Symbol(x, Decl(unreachableReturnStatementsVsInferredReturnTypes.ts, 15, 5))
51+
}
52+
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
//// [tests/cases/compiler/unreachableReturnStatementsVsInferredReturnTypes.ts] ////
2+
3+
=== unreachableReturnStatementsVsInferredReturnTypes.ts ===
4+
export function g() {
5+
>g : () => number
6+
7+
let x;
8+
>x : any
9+
10+
x = 1;
11+
>x = 1 : 1
12+
>x : any
13+
>1 : 1
14+
15+
return x;
16+
>x : number
17+
18+
return x;
19+
>x : any
20+
}
21+
22+
export function h() {
23+
>h : () => number
24+
25+
return 1;
26+
>1 : 1
27+
28+
let y;
29+
>y : any
30+
31+
y = 1;
32+
>y = 1 : 1
33+
>y : any
34+
>1 : 1
35+
36+
return y;
37+
>y : any
38+
}
39+
40+
export function i() {
41+
>i : () => number
42+
43+
let x: string | number | boolean;
44+
>x : string | number | boolean
45+
46+
x = 1;
47+
>x = 1 : 1
48+
>x : string | number | boolean
49+
>1 : 1
50+
51+
return x;
52+
>x : number
53+
54+
x = "foo";
55+
>x = "foo" : "foo"
56+
>x : string | number | boolean
57+
>"foo" : "foo"
58+
59+
return x;
60+
>x : string | number | boolean
61+
}
62+
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
//// [tests/cases/compiler/unreachableYieldExpressionsVsInferredYieldTypes.ts] ////
2+
3+
//// [unreachableYieldExpressionsVsInferredYieldTypes.ts]
4+
export function* g() {
5+
let x;
6+
x = 1;
7+
yield x;
8+
return 'foo';
9+
yield x;
10+
}
11+
12+
export function* h() {
13+
return 'foo';
14+
let y;
15+
y = 1;
16+
yield y;
17+
}
18+
19+
export function* i() {
20+
yield true;
21+
return 'foo';
22+
let y;
23+
y = 1;
24+
yield y;
25+
}
26+
27+
export function* j() {
28+
let x: string | number | boolean;
29+
x = 1;
30+
yield x;
31+
return true;
32+
33+
x = "foo";
34+
yield x;
35+
}
36+
37+
38+
//// [unreachableYieldExpressionsVsInferredYieldTypes.js]
39+
export function* g() {
40+
let x;
41+
x = 1;
42+
yield x;
43+
return 'foo';
44+
yield x;
45+
}
46+
export function* h() {
47+
return 'foo';
48+
let y;
49+
y = 1;
50+
yield y;
51+
}
52+
export function* i() {
53+
yield true;
54+
return 'foo';
55+
let y;
56+
y = 1;
57+
yield y;
58+
}
59+
export function* j() {
60+
let x;
61+
x = 1;
62+
yield x;
63+
return true;
64+
x = "foo";
65+
yield x;
66+
}
67+
68+
69+
//// [unreachableYieldExpressionsVsInferredYieldTypes.d.ts]
70+
export declare function g(): Generator<number, string, unknown>;
71+
export declare function h(): Generator<never, string, unknown>;
72+
export declare function i(): Generator<boolean, string, unknown>;
73+
export declare function j(): Generator<number, boolean, unknown>;
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
//// [tests/cases/compiler/unreachableYieldExpressionsVsInferredYieldTypes.ts] ////
2+
3+
=== unreachableYieldExpressionsVsInferredYieldTypes.ts ===
4+
export function* g() {
5+
>g : Symbol(g, Decl(unreachableYieldExpressionsVsInferredYieldTypes.ts, 0, 0))
6+
7+
let x;
8+
>x : Symbol(x, Decl(unreachableYieldExpressionsVsInferredYieldTypes.ts, 1, 5))
9+
10+
x = 1;
11+
>x : Symbol(x, Decl(unreachableYieldExpressionsVsInferredYieldTypes.ts, 1, 5))
12+
13+
yield x;
14+
>x : Symbol(x, Decl(unreachableYieldExpressionsVsInferredYieldTypes.ts, 1, 5))
15+
16+
return 'foo';
17+
yield x;
18+
>x : Symbol(x, Decl(unreachableYieldExpressionsVsInferredYieldTypes.ts, 1, 5))
19+
}
20+
21+
export function* h() {
22+
>h : Symbol(h, Decl(unreachableYieldExpressionsVsInferredYieldTypes.ts, 6, 1))
23+
24+
return 'foo';
25+
let y;
26+
>y : Symbol(y, Decl(unreachableYieldExpressionsVsInferredYieldTypes.ts, 10, 5))
27+
28+
y = 1;
29+
>y : Symbol(y, Decl(unreachableYieldExpressionsVsInferredYieldTypes.ts, 10, 5))
30+
31+
yield y;
32+
>y : Symbol(y, Decl(unreachableYieldExpressionsVsInferredYieldTypes.ts, 10, 5))
33+
}
34+
35+
export function* i() {
36+
>i : Symbol(i, Decl(unreachableYieldExpressionsVsInferredYieldTypes.ts, 13, 1))
37+
38+
yield true;
39+
return 'foo';
40+
let y;
41+
>y : Symbol(y, Decl(unreachableYieldExpressionsVsInferredYieldTypes.ts, 18, 5))
42+
43+
y = 1;
44+
>y : Symbol(y, Decl(unreachableYieldExpressionsVsInferredYieldTypes.ts, 18, 5))
45+
46+
yield y;
47+
>y : Symbol(y, Decl(unreachableYieldExpressionsVsInferredYieldTypes.ts, 18, 5))
48+
}
49+
50+
export function* j() {
51+
>j : Symbol(j, Decl(unreachableYieldExpressionsVsInferredYieldTypes.ts, 21, 1))
52+
53+
let x: string | number | boolean;
54+
>x : Symbol(x, Decl(unreachableYieldExpressionsVsInferredYieldTypes.ts, 24, 5))
55+
56+
x = 1;
57+
>x : Symbol(x, Decl(unreachableYieldExpressionsVsInferredYieldTypes.ts, 24, 5))
58+
59+
yield x;
60+
>x : Symbol(x, Decl(unreachableYieldExpressionsVsInferredYieldTypes.ts, 24, 5))
61+
62+
return true;
63+
64+
x = "foo";
65+
>x : Symbol(x, Decl(unreachableYieldExpressionsVsInferredYieldTypes.ts, 24, 5))
66+
67+
yield x;
68+
>x : Symbol(x, Decl(unreachableYieldExpressionsVsInferredYieldTypes.ts, 24, 5))
69+
}
70+

0 commit comments

Comments
 (0)