From fa99df82582eff61f821530b9c24f46a861eddd0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Fri, 1 Sep 2023 19:16:50 +0200 Subject: [PATCH 1/9] Avoid inferring return and yield types from unreachable statements --- src/compiler/checker.ts | 8 ++ .../reference/plainJSBinderErrors.types | 2 +- .../reference/recursiveNamedLambdaCall.types | 10 +- .../unreachableJavascriptChecked.types | 2 +- .../unreachableJavascriptUnchecked.types | 2 +- ...leReturnStatementsVsInferredReturnTypes.js | 59 ++++++++++++ ...urnStatementsVsInferredReturnTypes.symbols | 52 ++++++++++ ...eturnStatementsVsInferredReturnTypes.types | 62 ++++++++++++ ...bleYieldExpressionsVsInferredYieldTypes.js | 73 ++++++++++++++ ...eldExpressionsVsInferredYieldTypes.symbols | 70 ++++++++++++++ ...YieldExpressionsVsInferredYieldTypes.types | 96 +++++++++++++++++++ ...leReturnStatementsVsInferredReturnTypes.ts | 25 +++++ ...bleYieldExpressionsVsInferredYieldTypes.ts | 37 +++++++ 13 files changed, 490 insertions(+), 8 deletions(-) create mode 100644 tests/baselines/reference/unreachableReturnStatementsVsInferredReturnTypes.js create mode 100644 tests/baselines/reference/unreachableReturnStatementsVsInferredReturnTypes.symbols create mode 100644 tests/baselines/reference/unreachableReturnStatementsVsInferredReturnTypes.types create mode 100644 tests/baselines/reference/unreachableYieldExpressionsVsInferredYieldTypes.js create mode 100644 tests/baselines/reference/unreachableYieldExpressionsVsInferredYieldTypes.symbols create mode 100644 tests/baselines/reference/unreachableYieldExpressionsVsInferredYieldTypes.types create mode 100644 tests/cases/compiler/unreachableReturnStatementsVsInferredReturnTypes.ts create mode 100644 tests/cases/compiler/unreachableYieldExpressionsVsInferredYieldTypes.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index f1eb9d5b7c606..aa1beaec038f0 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -36286,6 +36286,10 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { const nextTypes: Type[] = []; const isAsync = (getFunctionFlags(func) & FunctionFlags.Async) !== 0; forEachYieldExpression(func.body as Block, yieldExpression => { + const statement = findAncestor(yieldExpression, isStatement)!; + if (canHaveFlowNode(statement) && !statement.flowNode && !compilerOptions.allowUnreachableCode) { + return; + } const yieldExpressionType = yieldExpression.expression ? checkExpression(yieldExpression.expression, checkMode) : undefinedWideningType; pushIfUnique(yieldTypes, getYieldedTypeOfYieldExpression(yieldExpression, yieldExpressionType, anyType, isAsync)); let nextType: Type | undefined; @@ -36393,6 +36397,10 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return; } + if (!returnStatement.flowNode && !compilerOptions.allowUnreachableCode) { + return; + } + let type = checkExpressionCached(expr, checkMode && checkMode & ~CheckMode.SkipGenericFunctions); if (functionFlags & FunctionFlags.Async) { // From within an async function you can return either a non-promise value or a promise. Any diff --git a/tests/baselines/reference/plainJSBinderErrors.types b/tests/baselines/reference/plainJSBinderErrors.types index 39a2b3e7b7dd9..19445d9dffbab 100644 --- a/tests/baselines/reference/plainJSBinderErrors.types +++ b/tests/baselines/reference/plainJSBinderErrors.types @@ -82,7 +82,7 @@ class C { } } label() { ->label : () => number +>label : () => void for(;;) { label: var x = 1 diff --git a/tests/baselines/reference/recursiveNamedLambdaCall.types b/tests/baselines/reference/recursiveNamedLambdaCall.types index bd026a3fb8466..badd1a46c7858 100644 --- a/tests/baselines/reference/recursiveNamedLambdaCall.types +++ b/tests/baselines/reference/recursiveNamedLambdaCall.types @@ -14,10 +14,10 @@ var promise = function( obj ) { >doScroll : any (function doScrollCheck() { ->(function doScrollCheck() { if ( false ) { try { top.doScroll("left"); } catch(e) { return setTimeout( doScrollCheck, 50 ); } // detach all dom ready events detach(); } })() : any ->(function doScrollCheck() { if ( false ) { try { top.doScroll("left"); } catch(e) { return setTimeout( doScrollCheck, 50 ); } // detach all dom ready events detach(); } }) : () => any ->function doScrollCheck() { if ( false ) { try { top.doScroll("left"); } catch(e) { return setTimeout( doScrollCheck, 50 ); } // detach all dom ready events detach(); } } : () => any ->doScrollCheck : () => any +>(function doScrollCheck() { if ( false ) { try { top.doScroll("left"); } catch(e) { return setTimeout( doScrollCheck, 50 ); } // detach all dom ready events detach(); } })() : void +>(function doScrollCheck() { if ( false ) { try { top.doScroll("left"); } catch(e) { return setTimeout( doScrollCheck, 50 ); } // detach all dom ready events detach(); } }) : () => void +>function doScrollCheck() { if ( false ) { try { top.doScroll("left"); } catch(e) { return setTimeout( doScrollCheck, 50 ); } // detach all dom ready events detach(); } } : () => void +>doScrollCheck : () => void if ( false ) { >false : false @@ -36,7 +36,7 @@ var promise = function( obj ) { return setTimeout( doScrollCheck, 50 ); >setTimeout( doScrollCheck, 50 ) : any >setTimeout : any ->doScrollCheck : () => any +>doScrollCheck : () => void >50 : 50 } diff --git a/tests/baselines/reference/unreachableJavascriptChecked.types b/tests/baselines/reference/unreachableJavascriptChecked.types index 91fba91bc27f7..89c040c55a57b 100644 --- a/tests/baselines/reference/unreachableJavascriptChecked.types +++ b/tests/baselines/reference/unreachableJavascriptChecked.types @@ -2,7 +2,7 @@ === unreachable.js === function unreachable() { ->unreachable : () => void | 2 | 3 | 4 +>unreachable : () => void return f(); >f() : void diff --git a/tests/baselines/reference/unreachableJavascriptUnchecked.types b/tests/baselines/reference/unreachableJavascriptUnchecked.types index a7f27a2260fac..01705ff4bd8c7 100644 --- a/tests/baselines/reference/unreachableJavascriptUnchecked.types +++ b/tests/baselines/reference/unreachableJavascriptUnchecked.types @@ -2,7 +2,7 @@ === unreachable.js === function unreachable() { ->unreachable : () => 1 | 2 +>unreachable : () => number return 1; >1 : 1 diff --git a/tests/baselines/reference/unreachableReturnStatementsVsInferredReturnTypes.js b/tests/baselines/reference/unreachableReturnStatementsVsInferredReturnTypes.js new file mode 100644 index 0000000000000..c5ae1a4909b82 --- /dev/null +++ b/tests/baselines/reference/unreachableReturnStatementsVsInferredReturnTypes.js @@ -0,0 +1,59 @@ +//// [tests/cases/compiler/unreachableReturnStatementsVsInferredReturnTypes.ts] //// + +//// [unreachableReturnStatementsVsInferredReturnTypes.ts] +export function g() { + let x; + x = 1; + return x; + return x; +} + +export function h() { + return 1; + let y; + y = 1; + return y; +} + +export function i() { + let x: string | number | boolean; + x = 1; + return x; + + x = "foo"; + return x; +} + + +//// [unreachableReturnStatementsVsInferredReturnTypes.js] +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.i = exports.h = exports.g = void 0; +function g() { + var x; + x = 1; + return x; + return x; +} +exports.g = g; +function h() { + return 1; + var y; + y = 1; + return y; +} +exports.h = h; +function i() { + var x; + x = 1; + return x; + x = "foo"; + return x; +} +exports.i = i; + + +//// [unreachableReturnStatementsVsInferredReturnTypes.d.ts] +export declare function g(): number; +export declare function h(): number; +export declare function i(): number; diff --git a/tests/baselines/reference/unreachableReturnStatementsVsInferredReturnTypes.symbols b/tests/baselines/reference/unreachableReturnStatementsVsInferredReturnTypes.symbols new file mode 100644 index 0000000000000..6c266c9d37c72 --- /dev/null +++ b/tests/baselines/reference/unreachableReturnStatementsVsInferredReturnTypes.symbols @@ -0,0 +1,52 @@ +//// [tests/cases/compiler/unreachableReturnStatementsVsInferredReturnTypes.ts] //// + +=== unreachableReturnStatementsVsInferredReturnTypes.ts === +export function g() { +>g : Symbol(g, Decl(unreachableReturnStatementsVsInferredReturnTypes.ts, 0, 0)) + + let x; +>x : Symbol(x, Decl(unreachableReturnStatementsVsInferredReturnTypes.ts, 1, 5)) + + x = 1; +>x : Symbol(x, Decl(unreachableReturnStatementsVsInferredReturnTypes.ts, 1, 5)) + + return x; +>x : Symbol(x, Decl(unreachableReturnStatementsVsInferredReturnTypes.ts, 1, 5)) + + return x; +>x : Symbol(x, Decl(unreachableReturnStatementsVsInferredReturnTypes.ts, 1, 5)) +} + +export function h() { +>h : Symbol(h, Decl(unreachableReturnStatementsVsInferredReturnTypes.ts, 5, 1)) + + return 1; + let y; +>y : Symbol(y, Decl(unreachableReturnStatementsVsInferredReturnTypes.ts, 9, 5)) + + y = 1; +>y : Symbol(y, Decl(unreachableReturnStatementsVsInferredReturnTypes.ts, 9, 5)) + + return y; +>y : Symbol(y, Decl(unreachableReturnStatementsVsInferredReturnTypes.ts, 9, 5)) +} + +export function i() { +>i : Symbol(i, Decl(unreachableReturnStatementsVsInferredReturnTypes.ts, 12, 1)) + + let x: string | number | boolean; +>x : Symbol(x, Decl(unreachableReturnStatementsVsInferredReturnTypes.ts, 15, 5)) + + x = 1; +>x : Symbol(x, Decl(unreachableReturnStatementsVsInferredReturnTypes.ts, 15, 5)) + + return x; +>x : Symbol(x, Decl(unreachableReturnStatementsVsInferredReturnTypes.ts, 15, 5)) + + x = "foo"; +>x : Symbol(x, Decl(unreachableReturnStatementsVsInferredReturnTypes.ts, 15, 5)) + + return x; +>x : Symbol(x, Decl(unreachableReturnStatementsVsInferredReturnTypes.ts, 15, 5)) +} + diff --git a/tests/baselines/reference/unreachableReturnStatementsVsInferredReturnTypes.types b/tests/baselines/reference/unreachableReturnStatementsVsInferredReturnTypes.types new file mode 100644 index 0000000000000..b0e0583d6c1b7 --- /dev/null +++ b/tests/baselines/reference/unreachableReturnStatementsVsInferredReturnTypes.types @@ -0,0 +1,62 @@ +//// [tests/cases/compiler/unreachableReturnStatementsVsInferredReturnTypes.ts] //// + +=== unreachableReturnStatementsVsInferredReturnTypes.ts === +export function g() { +>g : () => number + + let x; +>x : any + + x = 1; +>x = 1 : 1 +>x : any +>1 : 1 + + return x; +>x : number + + return x; +>x : any +} + +export function h() { +>h : () => number + + return 1; +>1 : 1 + + let y; +>y : any + + y = 1; +>y = 1 : 1 +>y : any +>1 : 1 + + return y; +>y : any +} + +export function i() { +>i : () => number + + let x: string | number | boolean; +>x : string | number | boolean + + x = 1; +>x = 1 : 1 +>x : string | number | boolean +>1 : 1 + + return x; +>x : number + + x = "foo"; +>x = "foo" : "foo" +>x : string | number | boolean +>"foo" : "foo" + + return x; +>x : string | number | boolean +} + diff --git a/tests/baselines/reference/unreachableYieldExpressionsVsInferredYieldTypes.js b/tests/baselines/reference/unreachableYieldExpressionsVsInferredYieldTypes.js new file mode 100644 index 0000000000000..81cee1362a574 --- /dev/null +++ b/tests/baselines/reference/unreachableYieldExpressionsVsInferredYieldTypes.js @@ -0,0 +1,73 @@ +//// [tests/cases/compiler/unreachableYieldExpressionsVsInferredYieldTypes.ts] //// + +//// [unreachableYieldExpressionsVsInferredYieldTypes.ts] +export function* g() { + let x; + x = 1; + yield x; + return 'foo'; + yield x; +} + +export function* h() { + return 'foo'; + let y; + y = 1; + yield y; +} + +export function* i() { + yield true; + return 'foo'; + let y; + y = 1; + yield y; +} + +export function* j() { + let x: string | number | boolean; + x = 1; + yield x; + return true; + + x = "foo"; + yield x; +} + + +//// [unreachableYieldExpressionsVsInferredYieldTypes.js] +export function* g() { + let x; + x = 1; + yield x; + return 'foo'; + yield x; +} +export function* h() { + return 'foo'; + let y; + y = 1; + yield y; +} +export function* i() { + yield true; + return 'foo'; + let y; + y = 1; + yield y; +} +export function* j() { + let x; + x = 1; + yield x; + return true; + x = "foo"; + yield x; +} + + +//// [unreachableYieldExpressionsVsInferredYieldTypes.d.ts] +export declare function g(): Generator; +export declare function h(): Generator; +export declare function i(): Generator; +export declare function j(): Generator; diff --git a/tests/baselines/reference/unreachableYieldExpressionsVsInferredYieldTypes.symbols b/tests/baselines/reference/unreachableYieldExpressionsVsInferredYieldTypes.symbols new file mode 100644 index 0000000000000..c588297e4dd23 --- /dev/null +++ b/tests/baselines/reference/unreachableYieldExpressionsVsInferredYieldTypes.symbols @@ -0,0 +1,70 @@ +//// [tests/cases/compiler/unreachableYieldExpressionsVsInferredYieldTypes.ts] //// + +=== unreachableYieldExpressionsVsInferredYieldTypes.ts === +export function* g() { +>g : Symbol(g, Decl(unreachableYieldExpressionsVsInferredYieldTypes.ts, 0, 0)) + + let x; +>x : Symbol(x, Decl(unreachableYieldExpressionsVsInferredYieldTypes.ts, 1, 5)) + + x = 1; +>x : Symbol(x, Decl(unreachableYieldExpressionsVsInferredYieldTypes.ts, 1, 5)) + + yield x; +>x : Symbol(x, Decl(unreachableYieldExpressionsVsInferredYieldTypes.ts, 1, 5)) + + return 'foo'; + yield x; +>x : Symbol(x, Decl(unreachableYieldExpressionsVsInferredYieldTypes.ts, 1, 5)) +} + +export function* h() { +>h : Symbol(h, Decl(unreachableYieldExpressionsVsInferredYieldTypes.ts, 6, 1)) + + return 'foo'; + let y; +>y : Symbol(y, Decl(unreachableYieldExpressionsVsInferredYieldTypes.ts, 10, 5)) + + y = 1; +>y : Symbol(y, Decl(unreachableYieldExpressionsVsInferredYieldTypes.ts, 10, 5)) + + yield y; +>y : Symbol(y, Decl(unreachableYieldExpressionsVsInferredYieldTypes.ts, 10, 5)) +} + +export function* i() { +>i : Symbol(i, Decl(unreachableYieldExpressionsVsInferredYieldTypes.ts, 13, 1)) + + yield true; + return 'foo'; + let y; +>y : Symbol(y, Decl(unreachableYieldExpressionsVsInferredYieldTypes.ts, 18, 5)) + + y = 1; +>y : Symbol(y, Decl(unreachableYieldExpressionsVsInferredYieldTypes.ts, 18, 5)) + + yield y; +>y : Symbol(y, Decl(unreachableYieldExpressionsVsInferredYieldTypes.ts, 18, 5)) +} + +export function* j() { +>j : Symbol(j, Decl(unreachableYieldExpressionsVsInferredYieldTypes.ts, 21, 1)) + + let x: string | number | boolean; +>x : Symbol(x, Decl(unreachableYieldExpressionsVsInferredYieldTypes.ts, 24, 5)) + + x = 1; +>x : Symbol(x, Decl(unreachableYieldExpressionsVsInferredYieldTypes.ts, 24, 5)) + + yield x; +>x : Symbol(x, Decl(unreachableYieldExpressionsVsInferredYieldTypes.ts, 24, 5)) + + return true; + + x = "foo"; +>x : Symbol(x, Decl(unreachableYieldExpressionsVsInferredYieldTypes.ts, 24, 5)) + + yield x; +>x : Symbol(x, Decl(unreachableYieldExpressionsVsInferredYieldTypes.ts, 24, 5)) +} + diff --git a/tests/baselines/reference/unreachableYieldExpressionsVsInferredYieldTypes.types b/tests/baselines/reference/unreachableYieldExpressionsVsInferredYieldTypes.types new file mode 100644 index 0000000000000..4630bdedf468e --- /dev/null +++ b/tests/baselines/reference/unreachableYieldExpressionsVsInferredYieldTypes.types @@ -0,0 +1,96 @@ +//// [tests/cases/compiler/unreachableYieldExpressionsVsInferredYieldTypes.ts] //// + +=== unreachableYieldExpressionsVsInferredYieldTypes.ts === +export function* g() { +>g : () => Generator + + let x; +>x : any + + x = 1; +>x = 1 : 1 +>x : any +>1 : 1 + + yield x; +>yield x : any +>x : number + + return 'foo'; +>'foo' : "foo" + + yield x; +>yield x : any +>x : any +} + +export function* h() { +>h : () => Generator + + return 'foo'; +>'foo' : "foo" + + let y; +>y : any + + y = 1; +>y = 1 : 1 +>y : any +>1 : 1 + + yield y; +>yield y : any +>y : any +} + +export function* i() { +>i : () => Generator + + yield true; +>yield true : any +>true : true + + return 'foo'; +>'foo' : "foo" + + let y; +>y : any + + y = 1; +>y = 1 : 1 +>y : any +>1 : 1 + + yield y; +>yield y : any +>y : any +} + +export function* j() { +>j : () => Generator + + let x: string | number | boolean; +>x : string | number | boolean + + x = 1; +>x = 1 : 1 +>x : string | number | boolean +>1 : 1 + + yield x; +>yield x : any +>x : number + + return true; +>true : true + + x = "foo"; +>x = "foo" : "foo" +>x : string | number | boolean +>"foo" : "foo" + + yield x; +>yield x : any +>x : string | number | boolean +} + diff --git a/tests/cases/compiler/unreachableReturnStatementsVsInferredReturnTypes.ts b/tests/cases/compiler/unreachableReturnStatementsVsInferredReturnTypes.ts new file mode 100644 index 0000000000000..2b81acb2d4fd8 --- /dev/null +++ b/tests/cases/compiler/unreachableReturnStatementsVsInferredReturnTypes.ts @@ -0,0 +1,25 @@ +// @strict: true +// @declaration: true + +export function g() { + let x; + x = 1; + return x; + return x; +} + +export function h() { + return 1; + let y; + y = 1; + return y; +} + +export function i() { + let x: string | number | boolean; + x = 1; + return x; + + x = "foo"; + return x; +} diff --git a/tests/cases/compiler/unreachableYieldExpressionsVsInferredYieldTypes.ts b/tests/cases/compiler/unreachableYieldExpressionsVsInferredYieldTypes.ts new file mode 100644 index 0000000000000..acb77677c36b4 --- /dev/null +++ b/tests/cases/compiler/unreachableYieldExpressionsVsInferredYieldTypes.ts @@ -0,0 +1,37 @@ +// @strict: true +// @target: esnext +// @lib: esnext +// @declaration: true + +export function* g() { + let x; + x = 1; + yield x; + return 'foo'; + yield x; +} + +export function* h() { + return 'foo'; + let y; + y = 1; + yield y; +} + +export function* i() { + yield true; + return 'foo'; + let y; + y = 1; + yield y; +} + +export function* j() { + let x: string | number | boolean; + x = 1; + yield x; + return true; + + x = "foo"; + yield x; +} From f04bc03327ac8fa27358c5f738eaf86c93326e36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Thu, 13 Nov 2025 09:43:18 +0100 Subject: [PATCH 2/9] dont gate it on allowUnreachableCode --- src/compiler/checker.ts | 4 +- ...pes(allowunreachablecode=false).errors.txt | 36 ++++++ ...eturnTypes(allowunreachablecode=false).js} | 0 ...Types(allowunreachablecode=false).symbols} | 0 ...urnTypes(allowunreachablecode=false).types | 86 +++++++++++++ ...dReturnTypes(allowunreachablecode=true).js | 58 +++++++++ ...rnTypes(allowunreachablecode=true).symbols | 52 ++++++++ ...urnTypes(allowunreachablecode=true).types} | 0 ...pes(allowunreachablecode=false).errors.txt | 49 +++++++ ...YieldTypes(allowunreachablecode=false).js} | 0 ...Types(allowunreachablecode=false).symbols} | 0 ...ldTypes(allowunreachablecode=false).types} | 16 +++ ...edYieldTypes(allowunreachablecode=true).js | 73 +++++++++++ ...ldTypes(allowunreachablecode=true).symbols | 70 ++++++++++ ...ieldTypes(allowunreachablecode=true).types | 121 ++++++++++++++++++ ...leReturnStatementsVsInferredReturnTypes.ts | 1 + ...bleYieldExpressionsVsInferredYieldTypes.ts | 1 + 17 files changed, 565 insertions(+), 2 deletions(-) create mode 100644 tests/baselines/reference/unreachableReturnStatementsVsInferredReturnTypes(allowunreachablecode=false).errors.txt rename tests/baselines/reference/{unreachableReturnStatementsVsInferredReturnTypes.js => unreachableReturnStatementsVsInferredReturnTypes(allowunreachablecode=false).js} (100%) rename tests/baselines/reference/{unreachableReturnStatementsVsInferredReturnTypes.symbols => unreachableReturnStatementsVsInferredReturnTypes(allowunreachablecode=false).symbols} (100%) create mode 100644 tests/baselines/reference/unreachableReturnStatementsVsInferredReturnTypes(allowunreachablecode=false).types create mode 100644 tests/baselines/reference/unreachableReturnStatementsVsInferredReturnTypes(allowunreachablecode=true).js create mode 100644 tests/baselines/reference/unreachableReturnStatementsVsInferredReturnTypes(allowunreachablecode=true).symbols rename tests/baselines/reference/{unreachableReturnStatementsVsInferredReturnTypes.types => unreachableReturnStatementsVsInferredReturnTypes(allowunreachablecode=true).types} (100%) create mode 100644 tests/baselines/reference/unreachableYieldExpressionsVsInferredYieldTypes(allowunreachablecode=false).errors.txt rename tests/baselines/reference/{unreachableYieldExpressionsVsInferredYieldTypes.js => unreachableYieldExpressionsVsInferredYieldTypes(allowunreachablecode=false).js} (100%) rename tests/baselines/reference/{unreachableYieldExpressionsVsInferredYieldTypes.symbols => unreachableYieldExpressionsVsInferredYieldTypes(allowunreachablecode=false).symbols} (100%) rename tests/baselines/reference/{unreachableYieldExpressionsVsInferredYieldTypes.types => unreachableYieldExpressionsVsInferredYieldTypes(allowunreachablecode=false).types} (84%) create mode 100644 tests/baselines/reference/unreachableYieldExpressionsVsInferredYieldTypes(allowunreachablecode=true).js create mode 100644 tests/baselines/reference/unreachableYieldExpressionsVsInferredYieldTypes(allowunreachablecode=true).symbols create mode 100644 tests/baselines/reference/unreachableYieldExpressionsVsInferredYieldTypes(allowunreachablecode=true).types diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index a385e9a6c38bf..6ba0cfa0548a4 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -39214,7 +39214,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { const isAsync = (getFunctionFlags(func) & FunctionFlags.Async) !== 0; forEachYieldExpression(func.body as Block, yieldExpression => { const statement = findAncestor(yieldExpression, isStatement)!; - if (canHaveFlowNode(statement) && !statement.flowNode && !compilerOptions.allowUnreachableCode) { + if (canHaveFlowNode(statement) && !statement.flowNode) { return; } let yieldExpressionType = yieldExpression.expression ? checkExpression(yieldExpression.expression, checkMode) : undefinedWideningType; @@ -39336,7 +39336,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return; } - if (!returnStatement.flowNode && !compilerOptions.allowUnreachableCode) { + if (!returnStatement.flowNode) { return; } diff --git a/tests/baselines/reference/unreachableReturnStatementsVsInferredReturnTypes(allowunreachablecode=false).errors.txt b/tests/baselines/reference/unreachableReturnStatementsVsInferredReturnTypes(allowunreachablecode=false).errors.txt new file mode 100644 index 0000000000000..757995ee91288 --- /dev/null +++ b/tests/baselines/reference/unreachableReturnStatementsVsInferredReturnTypes(allowunreachablecode=false).errors.txt @@ -0,0 +1,36 @@ +unreachableReturnStatementsVsInferredReturnTypes.ts(5,3): error TS7027: Unreachable code detected. +unreachableReturnStatementsVsInferredReturnTypes.ts(10,3): error TS7027: Unreachable code detected. +unreachableReturnStatementsVsInferredReturnTypes.ts(20,3): error TS7027: Unreachable code detected. + + +==== unreachableReturnStatementsVsInferredReturnTypes.ts (3 errors) ==== + export function g() { + let x; + x = 1; + return x; + return x; + ~~~~~~~~~ +!!! error TS7027: Unreachable code detected. + } + + export function h() { + return 1; + let y; + ~~~~~~ +!!! error TS7027: Unreachable code detected. + y = 1; + return y; + } + + export function i() { + let x: string | number | boolean; + x = 1; + return x; + + x = "foo"; + ~~~~~~~~~~ + return x; + ~~~~~~~~~~~ +!!! error TS7027: Unreachable code detected. + } + \ No newline at end of file diff --git a/tests/baselines/reference/unreachableReturnStatementsVsInferredReturnTypes.js b/tests/baselines/reference/unreachableReturnStatementsVsInferredReturnTypes(allowunreachablecode=false).js similarity index 100% rename from tests/baselines/reference/unreachableReturnStatementsVsInferredReturnTypes.js rename to tests/baselines/reference/unreachableReturnStatementsVsInferredReturnTypes(allowunreachablecode=false).js diff --git a/tests/baselines/reference/unreachableReturnStatementsVsInferredReturnTypes.symbols b/tests/baselines/reference/unreachableReturnStatementsVsInferredReturnTypes(allowunreachablecode=false).symbols similarity index 100% rename from tests/baselines/reference/unreachableReturnStatementsVsInferredReturnTypes.symbols rename to tests/baselines/reference/unreachableReturnStatementsVsInferredReturnTypes(allowunreachablecode=false).symbols diff --git a/tests/baselines/reference/unreachableReturnStatementsVsInferredReturnTypes(allowunreachablecode=false).types b/tests/baselines/reference/unreachableReturnStatementsVsInferredReturnTypes(allowunreachablecode=false).types new file mode 100644 index 0000000000000..02aa79cd53afd --- /dev/null +++ b/tests/baselines/reference/unreachableReturnStatementsVsInferredReturnTypes(allowunreachablecode=false).types @@ -0,0 +1,86 @@ +//// [tests/cases/compiler/unreachableReturnStatementsVsInferredReturnTypes.ts] //// + +=== unreachableReturnStatementsVsInferredReturnTypes.ts === +export function g() { +>g : () => number +> : ^^^^^^^^^^^^ + + let x; +>x : any +> : ^^^ + + x = 1; +>x = 1 : 1 +> : ^ +>x : any +> : ^^^ +>1 : 1 +> : ^ + + return x; +>x : number +> : ^^^^^^ + + return x; +>x : any +> : ^^^ +} + +export function h() { +>h : () => number +> : ^^^^^^^^^^^^ + + return 1; +>1 : 1 +> : ^ + + let y; +>y : any +> : ^^^ + + y = 1; +>y = 1 : 1 +> : ^ +>y : any +> : ^^^ +>1 : 1 +> : ^ + + return y; +>y : any +> : ^^^ +} + +export function i() { +>i : () => number +> : ^^^^^^^^^^^^ + + let x: string | number | boolean; +>x : string | number | boolean +> : ^^^^^^^^^^^^^^^^^^^^^^^^^ + + x = 1; +>x = 1 : 1 +> : ^ +>x : string | number | boolean +> : ^^^^^^^^^^^^^^^^^^^^^^^^^ +>1 : 1 +> : ^ + + return x; +>x : number +> : ^^^^^^ + + x = "foo"; +>x = "foo" : "foo" +> : ^^^^^ +>x : string | number | boolean +> : ^^^^^^^^^^^^^^^^^^^^^^^^^ +>"foo" : "foo" +> : ^^^^^ + + return x; +>x : string | number | boolean +> : ^^^^^^^^^^^^^^^^^^^^^^^^^ +} + diff --git a/tests/baselines/reference/unreachableReturnStatementsVsInferredReturnTypes(allowunreachablecode=true).js b/tests/baselines/reference/unreachableReturnStatementsVsInferredReturnTypes(allowunreachablecode=true).js new file mode 100644 index 0000000000000..f8a3582239eee --- /dev/null +++ b/tests/baselines/reference/unreachableReturnStatementsVsInferredReturnTypes(allowunreachablecode=true).js @@ -0,0 +1,58 @@ +//// [tests/cases/compiler/unreachableReturnStatementsVsInferredReturnTypes.ts] //// + +//// [unreachableReturnStatementsVsInferredReturnTypes.ts] +export function g() { + let x; + x = 1; + return x; + return x; +} + +export function h() { + return 1; + let y; + y = 1; + return y; +} + +export function i() { + let x: string | number | boolean; + x = 1; + return x; + + x = "foo"; + return x; +} + + +//// [unreachableReturnStatementsVsInferredReturnTypes.js] +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.g = g; +exports.h = h; +exports.i = i; +function g() { + var x; + x = 1; + return x; + return x; +} +function h() { + return 1; + var y; + y = 1; + return y; +} +function i() { + var x; + x = 1; + return x; + x = "foo"; + return x; +} + + +//// [unreachableReturnStatementsVsInferredReturnTypes.d.ts] +export declare function g(): number; +export declare function h(): number; +export declare function i(): number; diff --git a/tests/baselines/reference/unreachableReturnStatementsVsInferredReturnTypes(allowunreachablecode=true).symbols b/tests/baselines/reference/unreachableReturnStatementsVsInferredReturnTypes(allowunreachablecode=true).symbols new file mode 100644 index 0000000000000..6c266c9d37c72 --- /dev/null +++ b/tests/baselines/reference/unreachableReturnStatementsVsInferredReturnTypes(allowunreachablecode=true).symbols @@ -0,0 +1,52 @@ +//// [tests/cases/compiler/unreachableReturnStatementsVsInferredReturnTypes.ts] //// + +=== unreachableReturnStatementsVsInferredReturnTypes.ts === +export function g() { +>g : Symbol(g, Decl(unreachableReturnStatementsVsInferredReturnTypes.ts, 0, 0)) + + let x; +>x : Symbol(x, Decl(unreachableReturnStatementsVsInferredReturnTypes.ts, 1, 5)) + + x = 1; +>x : Symbol(x, Decl(unreachableReturnStatementsVsInferredReturnTypes.ts, 1, 5)) + + return x; +>x : Symbol(x, Decl(unreachableReturnStatementsVsInferredReturnTypes.ts, 1, 5)) + + return x; +>x : Symbol(x, Decl(unreachableReturnStatementsVsInferredReturnTypes.ts, 1, 5)) +} + +export function h() { +>h : Symbol(h, Decl(unreachableReturnStatementsVsInferredReturnTypes.ts, 5, 1)) + + return 1; + let y; +>y : Symbol(y, Decl(unreachableReturnStatementsVsInferredReturnTypes.ts, 9, 5)) + + y = 1; +>y : Symbol(y, Decl(unreachableReturnStatementsVsInferredReturnTypes.ts, 9, 5)) + + return y; +>y : Symbol(y, Decl(unreachableReturnStatementsVsInferredReturnTypes.ts, 9, 5)) +} + +export function i() { +>i : Symbol(i, Decl(unreachableReturnStatementsVsInferredReturnTypes.ts, 12, 1)) + + let x: string | number | boolean; +>x : Symbol(x, Decl(unreachableReturnStatementsVsInferredReturnTypes.ts, 15, 5)) + + x = 1; +>x : Symbol(x, Decl(unreachableReturnStatementsVsInferredReturnTypes.ts, 15, 5)) + + return x; +>x : Symbol(x, Decl(unreachableReturnStatementsVsInferredReturnTypes.ts, 15, 5)) + + x = "foo"; +>x : Symbol(x, Decl(unreachableReturnStatementsVsInferredReturnTypes.ts, 15, 5)) + + return x; +>x : Symbol(x, Decl(unreachableReturnStatementsVsInferredReturnTypes.ts, 15, 5)) +} + diff --git a/tests/baselines/reference/unreachableReturnStatementsVsInferredReturnTypes.types b/tests/baselines/reference/unreachableReturnStatementsVsInferredReturnTypes(allowunreachablecode=true).types similarity index 100% rename from tests/baselines/reference/unreachableReturnStatementsVsInferredReturnTypes.types rename to tests/baselines/reference/unreachableReturnStatementsVsInferredReturnTypes(allowunreachablecode=true).types diff --git a/tests/baselines/reference/unreachableYieldExpressionsVsInferredYieldTypes(allowunreachablecode=false).errors.txt b/tests/baselines/reference/unreachableYieldExpressionsVsInferredYieldTypes(allowunreachablecode=false).errors.txt new file mode 100644 index 0000000000000..271ac38c5c55d --- /dev/null +++ b/tests/baselines/reference/unreachableYieldExpressionsVsInferredYieldTypes(allowunreachablecode=false).errors.txt @@ -0,0 +1,49 @@ +unreachableYieldExpressionsVsInferredYieldTypes.ts(6,3): error TS7027: Unreachable code detected. +unreachableYieldExpressionsVsInferredYieldTypes.ts(11,3): error TS7027: Unreachable code detected. +unreachableYieldExpressionsVsInferredYieldTypes.ts(19,3): error TS7027: Unreachable code detected. +unreachableYieldExpressionsVsInferredYieldTypes.ts(30,3): error TS7027: Unreachable code detected. + + +==== unreachableYieldExpressionsVsInferredYieldTypes.ts (4 errors) ==== + export function* g() { + let x; + x = 1; + yield x; + return 'foo'; + yield x; + ~~~~~~~~ +!!! error TS7027: Unreachable code detected. + } + + export function* h() { + return 'foo'; + let y; + ~~~~~~ +!!! error TS7027: Unreachable code detected. + y = 1; + yield y; + } + + export function* i() { + yield true; + return 'foo'; + let y; + ~~~~~~ +!!! error TS7027: Unreachable code detected. + y = 1; + yield y; + } + + export function* j() { + let x: string | number | boolean; + x = 1; + yield x; + return true; + + x = "foo"; + ~~~~~~~~~~ + yield x; + ~~~~~~~~~~ +!!! error TS7027: Unreachable code detected. + } + \ No newline at end of file diff --git a/tests/baselines/reference/unreachableYieldExpressionsVsInferredYieldTypes.js b/tests/baselines/reference/unreachableYieldExpressionsVsInferredYieldTypes(allowunreachablecode=false).js similarity index 100% rename from tests/baselines/reference/unreachableYieldExpressionsVsInferredYieldTypes.js rename to tests/baselines/reference/unreachableYieldExpressionsVsInferredYieldTypes(allowunreachablecode=false).js diff --git a/tests/baselines/reference/unreachableYieldExpressionsVsInferredYieldTypes.symbols b/tests/baselines/reference/unreachableYieldExpressionsVsInferredYieldTypes(allowunreachablecode=false).symbols similarity index 100% rename from tests/baselines/reference/unreachableYieldExpressionsVsInferredYieldTypes.symbols rename to tests/baselines/reference/unreachableYieldExpressionsVsInferredYieldTypes(allowunreachablecode=false).symbols diff --git a/tests/baselines/reference/unreachableYieldExpressionsVsInferredYieldTypes.types b/tests/baselines/reference/unreachableYieldExpressionsVsInferredYieldTypes(allowunreachablecode=false).types similarity index 84% rename from tests/baselines/reference/unreachableYieldExpressionsVsInferredYieldTypes.types rename to tests/baselines/reference/unreachableYieldExpressionsVsInferredYieldTypes(allowunreachablecode=false).types index 4e12810c9c43b..20840a3afdefe 100644 --- a/tests/baselines/reference/unreachableYieldExpressionsVsInferredYieldTypes.types +++ b/tests/baselines/reference/unreachableYieldExpressionsVsInferredYieldTypes(allowunreachablecode=false).types @@ -7,16 +7,19 @@ export function* g() { let x; >x : any +> : ^^^ x = 1; >x = 1 : 1 > : ^ >x : any +> : ^^^ >1 : 1 > : ^ yield x; >yield x : any +> : ^^^ >x : number > : ^^^^^^ @@ -26,7 +29,9 @@ export function* g() { yield x; >yield x : any +> : ^^^ >x : any +> : ^^^ } export function* h() { @@ -39,17 +44,21 @@ export function* h() { let y; >y : any +> : ^^^ y = 1; >y = 1 : 1 > : ^ >y : any +> : ^^^ >1 : 1 > : ^ yield y; >yield y : any +> : ^^^ >y : any +> : ^^^ } export function* i() { @@ -58,6 +67,7 @@ export function* i() { yield true; >yield true : any +> : ^^^ >true : true > : ^^^^ @@ -67,17 +77,21 @@ export function* i() { let y; >y : any +> : ^^^ y = 1; >y = 1 : 1 > : ^ >y : any +> : ^^^ >1 : 1 > : ^ yield y; >yield y : any +> : ^^^ >y : any +> : ^^^ } export function* j() { @@ -98,6 +112,7 @@ export function* j() { yield x; >yield x : any +> : ^^^ >x : number > : ^^^^^^ @@ -115,6 +130,7 @@ export function* j() { yield x; >yield x : any +> : ^^^ >x : string | number | boolean > : ^^^^^^^^^^^^^^^^^^^^^^^^^ } diff --git a/tests/baselines/reference/unreachableYieldExpressionsVsInferredYieldTypes(allowunreachablecode=true).js b/tests/baselines/reference/unreachableYieldExpressionsVsInferredYieldTypes(allowunreachablecode=true).js new file mode 100644 index 0000000000000..58c666feac74a --- /dev/null +++ b/tests/baselines/reference/unreachableYieldExpressionsVsInferredYieldTypes(allowunreachablecode=true).js @@ -0,0 +1,73 @@ +//// [tests/cases/compiler/unreachableYieldExpressionsVsInferredYieldTypes.ts] //// + +//// [unreachableYieldExpressionsVsInferredYieldTypes.ts] +export function* g() { + let x; + x = 1; + yield x; + return 'foo'; + yield x; +} + +export function* h() { + return 'foo'; + let y; + y = 1; + yield y; +} + +export function* i() { + yield true; + return 'foo'; + let y; + y = 1; + yield y; +} + +export function* j() { + let x: string | number | boolean; + x = 1; + yield x; + return true; + + x = "foo"; + yield x; +} + + +//// [unreachableYieldExpressionsVsInferredYieldTypes.js] +export function* g() { + let x; + x = 1; + yield x; + return 'foo'; + yield x; +} +export function* h() { + return 'foo'; + let y; + y = 1; + yield y; +} +export function* i() { + yield true; + return 'foo'; + let y; + y = 1; + yield y; +} +export function* j() { + let x; + x = 1; + yield x; + return true; + x = "foo"; + yield x; +} + + +//// [unreachableYieldExpressionsVsInferredYieldTypes.d.ts] +export declare function g(): Generator; +export declare function h(): Generator; +export declare function i(): Generator; +export declare function j(): Generator; diff --git a/tests/baselines/reference/unreachableYieldExpressionsVsInferredYieldTypes(allowunreachablecode=true).symbols b/tests/baselines/reference/unreachableYieldExpressionsVsInferredYieldTypes(allowunreachablecode=true).symbols new file mode 100644 index 0000000000000..c588297e4dd23 --- /dev/null +++ b/tests/baselines/reference/unreachableYieldExpressionsVsInferredYieldTypes(allowunreachablecode=true).symbols @@ -0,0 +1,70 @@ +//// [tests/cases/compiler/unreachableYieldExpressionsVsInferredYieldTypes.ts] //// + +=== unreachableYieldExpressionsVsInferredYieldTypes.ts === +export function* g() { +>g : Symbol(g, Decl(unreachableYieldExpressionsVsInferredYieldTypes.ts, 0, 0)) + + let x; +>x : Symbol(x, Decl(unreachableYieldExpressionsVsInferredYieldTypes.ts, 1, 5)) + + x = 1; +>x : Symbol(x, Decl(unreachableYieldExpressionsVsInferredYieldTypes.ts, 1, 5)) + + yield x; +>x : Symbol(x, Decl(unreachableYieldExpressionsVsInferredYieldTypes.ts, 1, 5)) + + return 'foo'; + yield x; +>x : Symbol(x, Decl(unreachableYieldExpressionsVsInferredYieldTypes.ts, 1, 5)) +} + +export function* h() { +>h : Symbol(h, Decl(unreachableYieldExpressionsVsInferredYieldTypes.ts, 6, 1)) + + return 'foo'; + let y; +>y : Symbol(y, Decl(unreachableYieldExpressionsVsInferredYieldTypes.ts, 10, 5)) + + y = 1; +>y : Symbol(y, Decl(unreachableYieldExpressionsVsInferredYieldTypes.ts, 10, 5)) + + yield y; +>y : Symbol(y, Decl(unreachableYieldExpressionsVsInferredYieldTypes.ts, 10, 5)) +} + +export function* i() { +>i : Symbol(i, Decl(unreachableYieldExpressionsVsInferredYieldTypes.ts, 13, 1)) + + yield true; + return 'foo'; + let y; +>y : Symbol(y, Decl(unreachableYieldExpressionsVsInferredYieldTypes.ts, 18, 5)) + + y = 1; +>y : Symbol(y, Decl(unreachableYieldExpressionsVsInferredYieldTypes.ts, 18, 5)) + + yield y; +>y : Symbol(y, Decl(unreachableYieldExpressionsVsInferredYieldTypes.ts, 18, 5)) +} + +export function* j() { +>j : Symbol(j, Decl(unreachableYieldExpressionsVsInferredYieldTypes.ts, 21, 1)) + + let x: string | number | boolean; +>x : Symbol(x, Decl(unreachableYieldExpressionsVsInferredYieldTypes.ts, 24, 5)) + + x = 1; +>x : Symbol(x, Decl(unreachableYieldExpressionsVsInferredYieldTypes.ts, 24, 5)) + + yield x; +>x : Symbol(x, Decl(unreachableYieldExpressionsVsInferredYieldTypes.ts, 24, 5)) + + return true; + + x = "foo"; +>x : Symbol(x, Decl(unreachableYieldExpressionsVsInferredYieldTypes.ts, 24, 5)) + + yield x; +>x : Symbol(x, Decl(unreachableYieldExpressionsVsInferredYieldTypes.ts, 24, 5)) +} + diff --git a/tests/baselines/reference/unreachableYieldExpressionsVsInferredYieldTypes(allowunreachablecode=true).types b/tests/baselines/reference/unreachableYieldExpressionsVsInferredYieldTypes(allowunreachablecode=true).types new file mode 100644 index 0000000000000..b362748dc2580 --- /dev/null +++ b/tests/baselines/reference/unreachableYieldExpressionsVsInferredYieldTypes(allowunreachablecode=true).types @@ -0,0 +1,121 @@ +//// [tests/cases/compiler/unreachableYieldExpressionsVsInferredYieldTypes.ts] //// + +=== unreachableYieldExpressionsVsInferredYieldTypes.ts === +export function* g() { +>g : () => Generator +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + let x; +>x : any + + x = 1; +>x = 1 : 1 +> : ^ +>x : any +>1 : 1 +> : ^ + + yield x; +>yield x : any +>x : number +> : ^^^^^^ + + return 'foo'; +>'foo' : "foo" +> : ^^^^^ + + yield x; +>yield x : any +>x : any +} + +export function* h() { +>h : () => Generator +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + return 'foo'; +>'foo' : "foo" +> : ^^^^^ + + let y; +>y : any + + y = 1; +>y = 1 : 1 +> : ^ +>y : any +>1 : 1 +> : ^ + + yield y; +>yield y : any +>y : any +} + +export function* i() { +>i : () => Generator +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + yield true; +>yield true : any +>true : true +> : ^^^^ + + return 'foo'; +>'foo' : "foo" +> : ^^^^^ + + let y; +>y : any + + y = 1; +>y = 1 : 1 +> : ^ +>y : any +>1 : 1 +> : ^ + + yield y; +>yield y : any +>y : any +} + +export function* j() { +>j : () => Generator +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + let x: string | number | boolean; +>x : string | number | boolean +> : ^^^^^^^^^^^^^^^^^^^^^^^^^ + + x = 1; +>x = 1 : 1 +> : ^ +>x : string | number | boolean +> : ^^^^^^^^^^^^^^^^^^^^^^^^^ +>1 : 1 +> : ^ + + yield x; +>yield x : any +>x : number +> : ^^^^^^ + + return true; +>true : true +> : ^^^^ + + x = "foo"; +>x = "foo" : "foo" +> : ^^^^^ +>x : string | number | boolean +> : ^^^^^^^^^^^^^^^^^^^^^^^^^ +>"foo" : "foo" +> : ^^^^^ + + yield x; +>yield x : any +>x : string | number | boolean +> : ^^^^^^^^^^^^^^^^^^^^^^^^^ +} + diff --git a/tests/cases/compiler/unreachableReturnStatementsVsInferredReturnTypes.ts b/tests/cases/compiler/unreachableReturnStatementsVsInferredReturnTypes.ts index 2b81acb2d4fd8..d2a6f6a7e54b3 100644 --- a/tests/cases/compiler/unreachableReturnStatementsVsInferredReturnTypes.ts +++ b/tests/cases/compiler/unreachableReturnStatementsVsInferredReturnTypes.ts @@ -1,4 +1,5 @@ // @strict: true +// @allowUnreachableCode: true, false // @declaration: true export function g() { diff --git a/tests/cases/compiler/unreachableYieldExpressionsVsInferredYieldTypes.ts b/tests/cases/compiler/unreachableYieldExpressionsVsInferredYieldTypes.ts index acb77677c36b4..1b99e5eb96486 100644 --- a/tests/cases/compiler/unreachableYieldExpressionsVsInferredYieldTypes.ts +++ b/tests/cases/compiler/unreachableYieldExpressionsVsInferredYieldTypes.ts @@ -1,6 +1,7 @@ // @strict: true // @target: esnext // @lib: esnext +// @allowUnreachableCode: true, false // @declaration: true export function* g() { From 341a04b988e849ae82e4cd7f3b70c713830791bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Thu, 13 Nov 2025 09:52:41 +0100 Subject: [PATCH 3/9] add asserts-based tests --- ...pes(allowunreachablecode=false).errors.txt | 34 +++++++- ...ReturnTypes(allowunreachablecode=false).js | 41 ++++++++++ ...nTypes(allowunreachablecode=false).symbols | 43 ++++++++++ ...urnTypes(allowunreachablecode=false).types | 75 +++++++++++++++++ ...ypes(allowunreachablecode=true).errors.txt | 52 ++++++++++++ ...dReturnTypes(allowunreachablecode=true).js | 41 ++++++++++ ...rnTypes(allowunreachablecode=true).symbols | 43 ++++++++++ ...turnTypes(allowunreachablecode=true).types | 81 +++++++++++++++++++ ...leReturnStatementsVsInferredReturnTypes.ts | 20 +++++ 9 files changed, 429 insertions(+), 1 deletion(-) create mode 100644 tests/baselines/reference/unreachableReturnStatementsVsInferredReturnTypes(allowunreachablecode=true).errors.txt diff --git a/tests/baselines/reference/unreachableReturnStatementsVsInferredReturnTypes(allowunreachablecode=false).errors.txt b/tests/baselines/reference/unreachableReturnStatementsVsInferredReturnTypes(allowunreachablecode=false).errors.txt index 757995ee91288..320826cd7634d 100644 --- a/tests/baselines/reference/unreachableReturnStatementsVsInferredReturnTypes(allowunreachablecode=false).errors.txt +++ b/tests/baselines/reference/unreachableReturnStatementsVsInferredReturnTypes(allowunreachablecode=false).errors.txt @@ -1,9 +1,13 @@ unreachableReturnStatementsVsInferredReturnTypes.ts(5,3): error TS7027: Unreachable code detected. unreachableReturnStatementsVsInferredReturnTypes.ts(10,3): error TS7027: Unreachable code detected. unreachableReturnStatementsVsInferredReturnTypes.ts(20,3): error TS7027: Unreachable code detected. +unreachableReturnStatementsVsInferredReturnTypes.ts(30,3): error TS7027: Unreachable code detected. +unreachableReturnStatementsVsInferredReturnTypes.ts(34,7): error TS7034: Variable 'x' implicitly has type 'any' in some locations where its type cannot be determined. +unreachableReturnStatementsVsInferredReturnTypes.ts(38,5): error TS7027: Unreachable code detected. +unreachableReturnStatementsVsInferredReturnTypes.ts(38,12): error TS7005: Variable 'x' implicitly has an 'any' type. -==== unreachableReturnStatementsVsInferredReturnTypes.ts (3 errors) ==== +==== unreachableReturnStatementsVsInferredReturnTypes.ts (7 errors) ==== export function g() { let x; x = 1; @@ -33,4 +37,32 @@ unreachableReturnStatementsVsInferredReturnTypes.ts(20,3): error TS7027: Unreach ~~~~~~~~~~~ !!! error TS7027: Unreachable code detected. } + + function throws(): never { + throw new Error(); + } + + export function foo() { + throws(); + return 42; + ~~~~~~ +!!! error TS7027: Unreachable code detected. + } + + export function bar() { + var x; + ~ +!!! error TS7034: Variable 'x' implicitly has type 'any' in some locations where its type cannot be determined. + x = 1; + if (Math.random()) { + throws(); + return x; + ~~~~~~ +!!! error TS7027: Unreachable code detected. + ~ +!!! error TS7005: Variable 'x' implicitly has an 'any' type. + } + x = 2; + return x; + } \ No newline at end of file diff --git a/tests/baselines/reference/unreachableReturnStatementsVsInferredReturnTypes(allowunreachablecode=false).js b/tests/baselines/reference/unreachableReturnStatementsVsInferredReturnTypes(allowunreachablecode=false).js index f8a3582239eee..81b2d290fb37c 100644 --- a/tests/baselines/reference/unreachableReturnStatementsVsInferredReturnTypes(allowunreachablecode=false).js +++ b/tests/baselines/reference/unreachableReturnStatementsVsInferredReturnTypes(allowunreachablecode=false).js @@ -23,6 +23,26 @@ export function i() { x = "foo"; return x; } + +function throws(): never { + throw new Error(); +} + +export function foo() { + throws(); + return 42; +} + +export function bar() { + var x; + x = 1; + if (Math.random()) { + throws(); + return x; + } + x = 2; + return x; +} //// [unreachableReturnStatementsVsInferredReturnTypes.js] @@ -31,6 +51,8 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.g = g; exports.h = h; exports.i = i; +exports.foo = foo; +exports.bar = bar; function g() { var x; x = 1; @@ -50,9 +72,28 @@ function i() { x = "foo"; return x; } +function throws() { + throw new Error(); +} +function foo() { + throws(); + return 42; +} +function bar() { + var x; + x = 1; + if (Math.random()) { + throws(); + return x; + } + x = 2; + return x; +} //// [unreachableReturnStatementsVsInferredReturnTypes.d.ts] export declare function g(): number; export declare function h(): number; export declare function i(): number; +export declare function foo(): number; +export declare function bar(): any; diff --git a/tests/baselines/reference/unreachableReturnStatementsVsInferredReturnTypes(allowunreachablecode=false).symbols b/tests/baselines/reference/unreachableReturnStatementsVsInferredReturnTypes(allowunreachablecode=false).symbols index 6c266c9d37c72..dfcf144a267cc 100644 --- a/tests/baselines/reference/unreachableReturnStatementsVsInferredReturnTypes(allowunreachablecode=false).symbols +++ b/tests/baselines/reference/unreachableReturnStatementsVsInferredReturnTypes(allowunreachablecode=false).symbols @@ -50,3 +50,46 @@ export function i() { >x : Symbol(x, Decl(unreachableReturnStatementsVsInferredReturnTypes.ts, 15, 5)) } +function throws(): never { +>throws : Symbol(throws, Decl(unreachableReturnStatementsVsInferredReturnTypes.ts, 21, 1)) + + throw new Error(); +>Error : Symbol(Error, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +} + +export function foo() { +>foo : Symbol(foo, Decl(unreachableReturnStatementsVsInferredReturnTypes.ts, 25, 1)) + + throws(); +>throws : Symbol(throws, Decl(unreachableReturnStatementsVsInferredReturnTypes.ts, 21, 1)) + + return 42; +} + +export function bar() { +>bar : Symbol(bar, Decl(unreachableReturnStatementsVsInferredReturnTypes.ts, 30, 1)) + + var x; +>x : Symbol(x, Decl(unreachableReturnStatementsVsInferredReturnTypes.ts, 33, 5)) + + x = 1; +>x : Symbol(x, Decl(unreachableReturnStatementsVsInferredReturnTypes.ts, 33, 5)) + + if (Math.random()) { +>Math.random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --)) +>Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --)) +>random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --)) + + throws(); +>throws : Symbol(throws, Decl(unreachableReturnStatementsVsInferredReturnTypes.ts, 21, 1)) + + return x; +>x : Symbol(x, Decl(unreachableReturnStatementsVsInferredReturnTypes.ts, 33, 5)) + } + x = 2; +>x : Symbol(x, Decl(unreachableReturnStatementsVsInferredReturnTypes.ts, 33, 5)) + + return x; +>x : Symbol(x, Decl(unreachableReturnStatementsVsInferredReturnTypes.ts, 33, 5)) +} + diff --git a/tests/baselines/reference/unreachableReturnStatementsVsInferredReturnTypes(allowunreachablecode=false).types b/tests/baselines/reference/unreachableReturnStatementsVsInferredReturnTypes(allowunreachablecode=false).types index 02aa79cd53afd..daeddfa66bd8f 100644 --- a/tests/baselines/reference/unreachableReturnStatementsVsInferredReturnTypes(allowunreachablecode=false).types +++ b/tests/baselines/reference/unreachableReturnStatementsVsInferredReturnTypes(allowunreachablecode=false).types @@ -84,3 +84,78 @@ export function i() { > : ^^^^^^^^^^^^^^^^^^^^^^^^^ } +function throws(): never { +>throws : () => never +> : ^^^^^^ + + throw new Error(); +>new Error() : Error +> : ^^^^^ +>Error : ErrorConstructor +> : ^^^^^^^^^^^^^^^^ +} + +export function foo() { +>foo : () => number +> : ^^^^^^^^^^^^ + + throws(); +>throws() : never +> : ^^^^^ +>throws : () => never +> : ^^^^^^ + + return 42; +>42 : 42 +> : ^^ +} + +export function bar() { +>bar : () => any +> : ^^^^^^^^^ + + var x; +>x : any +> : ^^^ + + x = 1; +>x = 1 : 1 +> : ^ +>x : any +> : ^^^ +>1 : 1 +> : ^ + + if (Math.random()) { +>Math.random() : number +> : ^^^^^^ +>Math.random : () => number +> : ^^^^^^ +>Math : Math +> : ^^^^ +>random : () => number +> : ^^^^^^ + + throws(); +>throws() : never +> : ^^^^^ +>throws : () => never +> : ^^^^^^ + + return x; +>x : any +> : ^^^ + } + x = 2; +>x = 2 : 2 +> : ^ +>x : any +> : ^^^ +>2 : 2 +> : ^ + + return x; +>x : number +> : ^^^^^^ +} + diff --git a/tests/baselines/reference/unreachableReturnStatementsVsInferredReturnTypes(allowunreachablecode=true).errors.txt b/tests/baselines/reference/unreachableReturnStatementsVsInferredReturnTypes(allowunreachablecode=true).errors.txt new file mode 100644 index 0000000000000..2528f611d115c --- /dev/null +++ b/tests/baselines/reference/unreachableReturnStatementsVsInferredReturnTypes(allowunreachablecode=true).errors.txt @@ -0,0 +1,52 @@ +unreachableReturnStatementsVsInferredReturnTypes.ts(34,7): error TS7034: Variable 'x' implicitly has type 'any' in some locations where its type cannot be determined. +unreachableReturnStatementsVsInferredReturnTypes.ts(38,12): error TS7005: Variable 'x' implicitly has an 'any' type. + + +==== unreachableReturnStatementsVsInferredReturnTypes.ts (2 errors) ==== + export function g() { + let x; + x = 1; + return x; + return x; + } + + export function h() { + return 1; + let y; + y = 1; + return y; + } + + export function i() { + let x: string | number | boolean; + x = 1; + return x; + + x = "foo"; + return x; + } + + function throws(): never { + throw new Error(); + } + + export function foo() { + throws(); + return 42; + } + + export function bar() { + var x; + ~ +!!! error TS7034: Variable 'x' implicitly has type 'any' in some locations where its type cannot be determined. + x = 1; + if (Math.random()) { + throws(); + return x; + ~ +!!! error TS7005: Variable 'x' implicitly has an 'any' type. + } + x = 2; + return x; + } + \ No newline at end of file diff --git a/tests/baselines/reference/unreachableReturnStatementsVsInferredReturnTypes(allowunreachablecode=true).js b/tests/baselines/reference/unreachableReturnStatementsVsInferredReturnTypes(allowunreachablecode=true).js index f8a3582239eee..81b2d290fb37c 100644 --- a/tests/baselines/reference/unreachableReturnStatementsVsInferredReturnTypes(allowunreachablecode=true).js +++ b/tests/baselines/reference/unreachableReturnStatementsVsInferredReturnTypes(allowunreachablecode=true).js @@ -23,6 +23,26 @@ export function i() { x = "foo"; return x; } + +function throws(): never { + throw new Error(); +} + +export function foo() { + throws(); + return 42; +} + +export function bar() { + var x; + x = 1; + if (Math.random()) { + throws(); + return x; + } + x = 2; + return x; +} //// [unreachableReturnStatementsVsInferredReturnTypes.js] @@ -31,6 +51,8 @@ Object.defineProperty(exports, "__esModule", { value: true }); exports.g = g; exports.h = h; exports.i = i; +exports.foo = foo; +exports.bar = bar; function g() { var x; x = 1; @@ -50,9 +72,28 @@ function i() { x = "foo"; return x; } +function throws() { + throw new Error(); +} +function foo() { + throws(); + return 42; +} +function bar() { + var x; + x = 1; + if (Math.random()) { + throws(); + return x; + } + x = 2; + return x; +} //// [unreachableReturnStatementsVsInferredReturnTypes.d.ts] export declare function g(): number; export declare function h(): number; export declare function i(): number; +export declare function foo(): number; +export declare function bar(): any; diff --git a/tests/baselines/reference/unreachableReturnStatementsVsInferredReturnTypes(allowunreachablecode=true).symbols b/tests/baselines/reference/unreachableReturnStatementsVsInferredReturnTypes(allowunreachablecode=true).symbols index 6c266c9d37c72..dfcf144a267cc 100644 --- a/tests/baselines/reference/unreachableReturnStatementsVsInferredReturnTypes(allowunreachablecode=true).symbols +++ b/tests/baselines/reference/unreachableReturnStatementsVsInferredReturnTypes(allowunreachablecode=true).symbols @@ -50,3 +50,46 @@ export function i() { >x : Symbol(x, Decl(unreachableReturnStatementsVsInferredReturnTypes.ts, 15, 5)) } +function throws(): never { +>throws : Symbol(throws, Decl(unreachableReturnStatementsVsInferredReturnTypes.ts, 21, 1)) + + throw new Error(); +>Error : Symbol(Error, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +} + +export function foo() { +>foo : Symbol(foo, Decl(unreachableReturnStatementsVsInferredReturnTypes.ts, 25, 1)) + + throws(); +>throws : Symbol(throws, Decl(unreachableReturnStatementsVsInferredReturnTypes.ts, 21, 1)) + + return 42; +} + +export function bar() { +>bar : Symbol(bar, Decl(unreachableReturnStatementsVsInferredReturnTypes.ts, 30, 1)) + + var x; +>x : Symbol(x, Decl(unreachableReturnStatementsVsInferredReturnTypes.ts, 33, 5)) + + x = 1; +>x : Symbol(x, Decl(unreachableReturnStatementsVsInferredReturnTypes.ts, 33, 5)) + + if (Math.random()) { +>Math.random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --)) +>Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --)) +>random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --)) + + throws(); +>throws : Symbol(throws, Decl(unreachableReturnStatementsVsInferredReturnTypes.ts, 21, 1)) + + return x; +>x : Symbol(x, Decl(unreachableReturnStatementsVsInferredReturnTypes.ts, 33, 5)) + } + x = 2; +>x : Symbol(x, Decl(unreachableReturnStatementsVsInferredReturnTypes.ts, 33, 5)) + + return x; +>x : Symbol(x, Decl(unreachableReturnStatementsVsInferredReturnTypes.ts, 33, 5)) +} + diff --git a/tests/baselines/reference/unreachableReturnStatementsVsInferredReturnTypes(allowunreachablecode=true).types b/tests/baselines/reference/unreachableReturnStatementsVsInferredReturnTypes(allowunreachablecode=true).types index cecce9655a910..daeddfa66bd8f 100644 --- a/tests/baselines/reference/unreachableReturnStatementsVsInferredReturnTypes(allowunreachablecode=true).types +++ b/tests/baselines/reference/unreachableReturnStatementsVsInferredReturnTypes(allowunreachablecode=true).types @@ -7,11 +7,13 @@ export function g() { let x; >x : any +> : ^^^ x = 1; >x = 1 : 1 > : ^ >x : any +> : ^^^ >1 : 1 > : ^ @@ -21,6 +23,7 @@ export function g() { return x; >x : any +> : ^^^ } export function h() { @@ -33,16 +36,19 @@ export function h() { let y; >y : any +> : ^^^ y = 1; >y = 1 : 1 > : ^ >y : any +> : ^^^ >1 : 1 > : ^ return y; >y : any +> : ^^^ } export function i() { @@ -78,3 +84,78 @@ export function i() { > : ^^^^^^^^^^^^^^^^^^^^^^^^^ } +function throws(): never { +>throws : () => never +> : ^^^^^^ + + throw new Error(); +>new Error() : Error +> : ^^^^^ +>Error : ErrorConstructor +> : ^^^^^^^^^^^^^^^^ +} + +export function foo() { +>foo : () => number +> : ^^^^^^^^^^^^ + + throws(); +>throws() : never +> : ^^^^^ +>throws : () => never +> : ^^^^^^ + + return 42; +>42 : 42 +> : ^^ +} + +export function bar() { +>bar : () => any +> : ^^^^^^^^^ + + var x; +>x : any +> : ^^^ + + x = 1; +>x = 1 : 1 +> : ^ +>x : any +> : ^^^ +>1 : 1 +> : ^ + + if (Math.random()) { +>Math.random() : number +> : ^^^^^^ +>Math.random : () => number +> : ^^^^^^ +>Math : Math +> : ^^^^ +>random : () => number +> : ^^^^^^ + + throws(); +>throws() : never +> : ^^^^^ +>throws : () => never +> : ^^^^^^ + + return x; +>x : any +> : ^^^ + } + x = 2; +>x = 2 : 2 +> : ^ +>x : any +> : ^^^ +>2 : 2 +> : ^ + + return x; +>x : number +> : ^^^^^^ +} + diff --git a/tests/cases/compiler/unreachableReturnStatementsVsInferredReturnTypes.ts b/tests/cases/compiler/unreachableReturnStatementsVsInferredReturnTypes.ts index d2a6f6a7e54b3..c818532bbb6c6 100644 --- a/tests/cases/compiler/unreachableReturnStatementsVsInferredReturnTypes.ts +++ b/tests/cases/compiler/unreachableReturnStatementsVsInferredReturnTypes.ts @@ -24,3 +24,23 @@ export function i() { x = "foo"; return x; } + +function throws(): never { + throw new Error(); +} + +export function foo() { + throws(); + return 42; +} + +export function bar() { + var x; + x = 1; + if (Math.random()) { + throws(); + return x; + } + x = 2; + return x; +} From 74a1c0688102a998d2481e87c3ef42e1f21b524b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Thu, 13 Nov 2025 09:53:52 +0100 Subject: [PATCH 4/9] use `isReachableFlowNode` check --- src/compiler/checker.ts | 4 ++-- ...ementsVsInferredReturnTypes(allowunreachablecode=false).js | 2 +- ...ntsVsInferredReturnTypes(allowunreachablecode=false).types | 4 ++-- ...tementsVsInferredReturnTypes(allowunreachablecode=true).js | 2 +- ...entsVsInferredReturnTypes(allowunreachablecode=true).types | 4 ++-- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 6ba0cfa0548a4..3a3011273446c 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -39214,7 +39214,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { const isAsync = (getFunctionFlags(func) & FunctionFlags.Async) !== 0; forEachYieldExpression(func.body as Block, yieldExpression => { const statement = findAncestor(yieldExpression, isStatement)!; - if (canHaveFlowNode(statement) && !statement.flowNode) { + if (canHaveFlowNode(statement) && (!statement.flowNode || !isReachableFlowNode(statement.flowNode))) { return; } let yieldExpressionType = yieldExpression.expression ? checkExpression(yieldExpression.expression, checkMode) : undefinedWideningType; @@ -39336,7 +39336,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return; } - if (!returnStatement.flowNode) { + if (!returnStatement.flowNode || !isReachableFlowNode(returnStatement.flowNode)) { return; } diff --git a/tests/baselines/reference/unreachableReturnStatementsVsInferredReturnTypes(allowunreachablecode=false).js b/tests/baselines/reference/unreachableReturnStatementsVsInferredReturnTypes(allowunreachablecode=false).js index 81b2d290fb37c..53c8be4627386 100644 --- a/tests/baselines/reference/unreachableReturnStatementsVsInferredReturnTypes(allowunreachablecode=false).js +++ b/tests/baselines/reference/unreachableReturnStatementsVsInferredReturnTypes(allowunreachablecode=false).js @@ -96,4 +96,4 @@ export declare function g(): number; export declare function h(): number; export declare function i(): number; export declare function foo(): number; -export declare function bar(): any; +export declare function bar(): number; diff --git a/tests/baselines/reference/unreachableReturnStatementsVsInferredReturnTypes(allowunreachablecode=false).types b/tests/baselines/reference/unreachableReturnStatementsVsInferredReturnTypes(allowunreachablecode=false).types index daeddfa66bd8f..5f93e1b584777 100644 --- a/tests/baselines/reference/unreachableReturnStatementsVsInferredReturnTypes(allowunreachablecode=false).types +++ b/tests/baselines/reference/unreachableReturnStatementsVsInferredReturnTypes(allowunreachablecode=false).types @@ -111,8 +111,8 @@ export function foo() { } export function bar() { ->bar : () => any -> : ^^^^^^^^^ +>bar : () => number +> : ^^^^^^^^^^^^ var x; >x : any diff --git a/tests/baselines/reference/unreachableReturnStatementsVsInferredReturnTypes(allowunreachablecode=true).js b/tests/baselines/reference/unreachableReturnStatementsVsInferredReturnTypes(allowunreachablecode=true).js index 81b2d290fb37c..53c8be4627386 100644 --- a/tests/baselines/reference/unreachableReturnStatementsVsInferredReturnTypes(allowunreachablecode=true).js +++ b/tests/baselines/reference/unreachableReturnStatementsVsInferredReturnTypes(allowunreachablecode=true).js @@ -96,4 +96,4 @@ export declare function g(): number; export declare function h(): number; export declare function i(): number; export declare function foo(): number; -export declare function bar(): any; +export declare function bar(): number; diff --git a/tests/baselines/reference/unreachableReturnStatementsVsInferredReturnTypes(allowunreachablecode=true).types b/tests/baselines/reference/unreachableReturnStatementsVsInferredReturnTypes(allowunreachablecode=true).types index daeddfa66bd8f..5f93e1b584777 100644 --- a/tests/baselines/reference/unreachableReturnStatementsVsInferredReturnTypes(allowunreachablecode=true).types +++ b/tests/baselines/reference/unreachableReturnStatementsVsInferredReturnTypes(allowunreachablecode=true).types @@ -111,8 +111,8 @@ export function foo() { } export function bar() { ->bar : () => any -> : ^^^^^^^^^ +>bar : () => number +> : ^^^^^^^^^^^^ var x; >x : any From 22d5da72b673706bde325d718f68ff7079cb2993 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Thu, 13 Nov 2025 10:45:14 +0100 Subject: [PATCH 5/9] fix `typeFromSingleReturnExpression` --- src/compiler/checker.ts | 8 ++ src/compiler/expressionToTypeNode.ts | 2 +- src/compiler/types.ts | 1 + ...ReturnTypes(allowunreachablecode=false).js | 2 +- ...urnTypes(allowunreachablecode=false).types | 4 +- ...dReturnTypes(allowunreachablecode=true).js | 2 +- ...turnTypes(allowunreachablecode=true).types | 4 +- ...pes(allowunreachablecode=false).errors.txt | 35 ++++++- ...dYieldTypes(allowunreachablecode=false).js | 40 +++++++- ...dTypes(allowunreachablecode=false).symbols | 42 ++++++++ ...eldTypes(allowunreachablecode=false).types | 80 ++++++++++++++++ ...ypes(allowunreachablecode=true).errors.txt | 61 ++++++++++++ ...edYieldTypes(allowunreachablecode=true).js | 40 +++++++- ...ldTypes(allowunreachablecode=true).symbols | 42 ++++++++ ...ieldTypes(allowunreachablecode=true).types | 96 +++++++++++++++++++ ...bleYieldExpressionsVsInferredYieldTypes.ts | 20 ++++ 16 files changed, 468 insertions(+), 11 deletions(-) create mode 100644 tests/baselines/reference/unreachableYieldExpressionsVsInferredYieldTypes(allowunreachablecode=true).errors.txt diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 3a3011273446c..3f00ae9a2c210 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -6402,6 +6402,14 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } return !!annotationType && typeNodeIsEquivalentToType(node, type, annotationType) && existingTypeNodeIsNotReferenceOrIsReferenceWithCompatibleTypeArgumentCount(existing, type); }, + + isPossiblyReachable(expression) { + const ancestor = findAncestor(expression, canHaveFlowNode); + if (!ancestor) { + return true; + } + return !!ancestor.flowNode && isReachableFlowNode(ancestor.flowNode); + } }; return { syntacticBuilderResolver, diff --git a/src/compiler/expressionToTypeNode.ts b/src/compiler/expressionToTypeNode.ts index 0df3a399b8f14..f6bdbc96843a0 100644 --- a/src/compiler/expressionToTypeNode.ts +++ b/src/compiler/expressionToTypeNode.ts @@ -1305,7 +1305,7 @@ export function createSyntacticTypeNodeBuilder( candidateExpr = body; } } - if (candidateExpr) { + if (candidateExpr && resolver.isPossiblyReachable(candidateExpr)) { if (isContextuallyTyped(candidateExpr)) { const type = isJSDocTypeAssertion(candidateExpr) ? getJSDocTypeAssertionType(candidateExpr) : isAsExpression(candidateExpr) || isTypeAssertionExpression(candidateExpr) ? candidateExpr.type : diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 626bcecf9d34a..000c17c284871 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -10637,6 +10637,7 @@ export interface SyntacticTypeNodeBuilderResolver { markError(): void; hadError(): boolean; }; + isPossiblyReachable(expression: Expression): boolean; } /** @internal */ diff --git a/tests/baselines/reference/unreachableReturnStatementsVsInferredReturnTypes(allowunreachablecode=false).js b/tests/baselines/reference/unreachableReturnStatementsVsInferredReturnTypes(allowunreachablecode=false).js index 53c8be4627386..8d24a07692d5f 100644 --- a/tests/baselines/reference/unreachableReturnStatementsVsInferredReturnTypes(allowunreachablecode=false).js +++ b/tests/baselines/reference/unreachableReturnStatementsVsInferredReturnTypes(allowunreachablecode=false).js @@ -95,5 +95,5 @@ function bar() { export declare function g(): number; export declare function h(): number; export declare function i(): number; -export declare function foo(): number; +export declare function foo(): void; export declare function bar(): number; diff --git a/tests/baselines/reference/unreachableReturnStatementsVsInferredReturnTypes(allowunreachablecode=false).types b/tests/baselines/reference/unreachableReturnStatementsVsInferredReturnTypes(allowunreachablecode=false).types index 5f93e1b584777..22374e6b57e93 100644 --- a/tests/baselines/reference/unreachableReturnStatementsVsInferredReturnTypes(allowunreachablecode=false).types +++ b/tests/baselines/reference/unreachableReturnStatementsVsInferredReturnTypes(allowunreachablecode=false).types @@ -96,8 +96,8 @@ function throws(): never { } export function foo() { ->foo : () => number -> : ^^^^^^^^^^^^ +>foo : () => void +> : ^^^^^^^^^^ throws(); >throws() : never diff --git a/tests/baselines/reference/unreachableReturnStatementsVsInferredReturnTypes(allowunreachablecode=true).js b/tests/baselines/reference/unreachableReturnStatementsVsInferredReturnTypes(allowunreachablecode=true).js index 53c8be4627386..8d24a07692d5f 100644 --- a/tests/baselines/reference/unreachableReturnStatementsVsInferredReturnTypes(allowunreachablecode=true).js +++ b/tests/baselines/reference/unreachableReturnStatementsVsInferredReturnTypes(allowunreachablecode=true).js @@ -95,5 +95,5 @@ function bar() { export declare function g(): number; export declare function h(): number; export declare function i(): number; -export declare function foo(): number; +export declare function foo(): void; export declare function bar(): number; diff --git a/tests/baselines/reference/unreachableReturnStatementsVsInferredReturnTypes(allowunreachablecode=true).types b/tests/baselines/reference/unreachableReturnStatementsVsInferredReturnTypes(allowunreachablecode=true).types index 5f93e1b584777..22374e6b57e93 100644 --- a/tests/baselines/reference/unreachableReturnStatementsVsInferredReturnTypes(allowunreachablecode=true).types +++ b/tests/baselines/reference/unreachableReturnStatementsVsInferredReturnTypes(allowunreachablecode=true).types @@ -96,8 +96,8 @@ function throws(): never { } export function foo() { ->foo : () => number -> : ^^^^^^^^^^^^ +>foo : () => void +> : ^^^^^^^^^^ throws(); >throws() : never diff --git a/tests/baselines/reference/unreachableYieldExpressionsVsInferredYieldTypes(allowunreachablecode=false).errors.txt b/tests/baselines/reference/unreachableYieldExpressionsVsInferredYieldTypes(allowunreachablecode=false).errors.txt index 271ac38c5c55d..bf1cf177d0ba6 100644 --- a/tests/baselines/reference/unreachableYieldExpressionsVsInferredYieldTypes(allowunreachablecode=false).errors.txt +++ b/tests/baselines/reference/unreachableYieldExpressionsVsInferredYieldTypes(allowunreachablecode=false).errors.txt @@ -2,9 +2,13 @@ unreachableYieldExpressionsVsInferredYieldTypes.ts(6,3): error TS7027: Unreachab unreachableYieldExpressionsVsInferredYieldTypes.ts(11,3): error TS7027: Unreachable code detected. unreachableYieldExpressionsVsInferredYieldTypes.ts(19,3): error TS7027: Unreachable code detected. unreachableYieldExpressionsVsInferredYieldTypes.ts(30,3): error TS7027: Unreachable code detected. +unreachableYieldExpressionsVsInferredYieldTypes.ts(40,3): error TS7027: Unreachable code detected. +unreachableYieldExpressionsVsInferredYieldTypes.ts(44,7): error TS7034: Variable 'x' implicitly has type 'any' in some locations where its type cannot be determined. +unreachableYieldExpressionsVsInferredYieldTypes.ts(48,5): error TS7027: Unreachable code detected. +unreachableYieldExpressionsVsInferredYieldTypes.ts(48,11): error TS7005: Variable 'x' implicitly has an 'any' type. -==== unreachableYieldExpressionsVsInferredYieldTypes.ts (4 errors) ==== +==== unreachableYieldExpressionsVsInferredYieldTypes.ts (8 errors) ==== export function* g() { let x; x = 1; @@ -46,4 +50,31 @@ unreachableYieldExpressionsVsInferredYieldTypes.ts(30,3): error TS7027: Unreacha ~~~~~~~~~~ !!! error TS7027: Unreachable code detected. } - \ No newline at end of file + + function throws(): never { + throw new Error(); + } + + export function* foo() { + throws(); + yield 42; + ~~~~~~~~~ +!!! error TS7027: Unreachable code detected. + } + + export function* bar() { + var x; + ~ +!!! error TS7034: Variable 'x' implicitly has type 'any' in some locations where its type cannot be determined. + x = 1; + if (Math.random()) { + throws(); + yield x; + ~~~~~~~~ +!!! error TS7027: Unreachable code detected. + ~ +!!! error TS7005: Variable 'x' implicitly has an 'any' type. + } + x = 2; + yield x; + } \ No newline at end of file diff --git a/tests/baselines/reference/unreachableYieldExpressionsVsInferredYieldTypes(allowunreachablecode=false).js b/tests/baselines/reference/unreachableYieldExpressionsVsInferredYieldTypes(allowunreachablecode=false).js index 81cee1362a574..ac709f04e975b 100644 --- a/tests/baselines/reference/unreachableYieldExpressionsVsInferredYieldTypes(allowunreachablecode=false).js +++ b/tests/baselines/reference/unreachableYieldExpressionsVsInferredYieldTypes(allowunreachablecode=false).js @@ -33,7 +33,26 @@ export function* j() { x = "foo"; yield x; } - + +function throws(): never { + throw new Error(); +} + +export function* foo() { + throws(); + yield 42; +} + +export function* bar() { + var x; + x = 1; + if (Math.random()) { + throws(); + yield x; + } + x = 2; + yield x; +} //// [unreachableYieldExpressionsVsInferredYieldTypes.js] export function* g() { @@ -64,6 +83,23 @@ export function* j() { x = "foo"; yield x; } +function throws() { + throw new Error(); +} +export function* foo() { + throws(); + yield 42; +} +export function* bar() { + var x; + x = 1; + if (Math.random()) { + throws(); + yield x; + } + x = 2; + yield x; +} //// [unreachableYieldExpressionsVsInferredYieldTypes.d.ts] @@ -71,3 +107,5 @@ export declare function g(): Generator; export declare function h(): Generator; export declare function i(): Generator; export declare function j(): Generator; +export declare function foo(): Generator; +export declare function bar(): Generator; diff --git a/tests/baselines/reference/unreachableYieldExpressionsVsInferredYieldTypes(allowunreachablecode=false).symbols b/tests/baselines/reference/unreachableYieldExpressionsVsInferredYieldTypes(allowunreachablecode=false).symbols index c588297e4dd23..6e3974de20717 100644 --- a/tests/baselines/reference/unreachableYieldExpressionsVsInferredYieldTypes(allowunreachablecode=false).symbols +++ b/tests/baselines/reference/unreachableYieldExpressionsVsInferredYieldTypes(allowunreachablecode=false).symbols @@ -68,3 +68,45 @@ export function* j() { >x : Symbol(x, Decl(unreachableYieldExpressionsVsInferredYieldTypes.ts, 24, 5)) } +function throws(): never { +>throws : Symbol(throws, Decl(unreachableYieldExpressionsVsInferredYieldTypes.ts, 31, 1)) + + throw new Error(); +>Error : Symbol(Error, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2022.error.d.ts, --, --)) +} + +export function* foo() { +>foo : Symbol(foo, Decl(unreachableYieldExpressionsVsInferredYieldTypes.ts, 35, 1)) + + throws(); +>throws : Symbol(throws, Decl(unreachableYieldExpressionsVsInferredYieldTypes.ts, 31, 1)) + + yield 42; +} + +export function* bar() { +>bar : Symbol(bar, Decl(unreachableYieldExpressionsVsInferredYieldTypes.ts, 40, 1)) + + var x; +>x : Symbol(x, Decl(unreachableYieldExpressionsVsInferredYieldTypes.ts, 43, 5)) + + x = 1; +>x : Symbol(x, Decl(unreachableYieldExpressionsVsInferredYieldTypes.ts, 43, 5)) + + if (Math.random()) { +>Math.random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --)) +>Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.esnext.float16.d.ts, --, --)) +>random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --)) + + throws(); +>throws : Symbol(throws, Decl(unreachableYieldExpressionsVsInferredYieldTypes.ts, 31, 1)) + + yield x; +>x : Symbol(x, Decl(unreachableYieldExpressionsVsInferredYieldTypes.ts, 43, 5)) + } + x = 2; +>x : Symbol(x, Decl(unreachableYieldExpressionsVsInferredYieldTypes.ts, 43, 5)) + + yield x; +>x : Symbol(x, Decl(unreachableYieldExpressionsVsInferredYieldTypes.ts, 43, 5)) +} diff --git a/tests/baselines/reference/unreachableYieldExpressionsVsInferredYieldTypes(allowunreachablecode=false).types b/tests/baselines/reference/unreachableYieldExpressionsVsInferredYieldTypes(allowunreachablecode=false).types index 20840a3afdefe..531e3eb3e47a3 100644 --- a/tests/baselines/reference/unreachableYieldExpressionsVsInferredYieldTypes(allowunreachablecode=false).types +++ b/tests/baselines/reference/unreachableYieldExpressionsVsInferredYieldTypes(allowunreachablecode=false).types @@ -135,3 +135,83 @@ export function* j() { > : ^^^^^^^^^^^^^^^^^^^^^^^^^ } +function throws(): never { +>throws : () => never +> : ^^^^^^ + + throw new Error(); +>new Error() : Error +> : ^^^^^ +>Error : ErrorConstructor +> : ^^^^^^^^^^^^^^^^ +} + +export function* foo() { +>foo : () => Generator +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + throws(); +>throws() : never +> : ^^^^^ +>throws : () => never +> : ^^^^^^ + + yield 42; +>yield 42 : any +> : ^^^ +>42 : 42 +> : ^^ +} + +export function* bar() { +>bar : () => Generator +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + var x; +>x : any +> : ^^^ + + x = 1; +>x = 1 : 1 +> : ^ +>x : any +> : ^^^ +>1 : 1 +> : ^ + + if (Math.random()) { +>Math.random() : number +> : ^^^^^^ +>Math.random : () => number +> : ^^^^^^ +>Math : Math +> : ^^^^ +>random : () => number +> : ^^^^^^ + + throws(); +>throws() : never +> : ^^^^^ +>throws : () => never +> : ^^^^^^ + + yield x; +>yield x : any +> : ^^^ +>x : any +> : ^^^ + } + x = 2; +>x = 2 : 2 +> : ^ +>x : any +> : ^^^ +>2 : 2 +> : ^ + + yield x; +>yield x : any +> : ^^^ +>x : number +> : ^^^^^^ +} diff --git a/tests/baselines/reference/unreachableYieldExpressionsVsInferredYieldTypes(allowunreachablecode=true).errors.txt b/tests/baselines/reference/unreachableYieldExpressionsVsInferredYieldTypes(allowunreachablecode=true).errors.txt new file mode 100644 index 0000000000000..396dc10f12174 --- /dev/null +++ b/tests/baselines/reference/unreachableYieldExpressionsVsInferredYieldTypes(allowunreachablecode=true).errors.txt @@ -0,0 +1,61 @@ +unreachableYieldExpressionsVsInferredYieldTypes.ts(44,7): error TS7034: Variable 'x' implicitly has type 'any' in some locations where its type cannot be determined. +unreachableYieldExpressionsVsInferredYieldTypes.ts(48,11): error TS7005: Variable 'x' implicitly has an 'any' type. + + +==== unreachableYieldExpressionsVsInferredYieldTypes.ts (2 errors) ==== + export function* g() { + let x; + x = 1; + yield x; + return 'foo'; + yield x; + } + + export function* h() { + return 'foo'; + let y; + y = 1; + yield y; + } + + export function* i() { + yield true; + return 'foo'; + let y; + y = 1; + yield y; + } + + export function* j() { + let x: string | number | boolean; + x = 1; + yield x; + return true; + + x = "foo"; + yield x; + } + + function throws(): never { + throw new Error(); + } + + export function* foo() { + throws(); + yield 42; + } + + export function* bar() { + var x; + ~ +!!! error TS7034: Variable 'x' implicitly has type 'any' in some locations where its type cannot be determined. + x = 1; + if (Math.random()) { + throws(); + yield x; + ~ +!!! error TS7005: Variable 'x' implicitly has an 'any' type. + } + x = 2; + yield x; + } \ No newline at end of file diff --git a/tests/baselines/reference/unreachableYieldExpressionsVsInferredYieldTypes(allowunreachablecode=true).js b/tests/baselines/reference/unreachableYieldExpressionsVsInferredYieldTypes(allowunreachablecode=true).js index 58c666feac74a..a31d191622a3e 100644 --- a/tests/baselines/reference/unreachableYieldExpressionsVsInferredYieldTypes(allowunreachablecode=true).js +++ b/tests/baselines/reference/unreachableYieldExpressionsVsInferredYieldTypes(allowunreachablecode=true).js @@ -33,7 +33,26 @@ export function* j() { x = "foo"; yield x; } - + +function throws(): never { + throw new Error(); +} + +export function* foo() { + throws(); + yield 42; +} + +export function* bar() { + var x; + x = 1; + if (Math.random()) { + throws(); + yield x; + } + x = 2; + yield x; +} //// [unreachableYieldExpressionsVsInferredYieldTypes.js] export function* g() { @@ -64,6 +83,23 @@ export function* j() { x = "foo"; yield x; } +function throws() { + throw new Error(); +} +export function* foo() { + throws(); + yield 42; +} +export function* bar() { + var x; + x = 1; + if (Math.random()) { + throws(); + yield x; + } + x = 2; + yield x; +} //// [unreachableYieldExpressionsVsInferredYieldTypes.d.ts] @@ -71,3 +107,5 @@ export declare function g(): Generator; export declare function h(): Generator; export declare function i(): Generator; export declare function j(): Generator; +export declare function foo(): Generator; +export declare function bar(): Generator; diff --git a/tests/baselines/reference/unreachableYieldExpressionsVsInferredYieldTypes(allowunreachablecode=true).symbols b/tests/baselines/reference/unreachableYieldExpressionsVsInferredYieldTypes(allowunreachablecode=true).symbols index c588297e4dd23..6e3974de20717 100644 --- a/tests/baselines/reference/unreachableYieldExpressionsVsInferredYieldTypes(allowunreachablecode=true).symbols +++ b/tests/baselines/reference/unreachableYieldExpressionsVsInferredYieldTypes(allowunreachablecode=true).symbols @@ -68,3 +68,45 @@ export function* j() { >x : Symbol(x, Decl(unreachableYieldExpressionsVsInferredYieldTypes.ts, 24, 5)) } +function throws(): never { +>throws : Symbol(throws, Decl(unreachableYieldExpressionsVsInferredYieldTypes.ts, 31, 1)) + + throw new Error(); +>Error : Symbol(Error, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2022.error.d.ts, --, --)) +} + +export function* foo() { +>foo : Symbol(foo, Decl(unreachableYieldExpressionsVsInferredYieldTypes.ts, 35, 1)) + + throws(); +>throws : Symbol(throws, Decl(unreachableYieldExpressionsVsInferredYieldTypes.ts, 31, 1)) + + yield 42; +} + +export function* bar() { +>bar : Symbol(bar, Decl(unreachableYieldExpressionsVsInferredYieldTypes.ts, 40, 1)) + + var x; +>x : Symbol(x, Decl(unreachableYieldExpressionsVsInferredYieldTypes.ts, 43, 5)) + + x = 1; +>x : Symbol(x, Decl(unreachableYieldExpressionsVsInferredYieldTypes.ts, 43, 5)) + + if (Math.random()) { +>Math.random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --)) +>Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.esnext.float16.d.ts, --, --)) +>random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --)) + + throws(); +>throws : Symbol(throws, Decl(unreachableYieldExpressionsVsInferredYieldTypes.ts, 31, 1)) + + yield x; +>x : Symbol(x, Decl(unreachableYieldExpressionsVsInferredYieldTypes.ts, 43, 5)) + } + x = 2; +>x : Symbol(x, Decl(unreachableYieldExpressionsVsInferredYieldTypes.ts, 43, 5)) + + yield x; +>x : Symbol(x, Decl(unreachableYieldExpressionsVsInferredYieldTypes.ts, 43, 5)) +} diff --git a/tests/baselines/reference/unreachableYieldExpressionsVsInferredYieldTypes(allowunreachablecode=true).types b/tests/baselines/reference/unreachableYieldExpressionsVsInferredYieldTypes(allowunreachablecode=true).types index b362748dc2580..2687b2e6b09c7 100644 --- a/tests/baselines/reference/unreachableYieldExpressionsVsInferredYieldTypes(allowunreachablecode=true).types +++ b/tests/baselines/reference/unreachableYieldExpressionsVsInferredYieldTypes(allowunreachablecode=true).types @@ -7,16 +7,19 @@ export function* g() { let x; >x : any +> : ^^^ x = 1; >x = 1 : 1 > : ^ >x : any +> : ^^^ >1 : 1 > : ^ yield x; >yield x : any +> : ^^^ >x : number > : ^^^^^^ @@ -26,7 +29,9 @@ export function* g() { yield x; >yield x : any +> : ^^^ >x : any +> : ^^^ } export function* h() { @@ -39,17 +44,21 @@ export function* h() { let y; >y : any +> : ^^^ y = 1; >y = 1 : 1 > : ^ >y : any +> : ^^^ >1 : 1 > : ^ yield y; >yield y : any +> : ^^^ >y : any +> : ^^^ } export function* i() { @@ -58,6 +67,7 @@ export function* i() { yield true; >yield true : any +> : ^^^ >true : true > : ^^^^ @@ -67,17 +77,21 @@ export function* i() { let y; >y : any +> : ^^^ y = 1; >y = 1 : 1 > : ^ >y : any +> : ^^^ >1 : 1 > : ^ yield y; >yield y : any +> : ^^^ >y : any +> : ^^^ } export function* j() { @@ -98,6 +112,7 @@ export function* j() { yield x; >yield x : any +> : ^^^ >x : number > : ^^^^^^ @@ -115,7 +130,88 @@ export function* j() { yield x; >yield x : any +> : ^^^ >x : string | number | boolean > : ^^^^^^^^^^^^^^^^^^^^^^^^^ } +function throws(): never { +>throws : () => never +> : ^^^^^^ + + throw new Error(); +>new Error() : Error +> : ^^^^^ +>Error : ErrorConstructor +> : ^^^^^^^^^^^^^^^^ +} + +export function* foo() { +>foo : () => Generator +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + throws(); +>throws() : never +> : ^^^^^ +>throws : () => never +> : ^^^^^^ + + yield 42; +>yield 42 : any +> : ^^^ +>42 : 42 +> : ^^ +} + +export function* bar() { +>bar : () => Generator +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + var x; +>x : any +> : ^^^ + + x = 1; +>x = 1 : 1 +> : ^ +>x : any +> : ^^^ +>1 : 1 +> : ^ + + if (Math.random()) { +>Math.random() : number +> : ^^^^^^ +>Math.random : () => number +> : ^^^^^^ +>Math : Math +> : ^^^^ +>random : () => number +> : ^^^^^^ + + throws(); +>throws() : never +> : ^^^^^ +>throws : () => never +> : ^^^^^^ + + yield x; +>yield x : any +> : ^^^ +>x : any +> : ^^^ + } + x = 2; +>x = 2 : 2 +> : ^ +>x : any +> : ^^^ +>2 : 2 +> : ^ + + yield x; +>yield x : any +> : ^^^ +>x : number +> : ^^^^^^ +} diff --git a/tests/cases/compiler/unreachableYieldExpressionsVsInferredYieldTypes.ts b/tests/cases/compiler/unreachableYieldExpressionsVsInferredYieldTypes.ts index 1b99e5eb96486..af500c54b8ed3 100644 --- a/tests/cases/compiler/unreachableYieldExpressionsVsInferredYieldTypes.ts +++ b/tests/cases/compiler/unreachableYieldExpressionsVsInferredYieldTypes.ts @@ -36,3 +36,23 @@ export function* j() { x = "foo"; yield x; } + +function throws(): never { + throw new Error(); +} + +export function* foo() { + throws(); + yield 42; +} + +export function* bar() { + var x; + x = 1; + if (Math.random()) { + throws(); + yield x; + } + x = 2; + yield x; +} \ No newline at end of file From 8835aae4603cabfbe390b6bcbfa18480e6f3437b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Thu, 13 Nov 2025 11:26:31 +0100 Subject: [PATCH 6/9] update baselines --- src/compiler/checker.ts | 6 +- src/server/editorServices.ts | 2 +- .../bestCommonTypeReturnStatement.types | 4 +- ...WithoutReturnTypeAnnotationInference.types | 12 ++-- .../reference/duplicateLocalVariable1.types | 4 +- .../functionImplementationErrors.types | 62 +++++++++---------- .../reference/functionImplementations.types | 20 +++--- ...functionWithMultipleReturnStatements.types | 14 ++--- ...unctionWithMultipleReturnStatements2.types | 24 +++---- .../functionWithNoBestCommonType1.types | 4 +- .../functionWithNoBestCommonType2.types | 8 +-- ...nferredFunctionReturnTypeIsEmptyType.types | 4 +- ...nCaseClauseAfterCaseClauseWithReturn.types | 2 +- tests/baselines/reference/null.types | 8 +-- 14 files changed, 87 insertions(+), 87 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 3f00ae9a2c210..28d28205e842b 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -6402,14 +6402,14 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } return !!annotationType && typeNodeIsEquivalentToType(node, type, annotationType) && existingTypeNodeIsNotReferenceOrIsReferenceWithCompatibleTypeArgumentCount(existing, type); }, - + isPossiblyReachable(expression) { const ancestor = findAncestor(expression, canHaveFlowNode); if (!ancestor) { return true; } - return !!ancestor.flowNode && isReachableFlowNode(ancestor.flowNode); - } + return !!ancestor.flowNode && isReachableFlowNode(ancestor.flowNode); + }, }; return { syntacticBuilderResolver, diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index 23f8d844c9856..3ee35adc46c67 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -3922,7 +3922,7 @@ export class ProjectService { info.sourceFileLike = { get text() { Debug.fail("shouldnt need text"); - return ""; + return "" as any; }, getLineAndCharacterOfPosition: pos => { const lineOffset = info.positionToLineOffset(pos); diff --git a/tests/baselines/reference/bestCommonTypeReturnStatement.types b/tests/baselines/reference/bestCommonTypeReturnStatement.types index 38afacec4b0a8..4d240dca86062 100644 --- a/tests/baselines/reference/bestCommonTypeReturnStatement.types +++ b/tests/baselines/reference/bestCommonTypeReturnStatement.types @@ -15,8 +15,8 @@ interface IPromise { } function f() { ->f : () => IPromise -> : ^^^^^^^^^^^^^^^^^^^ +>f : () => IPromise +> : ^^^^^^^^^^^^^^^^^^^^ if (true) return b(); >true : true diff --git a/tests/baselines/reference/callSignatureWithoutReturnTypeAnnotationInference.types b/tests/baselines/reference/callSignatureWithoutReturnTypeAnnotationInference.types index 5c81cf536b3b4..7f494489d7e11 100644 --- a/tests/baselines/reference/callSignatureWithoutReturnTypeAnnotationInference.types +++ b/tests/baselines/reference/callSignatureWithoutReturnTypeAnnotationInference.types @@ -84,8 +84,8 @@ var r4 = foo4(1); > : ^ function foo5(x) { ->foo5 : (x: any) => 1 | 2 -> : ^ ^^^^^^^^^^^^^^^ +>foo5 : (x: any) => number +> : ^ ^^^^^^^^^^^^^^^^ >x : any if (true) { @@ -105,10 +105,10 @@ function foo5(x) { var r5 = foo5(1); >r5 : number > : ^^^^^^ ->foo5(1) : 1 | 2 -> : ^^^^^ ->foo5 : (x: any) => 1 | 2 -> : ^ ^^^^^^^^^^^^^^^ +>foo5(1) : number +> : ^^^^^^ +>foo5 : (x: any) => number +> : ^ ^^^^^^^^^^^^^^^^ >1 : 1 > : ^ diff --git a/tests/baselines/reference/duplicateLocalVariable1.types b/tests/baselines/reference/duplicateLocalVariable1.types index 30c540696609c..f9d15bf844e0f 100644 --- a/tests/baselines/reference/duplicateLocalVariable1.types +++ b/tests/baselines/reference/duplicateLocalVariable1.types @@ -377,7 +377,7 @@ export var tests: TestRunner = (function () { > : ^^^^^^^^^^^^^^^ >"Test for any error" : "Test for any error" > : ^^^^^^^^^^^^^^^^^^^^ ->function () { throw new Error(); return false; } : () => false +>function () { throw new Error(); return false; } : () => never > : ^^^^^^^^^^^ >new Error() : Error > : ^^^^^ @@ -403,7 +403,7 @@ export var tests: TestRunner = (function () { > : ^^^^^^^^^^^^^^^ >"Test RegEx error message match" : "Test RegEx error message match" > : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ->function () { throw new Error("Should also pass"); return false; } : () => false +>function () { throw new Error("Should also pass"); return false; } : () => never > : ^^^^^^^^^^^ >new Error("Should also pass") : Error > : ^^^^^ diff --git a/tests/baselines/reference/functionImplementationErrors.types b/tests/baselines/reference/functionImplementationErrors.types index 271ed68920ce6..a79142b84ff80 100644 --- a/tests/baselines/reference/functionImplementationErrors.types +++ b/tests/baselines/reference/functionImplementationErrors.types @@ -3,9 +3,9 @@ === functionImplementationErrors.ts === // FunctionExpression with no return type annotation with multiple return statements with unrelated types var f1 = function () { ->f1 : () => "" | 3 +>f1 : () => string > : ^^^^^^^^^^^^ ->function () { return ''; return 3;} : () => "" | 3 +>function () { return ''; return 3;} : () => string > : ^^^^^^^^^^^^ return ''; @@ -18,11 +18,11 @@ var f1 = function () { }; var f2 = function x() { ->f2 : () => "" | 3 +>f2 : () => string > : ^^^^^^^^^^^^ ->function x() { return ''; return 3;} : () => "" | 3 +>function x() { return ''; return 3;} : () => string > : ^^^^^^^^^^^^ ->x : () => "" | 3 +>x : () => string > : ^^^^^^^^^^^^ return ''; @@ -35,9 +35,9 @@ var f2 = function x() { }; var f3 = () => { ->f3 : () => "" | 3 +>f3 : () => string > : ^^^^^^^^^^^^ ->() => { return ''; return 3;} : () => "" | 3 +>() => { return ''; return 3;} : () => string > : ^^^^^^^^^^^^ return ''; @@ -52,10 +52,10 @@ var f3 = () => { // FunctionExpression with no return type annotation with return branch of number[] and other of string[] var f4 = function () { ->f4 : () => string[] | number[] -> : ^^^^^^^^^^^^^^^^^^^^^^^^^ ->function () { if (true) { return ['']; } else { return [1]; }} : () => string[] | number[] -> : ^^^^^^^^^^^^^^^^^^^^^^^^^ +>f4 : () => string[] +> : ^^^^^^^^^^^^^^ +>function () { if (true) { return ['']; } else { return [1]; }} : () => string[] +> : ^^^^^^^^^^^^^^ if (true) { >true : true @@ -165,8 +165,8 @@ class Derived2 extends Base { private n; } > : ^^^ function f8() { ->f8 : () => Derived1 | Derived2 -> : ^^^^^^^^^^^^^^^^^^^^^^^^^ +>f8 : () => Derived1 +> : ^^^^^^^^^^^^^^ return new Derived1(); >new Derived1() : Derived1 @@ -181,10 +181,10 @@ function f8() { > : ^^^^^^^^^^^^^^^ } var f9 = function () { ->f9 : () => Derived1 | Derived2 -> : ^^^^^^^^^^^^^^^^^^^^^^^^^ ->function () { return new Derived1(); return new Derived2();} : () => Derived1 | Derived2 -> : ^^^^^^^^^^^^^^^^^^^^^^^^^ +>f9 : () => Derived1 +> : ^^^^^^^^^^^^^^ +>function () { return new Derived1(); return new Derived2();} : () => Derived1 +> : ^^^^^^^^^^^^^^ return new Derived1(); >new Derived1() : Derived1 @@ -200,10 +200,10 @@ var f9 = function () { }; var f10 = () => { ->f10 : () => Derived1 | Derived2 -> : ^^^^^^^^^^^^^^^^^^^^^^^^^ ->() => { return new Derived1(); return new Derived2();} : () => Derived1 | Derived2 -> : ^^^^^^^^^^^^^^^^^^^^^^^^^ +>f10 : () => Derived1 +> : ^^^^^^^^^^^^^^ +>() => { return new Derived1(); return new Derived2();} : () => Derived1 +> : ^^^^^^^^^^^^^^ return new Derived1(); >new Derived1() : Derived1 @@ -219,8 +219,8 @@ var f10 = () => { }; function f11() { ->f11 : () => Base | AnotherClass -> : ^^^^^^^^^^^^^^^^^^^^^^^^^ +>f11 : () => Base +> : ^^^^^^^^^^ return new Base(); >new Base() : Base @@ -235,10 +235,10 @@ function f11() { > : ^^^^^^^^^^^^^^^^^^^ } var f12 = function () { ->f12 : () => Base | AnotherClass -> : ^^^^^^^^^^^^^^^^^^^^^^^^^ ->function () { return new Base(); return new AnotherClass();} : () => Base | AnotherClass -> : ^^^^^^^^^^^^^^^^^^^^^^^^^ +>f12 : () => Base +> : ^^^^^^^^^^ +>function () { return new Base(); return new AnotherClass();} : () => Base +> : ^^^^^^^^^^ return new Base(); >new Base() : Base @@ -254,10 +254,10 @@ var f12 = function () { }; var f13 = () => { ->f13 : () => Base | AnotherClass -> : ^^^^^^^^^^^^^^^^^^^^^^^^^ ->() => { return new Base(); return new AnotherClass();} : () => Base | AnotherClass -> : ^^^^^^^^^^^^^^^^^^^^^^^^^ +>f13 : () => Base +> : ^^^^^^^^^^ +>() => { return new Base(); return new AnotherClass();} : () => Base +> : ^^^^^^^^^^ return new Base(); >new Base() : Base diff --git a/tests/baselines/reference/functionImplementations.types b/tests/baselines/reference/functionImplementations.types index c07a8e17303ab..cfcf48bb70c11 100644 --- a/tests/baselines/reference/functionImplementations.types +++ b/tests/baselines/reference/functionImplementations.types @@ -233,10 +233,10 @@ var n = function (x: T) { var n = function () { >n : number > : ^^^^^^ ->function () { return 3; return 5;}() : 3 | 5 -> : ^^^^^ ->function () { return 3; return 5;} : () => 3 | 5 -> : ^^^^^^^^^^^ +>function () { return 3; return 5;}() : number +> : ^^^^^^ +>function () { return 3; return 5;} : () => number +> : ^^^^^^^^^^^^ return 3; >3 : 3 @@ -534,8 +534,8 @@ var f10: (x: number) => any = x => { // should be (x: number) => Derived | Deriv > : ^ ^^ ^^^^^ >x : number > : ^^^^^^ ->x => { // should be (x: number) => Derived | Derived1 return new Derived(); return new Derived2();} : (x: number) => Derived | Derived2 -> : ^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>x => { // should be (x: number) => Derived | Derived1 return new Derived(); return new Derived2();} : (x: number) => Derived +> : ^ ^^^^^^^^^^^^^^^^^^^^ >x : number > : ^^^^^^ @@ -556,8 +556,8 @@ var f11: (x: number) => any = x => { // should be (x: number) => Base | AnotherC > : ^ ^^ ^^^^^ >x : number > : ^^^^^^ ->x => { // should be (x: number) => Base | AnotherClass return new Base(); return new AnotherClass();} : (x: number) => Base | AnotherClass -> : ^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>x => { // should be (x: number) => Base | AnotherClass return new Base(); return new AnotherClass();} : (x: number) => Base +> : ^ ^^^^^^^^^^^^^^^^^ >x : number > : ^^^^^^ @@ -578,8 +578,8 @@ var f12: (x: number) => any = x => { // should be (x: number) => Base | AnotherC > : ^ ^^ ^^^^^ >x : number > : ^^^^^^ ->x => { // should be (x: number) => Base | AnotherClass return new Base(); return; // should be ignored return new AnotherClass();} : (x: number) => Base | AnotherClass -> : ^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>x => { // should be (x: number) => Base | AnotherClass return new Base(); return; // should be ignored return new AnotherClass();} : (x: number) => Base +> : ^ ^^^^^^^^^^^^^^^^^ >x : number > : ^^^^^^ diff --git a/tests/baselines/reference/functionWithMultipleReturnStatements.types b/tests/baselines/reference/functionWithMultipleReturnStatements.types index df2788141d906..24f37f6b74dd4 100644 --- a/tests/baselines/reference/functionWithMultipleReturnStatements.types +++ b/tests/baselines/reference/functionWithMultipleReturnStatements.types @@ -5,7 +5,7 @@ // it is an error if there is no single BCT, these are error cases function f1() { ->f1 : () => 1 | "" +>f1 : () => number > : ^^^^^^^^^^^^ if (true) { @@ -24,8 +24,8 @@ function f1() { } function f2() { ->f2 : () => 1 | "" | 2 -> : ^^^^^^^^^^^^^^^^ +>f2 : () => number +> : ^^^^^^^^^^^^ if (true) { >true : true @@ -89,7 +89,7 @@ function f4() { } function f5() { ->f5 : () => 1 | "" +>f5 : () => number > : ^^^^^^^^^^^^ return 1; @@ -102,8 +102,8 @@ function f5() { } function f6(x: T, y:U) { ->f6 : (x: T, y: U) => T | U -> : ^ ^^ ^^ ^^ ^^ ^^ ^^^^^^^^^^ +>f6 : (x: T, y: U) => T +> : ^ ^^ ^^ ^^ ^^ ^^ ^^^^^^ >x : T > : ^ >y : U @@ -125,7 +125,7 @@ function f6(x: T, y:U) { } function f8(x: T, y: U) { ->f8 : (x: T, y: U) => U +>f8 : (x: T, y: U) => T > : ^ ^^^^^^^^^ ^^ ^^^^^^^^^ ^^ ^^ ^^ ^^ ^^ ^^^^^^ >x : T > : ^ diff --git a/tests/baselines/reference/functionWithMultipleReturnStatements2.types b/tests/baselines/reference/functionWithMultipleReturnStatements2.types index a6c3495ae7a5d..df76414b454b4 100644 --- a/tests/baselines/reference/functionWithMultipleReturnStatements2.types +++ b/tests/baselines/reference/functionWithMultipleReturnStatements2.types @@ -22,8 +22,8 @@ function f1() { } function f2() { ->f2 : () => 1 | 2 -> : ^^^^^^^^^^^ +>f2 : () => number +> : ^^^^^^^^^^^^ if (true) { >true : true @@ -69,8 +69,8 @@ function f4() { } function f5() { ->f5 : () => Object | 1 -> : ^^^^^^^^^^^^^^^^ +>f5 : () => number +> : ^^^^^^^^^^^^ return 1; >1 : 1 @@ -128,8 +128,8 @@ var b: { x: number; z?: number }; // returns typeof a function f9() { ->f9 : () => { x: number; y?: number; } | { x: number; z?: number; } -> : ^^^^^^^^^^^ ^^^^^^ ^^^^^^^^^^^ ^^^^^^ ^^^ +>f9 : () => { x: number; y?: number; } +> : ^^^^^^^^^^^ ^^^^^^ ^^^ if (true) { >true : true @@ -148,8 +148,8 @@ function f9() { // returns typeof b function f10() { ->f10 : () => { x: number; y?: number; } | { x: number; z?: number; } -> : ^^^^^^^^^^^ ^^^^^^ ^^^^^^^^^^^ ^^^^^^ ^^^ +>f10 : () => { x: number; z?: number; } +> : ^^^^^^^^^^^ ^^^^^^ ^^^ if (true) { >true : true @@ -168,8 +168,8 @@ function f10() { // returns number => void function f11() { ->f11 : () => ((x: number) => void) | ((x: Object) => void) -> : ^^^^^^^^ ^^ ^^^^^^^^^^^^^^^ ^^ ^^^^^^^^^^ +>f11 : () => (x: number) => void +> : ^^^^^^^ ^^ ^^^^^^^^^ if (true) { >true : true @@ -192,8 +192,8 @@ function f11() { // returns Object => void function f12() { ->f12 : () => ((x: Object) => void) | ((x: number) => void) -> : ^^^^^^^^ ^^ ^^^^^^^^^^^^^^^ ^^ ^^^^^^^^^^ +>f12 : () => (x: Object) => void +> : ^^^^^^^ ^^ ^^^^^^^^^ if (true) { >true : true diff --git a/tests/baselines/reference/functionWithNoBestCommonType1.types b/tests/baselines/reference/functionWithNoBestCommonType1.types index eca6f4c1cece6..a125dacef35ed 100644 --- a/tests/baselines/reference/functionWithNoBestCommonType1.types +++ b/tests/baselines/reference/functionWithNoBestCommonType1.types @@ -2,8 +2,8 @@ === functionWithNoBestCommonType1.ts === function foo() { ->foo : () => true | void -> : ^^^^^^^^^^^^^^^^^ +>foo : () => boolean +> : ^^^^^^^^^^^^^ return true; >true : true diff --git a/tests/baselines/reference/functionWithNoBestCommonType2.types b/tests/baselines/reference/functionWithNoBestCommonType2.types index fca5cc68f2dfc..ec430216760b4 100644 --- a/tests/baselines/reference/functionWithNoBestCommonType2.types +++ b/tests/baselines/reference/functionWithNoBestCommonType2.types @@ -2,10 +2,10 @@ === functionWithNoBestCommonType2.ts === var v = function () { ->v : () => true | void -> : ^^^^^^^^^^^^^^^^^ ->function () { return true; return bar();} : () => true | void -> : ^^^^^^^^^^^^^^^^^ +>v : () => boolean +> : ^^^^^^^^^^^^^ +>function () { return true; return bar();} : () => boolean +> : ^^^^^^^^^^^^^ return true; >true : true diff --git a/tests/baselines/reference/inferredFunctionReturnTypeIsEmptyType.types b/tests/baselines/reference/inferredFunctionReturnTypeIsEmptyType.types index 4ac3c939797be..180e2ead4ef0c 100644 --- a/tests/baselines/reference/inferredFunctionReturnTypeIsEmptyType.types +++ b/tests/baselines/reference/inferredFunctionReturnTypeIsEmptyType.types @@ -2,8 +2,8 @@ === inferredFunctionReturnTypeIsEmptyType.ts === function foo() { ->foo : () => 42 | "42" -> : ^^^^^^^^^^^^^^^ +>foo : () => number +> : ^^^^^^^^^^^^ if (true) { >true : true diff --git a/tests/baselines/reference/narrowingInCaseClauseAfterCaseClauseWithReturn.types b/tests/baselines/reference/narrowingInCaseClauseAfterCaseClauseWithReturn.types index bee05ff1bf3e1..e0422d6cc66fb 100644 --- a/tests/baselines/reference/narrowingInCaseClauseAfterCaseClauseWithReturn.types +++ b/tests/baselines/reference/narrowingInCaseClauseAfterCaseClauseWithReturn.types @@ -94,7 +94,7 @@ function test1(arg: string | undefined) { } function test2(arg: string | undefined) { ->test2 : (arg: string | undefined) => "Not A, B, C or D" | "D" | "A, B or C" +>test2 : (arg: string | undefined) => "D" | "Not A, B, C or D" | "A, B or C" > : ^ ^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ >arg : string | undefined > : ^^^^^^^^^^^^^^^^^^ diff --git a/tests/baselines/reference/null.types b/tests/baselines/reference/null.types index 8887c02ac6ed9..ed0570da68f2f 100644 --- a/tests/baselines/reference/null.types +++ b/tests/baselines/reference/null.types @@ -28,8 +28,8 @@ class C { > : ^ } function f() { ->f : () => C -> : ^^^^^^^ +>f : () => any +> : ^^^^^^^^^ return null; return new C(); @@ -39,8 +39,8 @@ function f() { > : ^^^^^^^^ } function g() { ->g : () => number -> : ^^^^^^^^^^^^ +>g : () => any +> : ^^^^^^^^^ return null; return 3; From c60318edda914e96af4a695beb2eae4a1464d8a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Sun, 16 Nov 2025 15:54:50 +0100 Subject: [PATCH 7/9] handle getters returning never --- src/compiler/binder.ts | 9 +- src/compiler/checker.ts | 4 +- .../unreachableGetterReturns1.symbols | 98 ++++++++++ .../reference/unreachableGetterReturns1.types | 168 ++++++++++++++++++ .../compiler/unreachableGetterReturns1.ts | 48 +++++ 5 files changed, 322 insertions(+), 5 deletions(-) create mode 100644 tests/baselines/reference/unreachableGetterReturns1.symbols create mode 100644 tests/baselines/reference/unreachableGetterReturns1.types create mode 100644 tests/cases/compiler/unreachableGetterReturns1.ts diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 27ec079f614ab..abad965cc83f9 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -1028,9 +1028,10 @@ function createBinder(): (file: SourceFile, options: CompilerOptions) => void { currentFlow.node = node as FunctionExpression | ArrowFunction | MethodDeclaration | GetAccessorDeclaration | SetAccessorDeclaration; } } - // We create a return control flow graph for IIFEs and constructors. For constructors - // we use the return control flow graph in strict property initialization checks. - currentReturnTarget = isImmediatelyInvoked || node.kind === SyntaxKind.Constructor || (isInJSFile(node) && (node.kind === SyntaxKind.FunctionDeclaration || node.kind === SyntaxKind.FunctionExpression)) ? createBranchLabel() : undefined; + // We create a return control flow graph for IIFEs, constructors and getters. + // For constructors we use the return control flow graph in strict property initialization checks. + // For getters we use the return control flow graph to check if it's reachable to conditionally permit returnless getters. + currentReturnTarget = isImmediatelyInvoked || node.kind === SyntaxKind.Constructor || node.kind === SyntaxKind.GetAccessor || (isInJSFile(node) && (node.kind === SyntaxKind.FunctionDeclaration || node.kind === SyntaxKind.FunctionExpression)) ? createBranchLabel() : undefined; currentExceptionTarget = undefined; currentBreakTarget = undefined; currentContinueTarget = undefined; @@ -1052,7 +1053,7 @@ function createBinder(): (file: SourceFile, options: CompilerOptions) => void { if (currentReturnTarget) { addAntecedent(currentReturnTarget, currentFlow); currentFlow = finishFlowLabel(currentReturnTarget); - if (node.kind === SyntaxKind.Constructor || node.kind === SyntaxKind.ClassStaticBlockDeclaration || (isInJSFile(node) && (node.kind === SyntaxKind.FunctionDeclaration || node.kind === SyntaxKind.FunctionExpression))) { + if (node.kind === SyntaxKind.Constructor || node.kind === SyntaxKind.GetAccessor || node.kind === SyntaxKind.ClassStaticBlockDeclaration || (isInJSFile(node) && (node.kind === SyntaxKind.FunctionDeclaration || node.kind === SyntaxKind.FunctionExpression))) { (node as FunctionLikeDeclaration | ClassStaticBlockDeclaration).returnFlowNode = currentFlow; } } diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 28d28205e842b..ad7c811618c58 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -39384,6 +39384,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return true; case SyntaxKind.MethodDeclaration: return func.parent.kind === SyntaxKind.ObjectLiteralExpression; + case SyntaxKind.GetAccessor: + return !isReachableFlowNode(func.returnFlowNode!); default: return false; } @@ -42549,7 +42551,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { checkDecorators(node); checkSignatureDeclaration(node); if (node.kind === SyntaxKind.GetAccessor) { - if (!(node.flags & NodeFlags.Ambient) && nodeIsPresent(node.body) && (node.flags & NodeFlags.HasImplicitReturn)) { + if (!(node.flags & NodeFlags.Ambient) && nodeIsPresent(node.body) && (node.flags & NodeFlags.HasImplicitReturn) && isReachableFlowNode(node.returnFlowNode!)) { if (!(node.flags & NodeFlags.HasExplicitReturn)) { error(node.name, Diagnostics.A_get_accessor_must_return_a_value); } diff --git a/tests/baselines/reference/unreachableGetterReturns1.symbols b/tests/baselines/reference/unreachableGetterReturns1.symbols new file mode 100644 index 0000000000000..b6999e18c5af5 --- /dev/null +++ b/tests/baselines/reference/unreachableGetterReturns1.symbols @@ -0,0 +1,98 @@ +//// [tests/cases/compiler/unreachableGetterReturns1.ts] //// + +=== unreachableGetterReturns1.ts === +declare function assert(cond: unknown): asserts cond; +>assert : Symbol(assert, Decl(unreachableGetterReturns1.ts, 0, 0)) +>cond : Symbol(cond, Decl(unreachableGetterReturns1.ts, 0, 24)) +>cond : Symbol(cond, Decl(unreachableGetterReturns1.ts, 0, 24)) + +const obj1 = { +>obj1 : Symbol(obj1, Decl(unreachableGetterReturns1.ts, 2, 5)) + + get prop() { +>prop : Symbol(prop, Decl(unreachableGetterReturns1.ts, 2, 14)) + + assert(false); +>assert : Symbol(assert, Decl(unreachableGetterReturns1.ts, 0, 0)) + + }, +}; + +const obj2 = { +>obj2 : Symbol(obj2, Decl(unreachableGetterReturns1.ts, 8, 5)) + + get prop(): never { +>prop : Symbol(prop, Decl(unreachableGetterReturns1.ts, 8, 14)) + + assert(false); +>assert : Symbol(assert, Decl(unreachableGetterReturns1.ts, 0, 0)) + + }, +}; + +const obj3 = { +>obj3 : Symbol(obj3, Decl(unreachableGetterReturns1.ts, 14, 5)) + + get prop() { +>prop : Symbol(prop, Decl(unreachableGetterReturns1.ts, 14, 14)) + + if (Math.random()) { +>Math.random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --)) +>Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --)) +>random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --)) + + assert(false); +>assert : Symbol(assert, Decl(unreachableGetterReturns1.ts, 0, 0)) + } + return 42; + }, +}; + +type Obj = { prop: number }; +>Obj : Symbol(Obj, Decl(unreachableGetterReturns1.ts, 21, 2)) +>prop : Symbol(prop, Decl(unreachableGetterReturns1.ts, 23, 12)) + +const obj4: Obj = { +>obj4 : Symbol(obj4, Decl(unreachableGetterReturns1.ts, 25, 5)) +>Obj : Symbol(Obj, Decl(unreachableGetterReturns1.ts, 21, 2)) + + get prop() { +>prop : Symbol(prop, Decl(unreachableGetterReturns1.ts, 25, 19)) + + assert(false); +>assert : Symbol(assert, Decl(unreachableGetterReturns1.ts, 0, 0)) + + }, +}; + +const obj5: Obj = { +>obj5 : Symbol(obj5, Decl(unreachableGetterReturns1.ts, 31, 5)) +>Obj : Symbol(Obj, Decl(unreachableGetterReturns1.ts, 21, 2)) + + get prop(): never { +>prop : Symbol(prop, Decl(unreachableGetterReturns1.ts, 31, 19)) + + assert(false); +>assert : Symbol(assert, Decl(unreachableGetterReturns1.ts, 0, 0)) + + }, +}; + +const obj6: Obj = { +>obj6 : Symbol(obj6, Decl(unreachableGetterReturns1.ts, 37, 5)) +>Obj : Symbol(Obj, Decl(unreachableGetterReturns1.ts, 21, 2)) + + get prop() { +>prop : Symbol(prop, Decl(unreachableGetterReturns1.ts, 37, 19)) + + if (Math.random()) { +>Math.random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --)) +>Math : Symbol(Math, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --)) +>random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --)) + + assert(false); +>assert : Symbol(assert, Decl(unreachableGetterReturns1.ts, 0, 0)) + } + return 42; + }, +}; diff --git a/tests/baselines/reference/unreachableGetterReturns1.types b/tests/baselines/reference/unreachableGetterReturns1.types new file mode 100644 index 0000000000000..1dbe4718f91a1 --- /dev/null +++ b/tests/baselines/reference/unreachableGetterReturns1.types @@ -0,0 +1,168 @@ +//// [tests/cases/compiler/unreachableGetterReturns1.ts] //// + +=== unreachableGetterReturns1.ts === +declare function assert(cond: unknown): asserts cond; +>assert : (cond: unknown) => asserts cond +> : ^ ^^ ^^^^^ +>cond : unknown +> : ^^^^^^^ + +const obj1 = { +>obj1 : { readonly prop: never; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^ +>{ get prop() { assert(false); },} : { readonly prop: never; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^ + + get prop() { +>prop : never +> : ^^^^^ + + assert(false); +>assert(false) : void +> : ^^^^ +>assert : (cond: unknown) => asserts cond +> : ^ ^^ ^^^^^ +>false : false +> : ^^^^^ + + }, +}; + +const obj2 = { +>obj2 : { readonly prop: never; } +> : ^^^^^^^^^^^^^^^^^ ^^^ +>{ get prop(): never { assert(false); },} : { readonly prop: never; } +> : ^^^^^^^^^^^^^^^^^ ^^^ + + get prop(): never { +>prop : never +> : ^^^^^ + + assert(false); +>assert(false) : void +> : ^^^^ +>assert : (cond: unknown) => asserts cond +> : ^ ^^ ^^^^^ +>false : false +> : ^^^^^ + + }, +}; + +const obj3 = { +>obj3 : { readonly prop: number; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^ +>{ get prop() { if (Math.random()) { assert(false); } return 42; },} : { readonly prop: number; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^ + + get prop() { +>prop : number +> : ^^^^^^ + + if (Math.random()) { +>Math.random() : number +> : ^^^^^^ +>Math.random : () => number +> : ^^^^^^ +>Math : Math +> : ^^^^ +>random : () => number +> : ^^^^^^ + + assert(false); +>assert(false) : void +> : ^^^^ +>assert : (cond: unknown) => asserts cond +> : ^ ^^ ^^^^^ +>false : false +> : ^^^^^ + } + return 42; +>42 : 42 +> : ^^ + + }, +}; + +type Obj = { prop: number }; +>Obj : Obj +> : ^^^ +>prop : number +> : ^^^^^^ + +const obj4: Obj = { +>obj4 : Obj +> : ^^^ +>{ get prop() { assert(false); },} : { readonly prop: never; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^ + + get prop() { +>prop : never +> : ^^^^^ + + assert(false); +>assert(false) : void +> : ^^^^ +>assert : (cond: unknown) => asserts cond +> : ^ ^^ ^^^^^ +>false : false +> : ^^^^^ + + }, +}; + +const obj5: Obj = { +>obj5 : Obj +> : ^^^ +>{ get prop(): never { assert(false); },} : { readonly prop: never; } +> : ^^^^^^^^^^^^^^^^^ ^^^ + + get prop(): never { +>prop : never +> : ^^^^^ + + assert(false); +>assert(false) : void +> : ^^^^ +>assert : (cond: unknown) => asserts cond +> : ^ ^^ ^^^^^ +>false : false +> : ^^^^^ + + }, +}; + +const obj6: Obj = { +>obj6 : Obj +> : ^^^ +>{ get prop() { if (Math.random()) { assert(false); } return 42; },} : { readonly prop: number; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^ + + get prop() { +>prop : number +> : ^^^^^^ + + if (Math.random()) { +>Math.random() : number +> : ^^^^^^ +>Math.random : () => number +> : ^^^^^^ +>Math : Math +> : ^^^^ +>random : () => number +> : ^^^^^^ + + assert(false); +>assert(false) : void +> : ^^^^ +>assert : (cond: unknown) => asserts cond +> : ^ ^^ ^^^^^ +>false : false +> : ^^^^^ + } + return 42; +>42 : 42 +> : ^^ + + }, +}; diff --git a/tests/cases/compiler/unreachableGetterReturns1.ts b/tests/cases/compiler/unreachableGetterReturns1.ts new file mode 100644 index 0000000000000..ea03aedb6b324 --- /dev/null +++ b/tests/cases/compiler/unreachableGetterReturns1.ts @@ -0,0 +1,48 @@ +// @strict: true +// @noEmit: true + +declare function assert(cond: unknown): asserts cond; + +const obj1 = { + get prop() { + assert(false); + }, +}; + +const obj2 = { + get prop(): never { + assert(false); + }, +}; + +const obj3 = { + get prop() { + if (Math.random()) { + assert(false); + } + return 42; + }, +}; + +type Obj = { prop: number }; + +const obj4: Obj = { + get prop() { + assert(false); + }, +}; + +const obj5: Obj = { + get prop(): never { + assert(false); + }, +}; + +const obj6: Obj = { + get prop() { + if (Math.random()) { + assert(false); + } + return 42; + }, +}; \ No newline at end of file From ce446e85b0a92523f04a743c52d58ed251b2bb83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Sun, 16 Nov 2025 15:55:35 +0100 Subject: [PATCH 8/9] update editorServices --- src/server/editorServices.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index 3ee35adc46c67..a222c6cac9003 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -3920,9 +3920,8 @@ export class ProjectService { // Create sourceFileLike if (!info.sourceFileLike) { info.sourceFileLike = { - get text() { + get text(): never { Debug.fail("shouldnt need text"); - return "" as any; }, getLineAndCharacterOfPosition: pos => { const lineOffset = info.positionToLineOffset(pos); From 90d7e6bdb5cccfa37dca52c8e9bc232bbf09b20d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Sun, 16 Nov 2025 16:16:31 +0100 Subject: [PATCH 9/9] require explicit `never` annotation on getter --- src/compiler/binder.ts | 9 ++- src/compiler/checker.ts | 14 ++--- src/server/editorServices.ts | 2 +- .../unreachableGetterReturns1.errors.txt | 59 +++++++++++++++++++ .../unreachableGetterReturns1.symbols | 1 + .../reference/unreachableGetterReturns1.types | 21 +++---- .../compiler/unreachableGetterReturns1.ts | 2 +- 7 files changed, 83 insertions(+), 25 deletions(-) create mode 100644 tests/baselines/reference/unreachableGetterReturns1.errors.txt diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index abad965cc83f9..27ec079f614ab 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -1028,10 +1028,9 @@ function createBinder(): (file: SourceFile, options: CompilerOptions) => void { currentFlow.node = node as FunctionExpression | ArrowFunction | MethodDeclaration | GetAccessorDeclaration | SetAccessorDeclaration; } } - // We create a return control flow graph for IIFEs, constructors and getters. - // For constructors we use the return control flow graph in strict property initialization checks. - // For getters we use the return control flow graph to check if it's reachable to conditionally permit returnless getters. - currentReturnTarget = isImmediatelyInvoked || node.kind === SyntaxKind.Constructor || node.kind === SyntaxKind.GetAccessor || (isInJSFile(node) && (node.kind === SyntaxKind.FunctionDeclaration || node.kind === SyntaxKind.FunctionExpression)) ? createBranchLabel() : undefined; + // We create a return control flow graph for IIFEs and constructors. For constructors + // we use the return control flow graph in strict property initialization checks. + currentReturnTarget = isImmediatelyInvoked || node.kind === SyntaxKind.Constructor || (isInJSFile(node) && (node.kind === SyntaxKind.FunctionDeclaration || node.kind === SyntaxKind.FunctionExpression)) ? createBranchLabel() : undefined; currentExceptionTarget = undefined; currentBreakTarget = undefined; currentContinueTarget = undefined; @@ -1053,7 +1052,7 @@ function createBinder(): (file: SourceFile, options: CompilerOptions) => void { if (currentReturnTarget) { addAntecedent(currentReturnTarget, currentFlow); currentFlow = finishFlowLabel(currentReturnTarget); - if (node.kind === SyntaxKind.Constructor || node.kind === SyntaxKind.GetAccessor || node.kind === SyntaxKind.ClassStaticBlockDeclaration || (isInJSFile(node) && (node.kind === SyntaxKind.FunctionDeclaration || node.kind === SyntaxKind.FunctionExpression))) { + if (node.kind === SyntaxKind.Constructor || node.kind === SyntaxKind.ClassStaticBlockDeclaration || (isInJSFile(node) && (node.kind === SyntaxKind.FunctionDeclaration || node.kind === SyntaxKind.FunctionExpression))) { (node as FunctionLikeDeclaration | ClassStaticBlockDeclaration).returnFlowNode = currentFlow; } } diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index ad7c811618c58..c2ef6eef36ce6 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -39384,8 +39384,6 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return true; case SyntaxKind.MethodDeclaration: return func.parent.kind === SyntaxKind.ObjectLiteralExpression; - case SyntaxKind.GetAccessor: - return !isReachableFlowNode(func.returnFlowNode!); default: return false; } @@ -42550,12 +42548,12 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { checkDecorators(node); checkSignatureDeclaration(node); - if (node.kind === SyntaxKind.GetAccessor) { - if (!(node.flags & NodeFlags.Ambient) && nodeIsPresent(node.body) && (node.flags & NodeFlags.HasImplicitReturn) && isReachableFlowNode(node.returnFlowNode!)) { - if (!(node.flags & NodeFlags.HasExplicitReturn)) { - error(node.name, Diagnostics.A_get_accessor_must_return_a_value); - } - } + if ( + node.kind === SyntaxKind.GetAccessor && !(node.flags & NodeFlags.Ambient) && nodeIsPresent(node.body) && + (node.flags & (NodeFlags.HasImplicitReturn | NodeFlags.HasExplicitReturn)) === NodeFlags.HasImplicitReturn && + (!node.type || !(getTypeFromTypeNode(node.type).flags & TypeFlags.Never)) + ) { + error(node.name, Diagnostics.A_get_accessor_must_return_a_value); } // Do not use hasDynamicName here, because that returns false for well known symbols. // We want to perform checkComputedPropertyName for all computed properties, including diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index a222c6cac9003..20d8b076a1a19 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -3921,7 +3921,7 @@ export class ProjectService { if (!info.sourceFileLike) { info.sourceFileLike = { get text(): never { - Debug.fail("shouldnt need text"); + return Debug.fail("shouldnt need text"); }, getLineAndCharacterOfPosition: pos => { const lineOffset = info.positionToLineOffset(pos); diff --git a/tests/baselines/reference/unreachableGetterReturns1.errors.txt b/tests/baselines/reference/unreachableGetterReturns1.errors.txt new file mode 100644 index 0000000000000..35dfcba5b9416 --- /dev/null +++ b/tests/baselines/reference/unreachableGetterReturns1.errors.txt @@ -0,0 +1,59 @@ +unreachableGetterReturns1.ts(4,7): error TS2378: A 'get' accessor must return a value. +unreachableGetterReturns1.ts(27,7): error TS2322: Type 'void' is not assignable to type 'number'. +unreachableGetterReturns1.ts(27,7): error TS2378: A 'get' accessor must return a value. + + +==== unreachableGetterReturns1.ts (3 errors) ==== + declare function assert(cond: unknown): asserts cond; + + const obj1 = { + get prop() { + ~~~~ +!!! error TS2378: A 'get' accessor must return a value. + assert(false); + }, + }; + + const obj2 = { + get prop(): never { + assert(false); + }, + }; + + const obj3 = { + get prop() { + if (Math.random()) { + assert(false); + } + return 42; + }, + }; + + type Obj = { prop: number }; + + const obj4: Obj = { + get prop() { + ~~~~ +!!! error TS2322: Type 'void' is not assignable to type 'number'. +!!! related TS6500 unreachableGetterReturns1.ts:24:14: The expected type comes from property 'prop' which is declared here on type 'Obj' + ~~~~ +!!! error TS2378: A 'get' accessor must return a value. + assert(false); + }, + }; + + const obj5: Obj = { + get prop(): never { + assert(false); + }, + }; + + const obj6: Obj = { + get prop() { + if (Math.random()) { + assert(false); + } + return 42; + }, + }; + \ No newline at end of file diff --git a/tests/baselines/reference/unreachableGetterReturns1.symbols b/tests/baselines/reference/unreachableGetterReturns1.symbols index b6999e18c5af5..667e0eab2c23f 100644 --- a/tests/baselines/reference/unreachableGetterReturns1.symbols +++ b/tests/baselines/reference/unreachableGetterReturns1.symbols @@ -96,3 +96,4 @@ const obj6: Obj = { return 42; }, }; + diff --git a/tests/baselines/reference/unreachableGetterReturns1.types b/tests/baselines/reference/unreachableGetterReturns1.types index 1dbe4718f91a1..9adcce1af1f04 100644 --- a/tests/baselines/reference/unreachableGetterReturns1.types +++ b/tests/baselines/reference/unreachableGetterReturns1.types @@ -8,14 +8,14 @@ declare function assert(cond: unknown): asserts cond; > : ^^^^^^^ const obj1 = { ->obj1 : { readonly prop: never; } -> : ^^^^^^^^^^^^^^^^^^^^^^^^^ ->{ get prop() { assert(false); },} : { readonly prop: never; } -> : ^^^^^^^^^^^^^^^^^^^^^^^^^ +>obj1 : { readonly prop: void; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^ +>{ get prop() { assert(false); },} : { readonly prop: void; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^ get prop() { ->prop : never -> : ^^^^^ +>prop : void +> : ^^^^ assert(false); >assert(false) : void @@ -93,12 +93,12 @@ type Obj = { prop: number }; const obj4: Obj = { >obj4 : Obj > : ^^^ ->{ get prop() { assert(false); },} : { readonly prop: never; } -> : ^^^^^^^^^^^^^^^^^^^^^^^^^ +>{ get prop() { assert(false); },} : { readonly prop: void; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^ get prop() { ->prop : never -> : ^^^^^ +>prop : void +> : ^^^^ assert(false); >assert(false) : void @@ -166,3 +166,4 @@ const obj6: Obj = { }, }; + diff --git a/tests/cases/compiler/unreachableGetterReturns1.ts b/tests/cases/compiler/unreachableGetterReturns1.ts index ea03aedb6b324..ca7a67fb323ec 100644 --- a/tests/cases/compiler/unreachableGetterReturns1.ts +++ b/tests/cases/compiler/unreachableGetterReturns1.ts @@ -45,4 +45,4 @@ const obj6: Obj = { } return 42; }, -}; \ No newline at end of file +};