From 07c192eebc0c64fb542d3dc52dcc18dc48c9c680 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Tue, 25 Feb 2025 15:09:35 +0100 Subject: [PATCH 1/3] Fixed an issue with `ReduceLabel` flows spoiling node reachability cache --- src/compiler/checker.ts | 30 ++- .../reference/reachabilityChecks9.symbols | 113 ++++++++++ .../reference/reachabilityChecks9.types | 197 ++++++++++++++++++ tests/cases/compiler/reachabilityChecks9.ts | 61 ++++++ 4 files changed, 393 insertions(+), 8 deletions(-) create mode 100644 tests/baselines/reference/reachabilityChecks9.symbols create mode 100644 tests/baselines/reference/reachabilityChecks9.types create mode 100644 tests/cases/compiler/reachabilityChecks9.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index cbdb92933023e..59149d004aebe 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -1426,6 +1426,13 @@ const enum IntrinsicTypeKind { NoInfer, } +const enum FlowNodeReachableCacheFlags { + None = 0, + Read = 1 << 0, + Write = 1 << 1, + ReadWrite = Read | Write, +} + const intrinsicTypeKinds: ReadonlyMap = new Map(Object.entries({ Uppercase: IntrinsicTypeKind.Uppercase, Lowercase: IntrinsicTypeKind.Lowercase, @@ -28104,7 +28111,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } function isReachableFlowNode(flow: FlowNode) { - const result = isReachableFlowNodeWorker(flow, /*noCacheCheck*/ false); + const result = isReachableFlowNodeWorker(flow, FlowNodeReachableCacheFlags.ReadWrite); lastFlowNode = flow; lastFlowNodeReachable = result; return result; @@ -28118,19 +28125,26 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { ); } - function isReachableFlowNodeWorker(flow: FlowNode, noCacheCheck: boolean): boolean { + function isReachableFlowNodeWorker(flow: FlowNode, cacheFlags: FlowNodeReachableCacheFlags): boolean { while (true) { if (flow === lastFlowNode) { return lastFlowNodeReachable; } const flags = flow.flags; if (flags & FlowFlags.Shared) { - if (!noCacheCheck) { + if (cacheFlags & FlowNodeReachableCacheFlags.Read) { const id = getFlowNodeId(flow); - const reachable = flowNodeReachable[id]; - return reachable !== undefined ? reachable : (flowNodeReachable[id] = isReachableFlowNodeWorker(flow, /*noCacheCheck*/ true)); + let reachable = flowNodeReachable[id]; + if (reachable !== undefined) { + return reachable; + } + reachable = isReachableFlowNodeWorker(flow, cacheFlags & ~FlowNodeReachableCacheFlags.Read); + if (cacheFlags & FlowNodeReachableCacheFlags.Write) { + flowNodeReachable[id] = reachable; + } + return reachable; } - noCacheCheck = false; + cacheFlags |= FlowNodeReachableCacheFlags.Read; } if (flags & (FlowFlags.Assignment | FlowFlags.Condition | FlowFlags.ArrayMutation)) { flow = (flow as FlowAssignment | FlowCondition | FlowArrayMutation).antecedent; @@ -28153,7 +28167,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } else if (flags & FlowFlags.BranchLabel) { // A branching point is reachable if any branch is reachable. - return some((flow as FlowLabel).antecedent, f => isReachableFlowNodeWorker(f, /*noCacheCheck*/ false)); + return some((flow as FlowLabel).antecedent, f => isReachableFlowNodeWorker(f, cacheFlags)); } else if (flags & FlowFlags.LoopLabel) { const antecedents = (flow as FlowLabel).antecedent; @@ -28178,7 +28192,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { const target = (flow as FlowReduceLabel).node.target; const saveAntecedents = target.antecedent; target.antecedent = (flow as FlowReduceLabel).node.antecedents; - const result = isReachableFlowNodeWorker((flow as FlowReduceLabel).antecedent, /*noCacheCheck*/ false); + const result = isReachableFlowNodeWorker((flow as FlowReduceLabel).antecedent, FlowNodeReachableCacheFlags.None); target.antecedent = saveAntecedents; return result; } diff --git a/tests/baselines/reference/reachabilityChecks9.symbols b/tests/baselines/reference/reachabilityChecks9.symbols new file mode 100644 index 0000000000000..7ccd67398fc8b --- /dev/null +++ b/tests/baselines/reference/reachabilityChecks9.symbols @@ -0,0 +1,113 @@ +//// [tests/cases/compiler/reachabilityChecks9.ts] //// + +=== reachabilityChecks9.ts === +// https://github.com/microsoft/TypeScript/issues/61259 + +const a = (v: 1 | 2) => { +>a : Symbol(a, Decl(reachabilityChecks9.ts, 2, 5)) +>v : Symbol(v, Decl(reachabilityChecks9.ts, 2, 11)) + + try { + switch (v) { +>v : Symbol(v, Decl(reachabilityChecks9.ts, 2, 11)) + + case 1: + return v; +>v : Symbol(v, Decl(reachabilityChecks9.ts, 2, 11)) + + case 2: + return v; +>v : Symbol(v, Decl(reachabilityChecks9.ts, 2, 11)) + } + } finally { + console.log("exit"); +>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) +>console : Symbol(console, Decl(lib.dom.d.ts, --, --)) +>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) + } +}; + +const b = (v: number) => { +>b : Symbol(b, Decl(reachabilityChecks9.ts, 15, 5)) +>v : Symbol(v, Decl(reachabilityChecks9.ts, 15, 11)) + + try { + switch (v) { +>v : Symbol(v, Decl(reachabilityChecks9.ts, 15, 11)) + + case 1: + return v; +>v : Symbol(v, Decl(reachabilityChecks9.ts, 15, 11)) + + default: + return v; +>v : Symbol(v, Decl(reachabilityChecks9.ts, 15, 11)) + } + } finally { + console.log("exit"); +>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) +>console : Symbol(console, Decl(lib.dom.d.ts, --, --)) +>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) + } +}; + +const c = (v: 1 | 2) => { +>c : Symbol(c, Decl(reachabilityChecks9.ts, 28, 5)) +>v : Symbol(v, Decl(reachabilityChecks9.ts, 28, 11)) + + try { + switch (v) { +>v : Symbol(v, Decl(reachabilityChecks9.ts, 28, 11)) + + case 1: + return v; +>v : Symbol(v, Decl(reachabilityChecks9.ts, 28, 11)) + + case 2: + return v; +>v : Symbol(v, Decl(reachabilityChecks9.ts, 28, 11)) + } + } finally { + 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, --, --)) +>random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --)) + + console.log("exit"); +>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) +>console : Symbol(console, Decl(lib.dom.d.ts, --, --)) +>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) + } + } +}; + +const d = (v: number) => { +>d : Symbol(d, Decl(reachabilityChecks9.ts, 43, 5)) +>v : Symbol(v, Decl(reachabilityChecks9.ts, 43, 11)) + + try { + switch (v) { +>v : Symbol(v, Decl(reachabilityChecks9.ts, 43, 11)) + + case 1: + return v; +>v : Symbol(v, Decl(reachabilityChecks9.ts, 43, 11)) + + default: + return v; +>v : Symbol(v, Decl(reachabilityChecks9.ts, 43, 11)) + } + } finally { + 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, --, --)) +>random : Symbol(Math.random, Decl(lib.es5.d.ts, --, --)) + + console.log("exit"); +>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) +>console : Symbol(console, Decl(lib.dom.d.ts, --, --)) +>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) + } + } +}; + diff --git a/tests/baselines/reference/reachabilityChecks9.types b/tests/baselines/reference/reachabilityChecks9.types new file mode 100644 index 0000000000000..102cba292500a --- /dev/null +++ b/tests/baselines/reference/reachabilityChecks9.types @@ -0,0 +1,197 @@ +//// [tests/cases/compiler/reachabilityChecks9.ts] //// + +=== reachabilityChecks9.ts === +// https://github.com/microsoft/TypeScript/issues/61259 + +const a = (v: 1 | 2) => { +>a : (v: 1 | 2) => 1 | 2 +> : ^ ^^ ^^^^^^^^^^ +>(v: 1 | 2) => { try { switch (v) { case 1: return v; case 2: return v; } } finally { console.log("exit"); }} : (v: 1 | 2) => 1 | 2 +> : ^ ^^ ^^^^^^^^^^ +>v : 1 | 2 +> : ^^^^^ + + try { + switch (v) { +>v : 1 | 2 +> : ^^^^^ + + case 1: +>1 : 1 +> : ^ + + return v; +>v : 1 +> : ^ + + case 2: +>2 : 2 +> : ^ + + return v; +>v : 2 +> : ^ + } + } finally { + console.log("exit"); +>console.log("exit") : void +> : ^^^^ +>console.log : (...data: any[]) => void +> : ^^^^ ^^ ^^^^^ +>console : Console +> : ^^^^^^^ +>log : (...data: any[]) => void +> : ^^^^ ^^ ^^^^^ +>"exit" : "exit" +> : ^^^^^^ + } +}; + +const b = (v: number) => { +>b : (v: number) => number +> : ^ ^^ ^^^^^^^^^^^ +>(v: number) => { try { switch (v) { case 1: return v; default: return v; } } finally { console.log("exit"); }} : (v: number) => number +> : ^ ^^ ^^^^^^^^^^^ +>v : number +> : ^^^^^^ + + try { + switch (v) { +>v : number +> : ^^^^^^ + + case 1: +>1 : 1 +> : ^ + + return v; +>v : 1 +> : ^ + + default: + return v; +>v : number +> : ^^^^^^ + } + } finally { + console.log("exit"); +>console.log("exit") : void +> : ^^^^ +>console.log : (...data: any[]) => void +> : ^^^^ ^^ ^^^^^ +>console : Console +> : ^^^^^^^ +>log : (...data: any[]) => void +> : ^^^^ ^^ ^^^^^ +>"exit" : "exit" +> : ^^^^^^ + } +}; + +const c = (v: 1 | 2) => { +>c : (v: 1 | 2) => 1 | 2 +> : ^ ^^ ^^^^^^^^^^ +>(v: 1 | 2) => { try { switch (v) { case 1: return v; case 2: return v; } } finally { if (Math.random()) { console.log("exit"); } }} : (v: 1 | 2) => 1 | 2 +> : ^ ^^ ^^^^^^^^^^ +>v : 1 | 2 +> : ^^^^^ + + try { + switch (v) { +>v : 1 | 2 +> : ^^^^^ + + case 1: +>1 : 1 +> : ^ + + return v; +>v : 1 +> : ^ + + case 2: +>2 : 2 +> : ^ + + return v; +>v : 2 +> : ^ + } + } finally { + if (Math.random()) { +>Math.random() : number +> : ^^^^^^ +>Math.random : () => number +> : ^^^^^^ +>Math : Math +> : ^^^^ +>random : () => number +> : ^^^^^^ + + console.log("exit"); +>console.log("exit") : void +> : ^^^^ +>console.log : (...data: any[]) => void +> : ^^^^ ^^ ^^^^^ +>console : Console +> : ^^^^^^^ +>log : (...data: any[]) => void +> : ^^^^ ^^ ^^^^^ +>"exit" : "exit" +> : ^^^^^^ + } + } +}; + +const d = (v: number) => { +>d : (v: number) => number +> : ^ ^^ ^^^^^^^^^^^ +>(v: number) => { try { switch (v) { case 1: return v; default: return v; } } finally { if (Math.random()) { console.log("exit"); } }} : (v: number) => number +> : ^ ^^ ^^^^^^^^^^^ +>v : number +> : ^^^^^^ + + try { + switch (v) { +>v : number +> : ^^^^^^ + + case 1: +>1 : 1 +> : ^ + + return v; +>v : 1 +> : ^ + + default: + return v; +>v : number +> : ^^^^^^ + } + } finally { + if (Math.random()) { +>Math.random() : number +> : ^^^^^^ +>Math.random : () => number +> : ^^^^^^ +>Math : Math +> : ^^^^ +>random : () => number +> : ^^^^^^ + + console.log("exit"); +>console.log("exit") : void +> : ^^^^ +>console.log : (...data: any[]) => void +> : ^^^^ ^^ ^^^^^ +>console : Console +> : ^^^^^^^ +>log : (...data: any[]) => void +> : ^^^^ ^^ ^^^^^ +>"exit" : "exit" +> : ^^^^^^ + } + } +}; + diff --git a/tests/cases/compiler/reachabilityChecks9.ts b/tests/cases/compiler/reachabilityChecks9.ts new file mode 100644 index 0000000000000..b6f6da1bf649d --- /dev/null +++ b/tests/cases/compiler/reachabilityChecks9.ts @@ -0,0 +1,61 @@ +// @strict: true +// @allowUnreachableCode: false +// @noEmit: true + +// https://github.com/microsoft/TypeScript/issues/61259 + +const a = (v: 1 | 2) => { + try { + switch (v) { + case 1: + return v; + case 2: + return v; + } + } finally { + console.log("exit"); + } +}; + +const b = (v: number) => { + try { + switch (v) { + case 1: + return v; + default: + return v; + } + } finally { + console.log("exit"); + } +}; + +const c = (v: 1 | 2) => { + try { + switch (v) { + case 1: + return v; + case 2: + return v; + } + } finally { + if (Math.random()) { + console.log("exit"); + } + } +}; + +const d = (v: number) => { + try { + switch (v) { + case 1: + return v; + default: + return v; + } + } finally { + if (Math.random()) { + console.log("exit"); + } + } +}; From 4552c202b31a78d09af2ab75524743579b6bcd2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Tue, 25 Feb 2025 16:05:22 +0100 Subject: [PATCH 2/3] apply the same fix in `isPostSuperFlowNode` --- src/compiler/checker.ts | 39 +++++++++++++++++++++++---------------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 59149d004aebe..44c93c893e6e7 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -1426,7 +1426,7 @@ const enum IntrinsicTypeKind { NoInfer, } -const enum FlowNodeReachableCacheFlags { +const enum SharedFlowNodeCacheFlags { None = 0, Read = 1 << 0, Write = 1 << 1, @@ -28111,7 +28111,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } function isReachableFlowNode(flow: FlowNode) { - const result = isReachableFlowNodeWorker(flow, FlowNodeReachableCacheFlags.ReadWrite); + const result = isReachableFlowNodeWorker(flow, SharedFlowNodeCacheFlags.ReadWrite); lastFlowNode = flow; lastFlowNodeReachable = result; return result; @@ -28125,26 +28125,26 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { ); } - function isReachableFlowNodeWorker(flow: FlowNode, cacheFlags: FlowNodeReachableCacheFlags): boolean { + function isReachableFlowNodeWorker(flow: FlowNode, cacheFlags: SharedFlowNodeCacheFlags): boolean { while (true) { if (flow === lastFlowNode) { return lastFlowNodeReachable; } const flags = flow.flags; if (flags & FlowFlags.Shared) { - if (cacheFlags & FlowNodeReachableCacheFlags.Read) { + if (cacheFlags & SharedFlowNodeCacheFlags.Read) { const id = getFlowNodeId(flow); let reachable = flowNodeReachable[id]; if (reachable !== undefined) { return reachable; } - reachable = isReachableFlowNodeWorker(flow, cacheFlags & ~FlowNodeReachableCacheFlags.Read); - if (cacheFlags & FlowNodeReachableCacheFlags.Write) { + reachable = isReachableFlowNodeWorker(flow, cacheFlags & ~SharedFlowNodeCacheFlags.Read); + if (cacheFlags & SharedFlowNodeCacheFlags.Write) { flowNodeReachable[id] = reachable; } return reachable; } - cacheFlags |= FlowNodeReachableCacheFlags.Read; + cacheFlags |= SharedFlowNodeCacheFlags.Read; } if (flags & (FlowFlags.Assignment | FlowFlags.Condition | FlowFlags.ArrayMutation)) { flow = (flow as FlowAssignment | FlowCondition | FlowArrayMutation).antecedent; @@ -28192,7 +28192,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { const target = (flow as FlowReduceLabel).node.target; const saveAntecedents = target.antecedent; target.antecedent = (flow as FlowReduceLabel).node.antecedents; - const result = isReachableFlowNodeWorker((flow as FlowReduceLabel).antecedent, FlowNodeReachableCacheFlags.None); + const result = isReachableFlowNodeWorker((flow as FlowReduceLabel).antecedent, SharedFlowNodeCacheFlags.None); target.antecedent = saveAntecedents; return result; } @@ -28204,16 +28204,23 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { // Return true if the given flow node is preceded by a 'super(...)' call in every possible code path // leading to the node. - function isPostSuperFlowNode(flow: FlowNode, noCacheCheck: boolean): boolean { + function isPostSuperFlowNode(flow: FlowNode, cacheFlags: SharedFlowNodeCacheFlags): boolean { while (true) { const flags = flow.flags; if (flags & FlowFlags.Shared) { - if (!noCacheCheck) { + if (cacheFlags & SharedFlowNodeCacheFlags.Read) { const id = getFlowNodeId(flow); - const postSuper = flowNodePostSuper[id]; - return postSuper !== undefined ? postSuper : (flowNodePostSuper[id] = isPostSuperFlowNode(flow, /*noCacheCheck*/ true)); + let postSuper = flowNodePostSuper[id]; + if (postSuper !== undefined) { + return postSuper; + } + postSuper = isPostSuperFlowNode(flow, cacheFlags & ~SharedFlowNodeCacheFlags.Read); + if (cacheFlags & SharedFlowNodeCacheFlags.Write) { + flowNodePostSuper[id] = postSuper; + } + return postSuper; } - noCacheCheck = false; + cacheFlags |= SharedFlowNodeCacheFlags.Read; } if (flags & (FlowFlags.Assignment | FlowFlags.Condition | FlowFlags.ArrayMutation | FlowFlags.SwitchClause)) { flow = (flow as FlowAssignment | FlowCondition | FlowArrayMutation | FlowSwitchClause).antecedent; @@ -28226,7 +28233,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } else if (flags & FlowFlags.BranchLabel) { // A branching point is post-super if every branch is post-super. - return every((flow as FlowLabel).antecedent, f => isPostSuperFlowNode(f, /*noCacheCheck*/ false)); + return every((flow as FlowLabel).antecedent, f => isPostSuperFlowNode(f, cacheFlags)); } else if (flags & FlowFlags.LoopLabel) { // A loop is post-super if the control flow path that leads to the top is post-super. @@ -28236,7 +28243,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { const target = (flow as FlowReduceLabel).node.target; const saveAntecedents = target.antecedent; target.antecedent = (flow as FlowReduceLabel).node.antecedents; - const result = isPostSuperFlowNode((flow as FlowReduceLabel).antecedent, /*noCacheCheck*/ false); + const result = isPostSuperFlowNode((flow as FlowReduceLabel).antecedent, SharedFlowNodeCacheFlags.None); target.antecedent = saveAntecedents; return result; } @@ -30673,7 +30680,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { // If a containing class does not have extends clause or the class extends null // skip checking whether super statement is called before "this" accessing. if (baseTypeNode && !classDeclarationExtendsNull(containingClassDecl)) { - if (canHaveFlowNode(node) && node.flowNode && !isPostSuperFlowNode(node.flowNode, /*noCacheCheck*/ false)) { + if (canHaveFlowNode(node) && node.flowNode && !isPostSuperFlowNode(node.flowNode, SharedFlowNodeCacheFlags.ReadWrite)) { error(node, diagnosticMessage); } } From a6cae976dbee41982a7a39322a609829911b831c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Sun, 18 Jan 2026 14:21:02 +0100 Subject: [PATCH 3/3] add test case --- .../reference/reachabilityChecks12.symbols | 38 +++++++++++ .../reference/reachabilityChecks12.types | 63 +++++++++++++++++++ tests/cases/compiler/reachabilityChecks12.ts | 17 +++++ 3 files changed, 118 insertions(+) diff --git a/tests/baselines/reference/reachabilityChecks12.symbols b/tests/baselines/reference/reachabilityChecks12.symbols index e2cbdf6721277..071f9011d0338 100644 --- a/tests/baselines/reference/reachabilityChecks12.symbols +++ b/tests/baselines/reference/reachabilityChecks12.symbols @@ -111,3 +111,41 @@ const d = (v: number) => { } }; +// https://github.com/microsoft/TypeScript/issues/63004 +const report: (e: any) => never = (e): never => { +>report : Symbol(report, Decl(reachabilityChecks12.ts, 59, 5)) +>e : Symbol(e, Decl(reachabilityChecks12.ts, 59, 15)) +>e : Symbol(e, Decl(reachabilityChecks12.ts, 59, 35)) + + throw e; +>e : Symbol(e, Decl(reachabilityChecks12.ts, 59, 35)) + +}; + +const foo = async (): Promise => { +>foo : Symbol(foo, Decl(reachabilityChecks12.ts, 63, 5)) +>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --)) + + try { + return 3; + } catch (e) { +>e : Symbol(e, Decl(reachabilityChecks12.ts, 66, 11)) + + report(e); +>report : Symbol(report, Decl(reachabilityChecks12.ts, 59, 5)) +>e : Symbol(e, Decl(reachabilityChecks12.ts, 66, 11)) + + } finally { + 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, --, --)) + + console.log("heh"); +>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) +>console : Symbol(console, Decl(lib.dom.d.ts, --, --)) +>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) + } + } +}; + diff --git a/tests/baselines/reference/reachabilityChecks12.types b/tests/baselines/reference/reachabilityChecks12.types index c687b24dd7d8c..8533677d7534a 100644 --- a/tests/baselines/reference/reachabilityChecks12.types +++ b/tests/baselines/reference/reachabilityChecks12.types @@ -195,3 +195,66 @@ const d = (v: number) => { } }; +// https://github.com/microsoft/TypeScript/issues/63004 +const report: (e: any) => never = (e): never => { +>report : (e: any) => never +> : ^ ^^ ^^^^^ +>e : any +>(e): never => { throw e;} : (e: any) => never +> : ^ ^^^^^^^^^^ +>e : any + + throw e; +>e : any + +}; + +const foo = async (): Promise => { +>foo : () => Promise +> : ^^^^^^ +>async (): Promise => { try { return 3; } catch (e) { report(e); } finally { if (Math.random()) { console.log("heh"); } }} : () => Promise +> : ^^^^^^ + + try { + return 3; +>3 : 3 +> : ^ + + } catch (e) { +>e : unknown +> : ^^^^^^^ + + report(e); +>report(e) : never +> : ^^^^^ +>report : (e: any) => never +> : ^ ^^ ^^^^^ +>e : unknown +> : ^^^^^^^ + + } finally { + if (Math.random()) { +>Math.random() : number +> : ^^^^^^ +>Math.random : () => number +> : ^^^^^^ +>Math : Math +> : ^^^^ +>random : () => number +> : ^^^^^^ + + console.log("heh"); +>console.log("heh") : void +> : ^^^^ +>console.log : (...data: any[]) => void +> : ^^^^ ^^ ^^^^^ +>console : Console +> : ^^^^^^^ +>log : (...data: any[]) => void +> : ^^^^ ^^ ^^^^^ +>"heh" : "heh" +> : ^^^^^ + } + } +}; + diff --git a/tests/cases/compiler/reachabilityChecks12.ts b/tests/cases/compiler/reachabilityChecks12.ts index b6f6da1bf649d..d4ea45221e4f2 100644 --- a/tests/cases/compiler/reachabilityChecks12.ts +++ b/tests/cases/compiler/reachabilityChecks12.ts @@ -59,3 +59,20 @@ const d = (v: number) => { } } }; + +// https://github.com/microsoft/TypeScript/issues/63004 +const report: (e: any) => never = (e): never => { + throw e; +}; + +const foo = async (): Promise => { + try { + return 3; + } catch (e) { + report(e); + } finally { + if (Math.random()) { + console.log("heh"); + } + } +};