mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-05-19 01:33:15 -05:00
Properly handle control flows from returns in try/catch within IIFE (#36901)
* Properly handle control flows from returns in try/catch within IIFE * Accept new baselines * Add tests * Accept new baselines * When end of finally is unreachable, end of try statement is too * Add additional test case
This commit is contained in:
@@ -952,6 +952,10 @@ namespace ts {
|
||||
return initFlowNode({ flags: FlowFlags.LoopLabel, antecedents: undefined });
|
||||
}
|
||||
|
||||
function createReduceLabel(target: FlowLabel, antecedents: FlowNode[], antecedent: FlowNode): FlowReduceLabel {
|
||||
return initFlowNode({ flags: FlowFlags.ReduceLabel, target, antecedents, antecedent });
|
||||
}
|
||||
|
||||
function setFlowNodeReferenced(flow: FlowNode) {
|
||||
// On first reference we set the Referenced flag, thereafter we set the Shared flag
|
||||
flow.flags |= flow.flags & FlowFlags.Referenced ? FlowFlags.Shared : FlowFlags.Referenced;
|
||||
@@ -1209,35 +1213,36 @@ namespace ts {
|
||||
}
|
||||
|
||||
function bindTryStatement(node: TryStatement): void {
|
||||
const preFinallyLabel = createBranchLabel();
|
||||
// We conservatively assume that *any* code in the try block can cause an exception, but we only need
|
||||
// to track code that causes mutations (because only mutations widen the possible control flow type of
|
||||
// a variable). The currentExceptionTarget is the target label for control flows that result from
|
||||
// exceptions. We add all mutation flow nodes as antecedents of this label such that we can analyze them
|
||||
// as possible antecedents of the start of catch or finally blocks. Furthermore, we add the current
|
||||
// control flow to represent exceptions that occur before any mutations.
|
||||
// a variable). The exceptionLabel is the target label for control flows that result from exceptions.
|
||||
// We add all mutation flow nodes as antecedents of this label such that we can analyze them as possible
|
||||
// antecedents of the start of catch or finally blocks. Furthermore, we add the current control flow to
|
||||
// represent exceptions that occur before any mutations.
|
||||
const saveReturnTarget = currentReturnTarget;
|
||||
const saveExceptionTarget = currentExceptionTarget;
|
||||
currentReturnTarget = createBranchLabel();
|
||||
currentExceptionTarget = node.catchClause ? createBranchLabel() : currentReturnTarget;
|
||||
addAntecedent(currentExceptionTarget, currentFlow);
|
||||
const normalExitLabel = createBranchLabel();
|
||||
const returnLabel = createBranchLabel();
|
||||
let exceptionLabel = createBranchLabel();
|
||||
if (node.finallyBlock) {
|
||||
currentReturnTarget = returnLabel;
|
||||
}
|
||||
addAntecedent(exceptionLabel, currentFlow);
|
||||
currentExceptionTarget = exceptionLabel;
|
||||
bind(node.tryBlock);
|
||||
addAntecedent(preFinallyLabel, currentFlow);
|
||||
const flowAfterTry = currentFlow;
|
||||
let flowAfterCatch = unreachableFlow;
|
||||
addAntecedent(normalExitLabel, currentFlow);
|
||||
if (node.catchClause) {
|
||||
// Start of catch clause is the target of exceptions from try block.
|
||||
currentFlow = finishFlowLabel(currentExceptionTarget);
|
||||
currentFlow = finishFlowLabel(exceptionLabel);
|
||||
// The currentExceptionTarget now represents control flows from exceptions in the catch clause.
|
||||
// Effectively, in a try-catch-finally, if an exception occurs in the try block, the catch block
|
||||
// acts like a second try block.
|
||||
currentExceptionTarget = currentReturnTarget;
|
||||
addAntecedent(currentExceptionTarget, currentFlow);
|
||||
exceptionLabel = createBranchLabel();
|
||||
addAntecedent(exceptionLabel, currentFlow);
|
||||
currentExceptionTarget = exceptionLabel;
|
||||
bind(node.catchClause);
|
||||
addAntecedent(preFinallyLabel, currentFlow);
|
||||
flowAfterCatch = currentFlow;
|
||||
addAntecedent(normalExitLabel, currentFlow);
|
||||
}
|
||||
const exceptionTarget = finishFlowLabel(currentExceptionTarget);
|
||||
currentReturnTarget = saveReturnTarget;
|
||||
currentExceptionTarget = saveExceptionTarget;
|
||||
if (node.finallyBlock) {
|
||||
@@ -1250,35 +1255,33 @@ namespace ts {
|
||||
// When analyzing a control flow graph that starts inside a finally block we want to consider all
|
||||
// five possibilities above. However, when analyzing a control flow graph that starts outside (past)
|
||||
// the finally block, we only want to consider the first two (if we're past a finally block then it
|
||||
// must have completed normally). To make this possible, we inject two extra nodes into the control
|
||||
// flow graph: An after-finally with an antecedent of the control flow at the end of the finally
|
||||
// block, and a pre-finally with an antecedent that represents all exceptional control flows. The
|
||||
// 'lock' property of the pre-finally references the after-finally, and the after-finally has a
|
||||
// boolean 'locked' property that we set to true when analyzing a control flow that contained the
|
||||
// the after-finally node. When the lock associated with a pre-finally is locked, the antecedent of
|
||||
// the pre-finally (i.e. the exceptional control flows) are skipped.
|
||||
const preFinallyFlow: PreFinallyFlow = initFlowNode({ flags: FlowFlags.PreFinally, antecedent: exceptionTarget, lock: {} });
|
||||
addAntecedent(preFinallyLabel, preFinallyFlow);
|
||||
currentFlow = finishFlowLabel(preFinallyLabel);
|
||||
// must have completed normally). Likewise, when analyzing a control flow graph from return statements
|
||||
// in try or catch blocks in an IIFE, we only want to consider the third. To make this possible, we
|
||||
// inject a ReduceLabel node into the control flow graph. This node contains an alternate reduced
|
||||
// set of antecedents for the pre-finally label. As control flow analysis passes by a ReduceLabel
|
||||
// node, the pre-finally label is temporarily switched to the reduced antecedent set.
|
||||
const finallyLabel = createBranchLabel();
|
||||
finallyLabel.antecedents = concatenate(concatenate(normalExitLabel.antecedents, exceptionLabel.antecedents), returnLabel.antecedents);
|
||||
currentFlow = finallyLabel;
|
||||
bind(node.finallyBlock);
|
||||
// If the end of the finally block is reachable, but the end of the try and catch blocks are not,
|
||||
// convert the current flow to unreachable. For example, 'try { return 1; } finally { ... }' should
|
||||
// result in an unreachable current control flow.
|
||||
if (!(currentFlow.flags & FlowFlags.Unreachable)) {
|
||||
if ((flowAfterTry.flags & FlowFlags.Unreachable) && (flowAfterCatch.flags & FlowFlags.Unreachable)) {
|
||||
currentFlow = flowAfterTry === reportedUnreachableFlow || flowAfterCatch === reportedUnreachableFlow
|
||||
? reportedUnreachableFlow
|
||||
: unreachableFlow;
|
||||
}
|
||||
if (currentFlow.flags & FlowFlags.Unreachable) {
|
||||
// If the end of the finally block is unreachable, the end of the entire try statement is unreachable.
|
||||
currentFlow = unreachableFlow;
|
||||
}
|
||||
if (!(currentFlow.flags & FlowFlags.Unreachable)) {
|
||||
const afterFinallyFlow: AfterFinallyFlow = initFlowNode({ flags: FlowFlags.AfterFinally, antecedent: currentFlow });
|
||||
preFinallyFlow.lock = afterFinallyFlow;
|
||||
currentFlow = afterFinallyFlow;
|
||||
else {
|
||||
// If we have an IIFE return target and return statements in the try or catch blocks, add a control
|
||||
// flow that goes back through the finally block and back through only the return statements.
|
||||
if (currentReturnTarget && returnLabel.antecedents) {
|
||||
addAntecedent(currentReturnTarget, createReduceLabel(finallyLabel, returnLabel.antecedents, currentFlow));
|
||||
}
|
||||
// If the end of the finally block is reachable, but the end of the try and catch blocks are not,
|
||||
// convert the current flow to unreachable. For example, 'try { return 1; } finally { ... }' should
|
||||
// result in an unreachable current control flow.
|
||||
currentFlow = normalExitLabel.antecedents ? createReduceLabel(finallyLabel, normalExitLabel.antecedents, currentFlow) : unreachableFlow;
|
||||
}
|
||||
}
|
||||
else {
|
||||
currentFlow = finishFlowLabel(preFinallyLabel);
|
||||
currentFlow = finishFlowLabel(normalExitLabel);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19437,16 +19437,12 @@ namespace ts {
|
||||
}
|
||||
|
||||
function isReachableFlowNode(flow: FlowNode) {
|
||||
const result = isReachableFlowNodeWorker(flow, /*skipCacheCheck*/ false);
|
||||
const result = isReachableFlowNodeWorker(flow, /*noCacheCheck*/ false);
|
||||
lastFlowNode = flow;
|
||||
lastFlowNodeReachable = result;
|
||||
return result;
|
||||
}
|
||||
|
||||
function isUnlockedReachableFlowNode(flow: FlowNode) {
|
||||
return !(flow.flags & FlowFlags.PreFinally && (<PreFinallyFlow>flow).lock.locked) && isReachableFlowNodeWorker(flow, /*skipCacheCheck*/ false);
|
||||
}
|
||||
|
||||
function isFalseExpression(expr: Expression): boolean {
|
||||
const node = skipParentheses(expr);
|
||||
return node.kind === SyntaxKind.FalseKeyword || node.kind === SyntaxKind.BinaryExpression && (
|
||||
@@ -19464,11 +19460,11 @@ namespace ts {
|
||||
if (!noCacheCheck) {
|
||||
const id = getFlowNodeId(flow);
|
||||
const reachable = flowNodeReachable[id];
|
||||
return reachable !== undefined ? reachable : (flowNodeReachable[id] = isReachableFlowNodeWorker(flow, /*skipCacheCheck*/ true));
|
||||
return reachable !== undefined ? reachable : (flowNodeReachable[id] = isReachableFlowNodeWorker(flow, /*noCacheCheck*/ true));
|
||||
}
|
||||
noCacheCheck = false;
|
||||
}
|
||||
if (flags & (FlowFlags.Assignment | FlowFlags.Condition | FlowFlags.ArrayMutation | FlowFlags.PreFinally)) {
|
||||
if (flags & (FlowFlags.Assignment | FlowFlags.Condition | FlowFlags.ArrayMutation)) {
|
||||
flow = (<FlowAssignment | FlowCondition | FlowArrayMutation | PreFinallyFlow>flow).antecedent;
|
||||
}
|
||||
else if (flags & FlowFlags.Call) {
|
||||
@@ -19489,7 +19485,7 @@ namespace ts {
|
||||
}
|
||||
else if (flags & FlowFlags.BranchLabel) {
|
||||
// A branching point is reachable if any branch is reachable.
|
||||
return some((<FlowLabel>flow).antecedents, isUnlockedReachableFlowNode);
|
||||
return some((<FlowLabel>flow).antecedents, f => isReachableFlowNodeWorker(f, /*noCacheCheck*/ false));
|
||||
}
|
||||
else if (flags & FlowFlags.LoopLabel) {
|
||||
// A loop is reachable if the control flow path that leads to the top is reachable.
|
||||
@@ -19503,12 +19499,14 @@ namespace ts {
|
||||
}
|
||||
flow = (<FlowSwitchClause>flow).antecedent;
|
||||
}
|
||||
else if (flags & FlowFlags.AfterFinally) {
|
||||
// Cache is unreliable once we start locking nodes
|
||||
else if (flags & FlowFlags.ReduceLabel) {
|
||||
// Cache is unreliable once we start adjusting labels
|
||||
lastFlowNode = undefined;
|
||||
(<AfterFinallyFlow>flow).locked = true;
|
||||
const result = isReachableFlowNodeWorker((<AfterFinallyFlow>flow).antecedent, /*skipCacheCheck*/ false);
|
||||
(<AfterFinallyFlow>flow).locked = false;
|
||||
const target = (<FlowReduceLabel>flow).target;
|
||||
const saveAntecedents = target.antecedents;
|
||||
target.antecedents = (<FlowReduceLabel>flow).antecedents;
|
||||
const result = isReachableFlowNodeWorker((<FlowReduceLabel>flow).antecedent, /*noCacheCheck*/ false);
|
||||
target.antecedents = saveAntecedents;
|
||||
return result;
|
||||
}
|
||||
else {
|
||||
@@ -19572,19 +19570,7 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
let type: FlowType | undefined;
|
||||
if (flags & FlowFlags.AfterFinally) {
|
||||
// block flow edge: finally -> pre-try (for larger explanation check comment in binder.ts - bindTryStatement
|
||||
(<AfterFinallyFlow>flow).locked = true;
|
||||
type = getTypeAtFlowNode((<AfterFinallyFlow>flow).antecedent);
|
||||
(<AfterFinallyFlow>flow).locked = false;
|
||||
}
|
||||
else if (flags & FlowFlags.PreFinally) {
|
||||
// locked pre-finally flows are filtered out in getTypeAtFlowBranchLabel
|
||||
// so here just redirect to antecedent
|
||||
flow = (<PreFinallyFlow>flow).antecedent;
|
||||
continue;
|
||||
}
|
||||
else if (flags & FlowFlags.Assignment) {
|
||||
if (flags & FlowFlags.Assignment) {
|
||||
type = getTypeAtFlowAssignment(<FlowAssignment>flow);
|
||||
if (!type) {
|
||||
flow = (<FlowAssignment>flow).antecedent;
|
||||
@@ -19620,6 +19606,13 @@ namespace ts {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else if (flags & FlowFlags.ReduceLabel) {
|
||||
const target = (<FlowReduceLabel>flow).target;
|
||||
const saveAntecedents = target.antecedents;
|
||||
target.antecedents = (<FlowReduceLabel>flow).antecedents;
|
||||
type = getTypeAtFlowNode((<FlowReduceLabel>flow).antecedent);
|
||||
target.antecedents = saveAntecedents;
|
||||
}
|
||||
else if (flags & FlowFlags.Start) {
|
||||
// Check if we should continue with the control flow of the containing function.
|
||||
const container = (<FlowStart>flow).node;
|
||||
@@ -19831,12 +19824,6 @@ namespace ts {
|
||||
let seenIncomplete = false;
|
||||
let bypassFlow: FlowSwitchClause | undefined;
|
||||
for (const antecedent of flow.antecedents!) {
|
||||
if (antecedent.flags & FlowFlags.PreFinally && (<PreFinallyFlow>antecedent).lock.locked) {
|
||||
// if flow correspond to branch from pre-try to finally and this branch is locked - this means that
|
||||
// we initially have started following the flow outside the finally block.
|
||||
// in this case we should ignore this branch.
|
||||
continue;
|
||||
}
|
||||
if (!bypassFlow && antecedent.flags & FlowFlags.SwitchClause && (<FlowSwitchClause>antecedent).clauseStart === (<FlowSwitchClause>antecedent).clauseEnd) {
|
||||
// The antecedent is the bypass branch of a potentially exhaustive switch statement.
|
||||
bypassFlow = <FlowSwitchClause>antecedent;
|
||||
|
||||
@@ -2770,10 +2770,9 @@ namespace ts {
|
||||
SwitchClause = 1 << 7, // Switch statement clause
|
||||
ArrayMutation = 1 << 8, // Potential array mutation
|
||||
Call = 1 << 9, // Potential assertion call
|
||||
Referenced = 1 << 10, // Referenced as antecedent once
|
||||
Shared = 1 << 11, // Referenced as antecedent more than once
|
||||
PreFinally = 1 << 12, // Injected edge that links pre-finally label and pre-try flow
|
||||
AfterFinally = 1 << 13, // Injected edge that links post-finally flow with the rest of the graph
|
||||
ReduceLabel = 1 << 10, // Temporarily reduce antecedents of label
|
||||
Referenced = 1 << 11, // Referenced as antecedent once
|
||||
Shared = 1 << 12, // Referenced as antecedent more than once
|
||||
|
||||
Label = BranchLabel | LoopLabel,
|
||||
Condition = TrueCondition | FalseCondition,
|
||||
@@ -2853,6 +2852,12 @@ namespace ts {
|
||||
antecedent: FlowNode;
|
||||
}
|
||||
|
||||
export interface FlowReduceLabel extends FlowNodeBase {
|
||||
target: FlowLabel;
|
||||
antecedents: FlowNode[];
|
||||
antecedent: FlowNode;
|
||||
}
|
||||
|
||||
export type FlowType = Type | IncompleteType;
|
||||
|
||||
// Incomplete types occur during control flow analysis of loops. An IncompleteType
|
||||
|
||||
@@ -46,10 +46,9 @@ namespace Debug {
|
||||
readonly SwitchClause: number,
|
||||
readonly ArrayMutation: number,
|
||||
readonly Call: number,
|
||||
readonly ReduceLabel: number,
|
||||
readonly Referenced: number,
|
||||
readonly Shared: number,
|
||||
readonly PreFinally: number,
|
||||
readonly AfterFinally: number,
|
||||
readonly Label: number,
|
||||
readonly Condition: number,
|
||||
};
|
||||
@@ -69,6 +68,7 @@ namespace Debug {
|
||||
| FlowCondition
|
||||
| FlowSwitchClause
|
||||
| FlowArrayMutation
|
||||
| FlowReduceLabel
|
||||
;
|
||||
|
||||
interface FlowNodeBase {
|
||||
@@ -119,6 +119,12 @@ namespace Debug {
|
||||
antecedent: FlowNode;
|
||||
}
|
||||
|
||||
export interface FlowReduceLabel extends FlowNodeBase {
|
||||
target: FlowLabel;
|
||||
antecedents: FlowNode[];
|
||||
antecedent: FlowNode;
|
||||
}
|
||||
|
||||
type FlowFlags = number;
|
||||
let FlowFlags: TypeScriptModule["FlowFlags"];
|
||||
let getSourceFileOfNode: TypeScriptModule["getSourceFileOfNode"];
|
||||
@@ -199,8 +205,7 @@ namespace Debug {
|
||||
FlowFlags.SwitchClause |
|
||||
FlowFlags.ArrayMutation |
|
||||
FlowFlags.Call |
|
||||
FlowFlags.PreFinally |
|
||||
FlowFlags.AfterFinally;
|
||||
FlowFlags.ReduceLabel;
|
||||
|
||||
const hasNodeFlags =
|
||||
FlowFlags.Start |
|
||||
@@ -264,17 +269,14 @@ namespace Debug {
|
||||
if (!graphNode) {
|
||||
links[id] = graphNode = { id, flowNode, edges: [], text: renderFlowNode(flowNode), lane: -1, endLane: -1, level: -1 };
|
||||
nodes.push(graphNode);
|
||||
if (!(flowNode.flags & FlowFlags.PreFinally)) {
|
||||
if (hasAntecedents(flowNode)) {
|
||||
|
||||
for (const antecedent of flowNode.antecedents) {
|
||||
buildGraphEdge(graphNode, antecedent);
|
||||
}
|
||||
}
|
||||
else if (hasAntecedent(flowNode)) {
|
||||
buildGraphEdge(graphNode, flowNode.antecedent);
|
||||
if (hasAntecedents(flowNode)) {
|
||||
for (const antecedent of flowNode.antecedents) {
|
||||
buildGraphEdge(graphNode, antecedent);
|
||||
}
|
||||
}
|
||||
else if (hasAntecedent(flowNode)) {
|
||||
buildGraphEdge(graphNode, flowNode.antecedent);
|
||||
}
|
||||
}
|
||||
return graphNode;
|
||||
}
|
||||
@@ -341,8 +343,7 @@ namespace Debug {
|
||||
if (flags & FlowFlags.SwitchClause) return "SwitchClause";
|
||||
if (flags & FlowFlags.ArrayMutation) return "ArrayMutation";
|
||||
if (flags & FlowFlags.Call) return "Call";
|
||||
if (flags & FlowFlags.PreFinally) return "PreFinally";
|
||||
if (flags & FlowFlags.AfterFinally) return "AfterFinally";
|
||||
if (flags & FlowFlags.ReduceLabel) return "ReduceLabel";
|
||||
if (flags & FlowFlags.Unreachable) return "Unreachable";
|
||||
throw new Error();
|
||||
}
|
||||
|
||||
@@ -1726,10 +1726,9 @@ declare namespace ts {
|
||||
SwitchClause = 128,
|
||||
ArrayMutation = 256,
|
||||
Call = 512,
|
||||
Referenced = 1024,
|
||||
Shared = 2048,
|
||||
PreFinally = 4096,
|
||||
AfterFinally = 8192,
|
||||
ReduceLabel = 1024,
|
||||
Referenced = 2048,
|
||||
Shared = 4096,
|
||||
Label = 12,
|
||||
Condition = 96
|
||||
}
|
||||
@@ -1776,6 +1775,11 @@ declare namespace ts {
|
||||
node: CallExpression | BinaryExpression;
|
||||
antecedent: FlowNode;
|
||||
}
|
||||
export interface FlowReduceLabel extends FlowNodeBase {
|
||||
target: FlowLabel;
|
||||
antecedents: FlowNode[];
|
||||
antecedent: FlowNode;
|
||||
}
|
||||
export type FlowType = Type | IncompleteType;
|
||||
export interface IncompleteType {
|
||||
flags: TypeFlags;
|
||||
|
||||
12
tests/baselines/reference/api/typescript.d.ts
vendored
12
tests/baselines/reference/api/typescript.d.ts
vendored
@@ -1726,10 +1726,9 @@ declare namespace ts {
|
||||
SwitchClause = 128,
|
||||
ArrayMutation = 256,
|
||||
Call = 512,
|
||||
Referenced = 1024,
|
||||
Shared = 2048,
|
||||
PreFinally = 4096,
|
||||
AfterFinally = 8192,
|
||||
ReduceLabel = 1024,
|
||||
Referenced = 2048,
|
||||
Shared = 4096,
|
||||
Label = 12,
|
||||
Condition = 96
|
||||
}
|
||||
@@ -1776,6 +1775,11 @@ declare namespace ts {
|
||||
node: CallExpression | BinaryExpression;
|
||||
antecedent: FlowNode;
|
||||
}
|
||||
export interface FlowReduceLabel extends FlowNodeBase {
|
||||
target: FlowLabel;
|
||||
antecedents: FlowNode[];
|
||||
antecedent: FlowNode;
|
||||
}
|
||||
export type FlowType = Type | IncompleteType;
|
||||
export interface IncompleteType {
|
||||
flags: TypeFlags;
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
tests/cases/compiler/tryCatchFinallyControlFlow.ts(105,5): error TS7027: Unreachable code detected.
|
||||
tests/cases/compiler/tryCatchFinallyControlFlow.ts(118,9): error TS7027: Unreachable code detected.
|
||||
tests/cases/compiler/tryCatchFinallyControlFlow.ts(218,13): error TS7027: Unreachable code detected.
|
||||
tests/cases/compiler/tryCatchFinallyControlFlow.ts(220,9): error TS7027: Unreachable code detected.
|
||||
tests/cases/compiler/tryCatchFinallyControlFlow.ts(255,9): error TS7027: Unreachable code detected.
|
||||
|
||||
|
||||
==== tests/cases/compiler/tryCatchFinallyControlFlow.ts (1 errors) ====
|
||||
==== tests/cases/compiler/tryCatchFinallyControlFlow.ts (5 errors) ====
|
||||
// Repro from #34797
|
||||
|
||||
function f1() {
|
||||
@@ -111,6 +115,131 @@ tests/cases/compiler/tryCatchFinallyControlFlow.ts(105,5): error TS7027: Unreach
|
||||
!!! error TS7027: Unreachable code detected.
|
||||
}
|
||||
|
||||
function f8() {
|
||||
let x: 0 | 1 = 0;
|
||||
(() => {
|
||||
try {
|
||||
x = 1;
|
||||
return;
|
||||
}
|
||||
finally {
|
||||
x; // 0 | 1
|
||||
}
|
||||
x; // Unreachable
|
||||
~~
|
||||
!!! error TS7027: Unreachable code detected.
|
||||
})();
|
||||
x; // 1
|
||||
}
|
||||
|
||||
function f9() {
|
||||
let x: 0 | 1 | 2 = 0;
|
||||
(() => {
|
||||
try {
|
||||
if (!!true) {
|
||||
x = 1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
finally {
|
||||
x; // 0 | 1
|
||||
}
|
||||
x; // 0
|
||||
x = 2;
|
||||
})();
|
||||
x; // 1 | 2
|
||||
}
|
||||
|
||||
function f10() {
|
||||
let x: 0 | 1 | 2 | 3 = 0;
|
||||
(() => {
|
||||
try {
|
||||
x = 1;
|
||||
return;
|
||||
}
|
||||
catch (e) {
|
||||
x = 2;
|
||||
}
|
||||
finally {
|
||||
x; // 0 | 1 | 2
|
||||
}
|
||||
x; // 2
|
||||
x = 3;
|
||||
})();
|
||||
x; // 1 | 3
|
||||
}
|
||||
|
||||
function f11() {
|
||||
let x: 0 | 1 | 2 | 3 | 4 | 5 = 0;
|
||||
(() => {
|
||||
try {
|
||||
if (!!true) {
|
||||
x = 1;
|
||||
return;
|
||||
}
|
||||
if (!!true) {
|
||||
x = 2;
|
||||
throw 0;
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
x; // 0 | 1 | 2
|
||||
x = 3;
|
||||
}
|
||||
finally {
|
||||
x; // 0 | 1 | 2 | 3
|
||||
if (!!true) {
|
||||
x = 4;
|
||||
}
|
||||
}
|
||||
x; // 0 | 3 | 4
|
||||
x = 5;
|
||||
})();
|
||||
x; // 1 | 4 | 5
|
||||
}
|
||||
|
||||
function f12() {
|
||||
let x: 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 = 0;
|
||||
(() => {
|
||||
try {
|
||||
if (!!true) {
|
||||
x = 1;
|
||||
return;
|
||||
}
|
||||
if (!!true) {
|
||||
x = 2;
|
||||
throw 0;
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
x; // 0 | 1 | 2
|
||||
x = 3;
|
||||
}
|
||||
finally {
|
||||
x; // 0 | 1 | 2 | 3
|
||||
if (!!true) {
|
||||
x = 4;
|
||||
return;
|
||||
}
|
||||
if (!!true) {
|
||||
x = 5;
|
||||
return;
|
||||
}
|
||||
x = 6;
|
||||
return;
|
||||
x; // unreachable
|
||||
~~
|
||||
!!! error TS7027: Unreachable code detected.
|
||||
}
|
||||
x; // unreachable
|
||||
~~~~~~~~~~~~~~~~~
|
||||
x = 7; // no effect
|
||||
~~~~~~~~~~~~~~
|
||||
!!! error TS7027: Unreachable code detected.
|
||||
})();
|
||||
x; // 4 | 5 | 6
|
||||
}
|
||||
|
||||
// Repro from #35644
|
||||
|
||||
const main = () => {
|
||||
@@ -129,4 +258,21 @@ tests/cases/compiler/tryCatchFinallyControlFlow.ts(105,5): error TS7027: Unreach
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Repro from #36828
|
||||
|
||||
function t1() {
|
||||
const x = (() => {
|
||||
try {
|
||||
return 'x';
|
||||
}
|
||||
catch (e) {
|
||||
return null;
|
||||
}
|
||||
x; // Unreachable
|
||||
~~
|
||||
!!! error TS7027: Unreachable code detected.
|
||||
})();
|
||||
x; // Reachable
|
||||
}
|
||||
|
||||
@@ -106,6 +106,124 @@ function f7() {
|
||||
x; // Unreachable
|
||||
}
|
||||
|
||||
function f8() {
|
||||
let x: 0 | 1 = 0;
|
||||
(() => {
|
||||
try {
|
||||
x = 1;
|
||||
return;
|
||||
}
|
||||
finally {
|
||||
x; // 0 | 1
|
||||
}
|
||||
x; // Unreachable
|
||||
})();
|
||||
x; // 1
|
||||
}
|
||||
|
||||
function f9() {
|
||||
let x: 0 | 1 | 2 = 0;
|
||||
(() => {
|
||||
try {
|
||||
if (!!true) {
|
||||
x = 1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
finally {
|
||||
x; // 0 | 1
|
||||
}
|
||||
x; // 0
|
||||
x = 2;
|
||||
})();
|
||||
x; // 1 | 2
|
||||
}
|
||||
|
||||
function f10() {
|
||||
let x: 0 | 1 | 2 | 3 = 0;
|
||||
(() => {
|
||||
try {
|
||||
x = 1;
|
||||
return;
|
||||
}
|
||||
catch (e) {
|
||||
x = 2;
|
||||
}
|
||||
finally {
|
||||
x; // 0 | 1 | 2
|
||||
}
|
||||
x; // 2
|
||||
x = 3;
|
||||
})();
|
||||
x; // 1 | 3
|
||||
}
|
||||
|
||||
function f11() {
|
||||
let x: 0 | 1 | 2 | 3 | 4 | 5 = 0;
|
||||
(() => {
|
||||
try {
|
||||
if (!!true) {
|
||||
x = 1;
|
||||
return;
|
||||
}
|
||||
if (!!true) {
|
||||
x = 2;
|
||||
throw 0;
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
x; // 0 | 1 | 2
|
||||
x = 3;
|
||||
}
|
||||
finally {
|
||||
x; // 0 | 1 | 2 | 3
|
||||
if (!!true) {
|
||||
x = 4;
|
||||
}
|
||||
}
|
||||
x; // 0 | 3 | 4
|
||||
x = 5;
|
||||
})();
|
||||
x; // 1 | 4 | 5
|
||||
}
|
||||
|
||||
function f12() {
|
||||
let x: 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 = 0;
|
||||
(() => {
|
||||
try {
|
||||
if (!!true) {
|
||||
x = 1;
|
||||
return;
|
||||
}
|
||||
if (!!true) {
|
||||
x = 2;
|
||||
throw 0;
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
x; // 0 | 1 | 2
|
||||
x = 3;
|
||||
}
|
||||
finally {
|
||||
x; // 0 | 1 | 2 | 3
|
||||
if (!!true) {
|
||||
x = 4;
|
||||
return;
|
||||
}
|
||||
if (!!true) {
|
||||
x = 5;
|
||||
return;
|
||||
}
|
||||
x = 6;
|
||||
return;
|
||||
x; // unreachable
|
||||
}
|
||||
x; // unreachable
|
||||
x = 7; // no effect
|
||||
})();
|
||||
x; // 4 | 5 | 6
|
||||
}
|
||||
|
||||
// Repro from #35644
|
||||
|
||||
const main = () => {
|
||||
@@ -124,6 +242,21 @@ const main = () => {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Repro from #36828
|
||||
|
||||
function t1() {
|
||||
const x = (() => {
|
||||
try {
|
||||
return 'x';
|
||||
}
|
||||
catch (e) {
|
||||
return null;
|
||||
}
|
||||
x; // Unreachable
|
||||
})();
|
||||
x; // Reachable
|
||||
}
|
||||
|
||||
|
||||
//// [tryCatchFinallyControlFlow.js]
|
||||
@@ -227,6 +360,119 @@ function f7() {
|
||||
}
|
||||
x; // Unreachable
|
||||
}
|
||||
function f8() {
|
||||
var x = 0;
|
||||
(function () {
|
||||
try {
|
||||
x = 1;
|
||||
return;
|
||||
}
|
||||
finally {
|
||||
x; // 0 | 1
|
||||
}
|
||||
x; // Unreachable
|
||||
})();
|
||||
x; // 1
|
||||
}
|
||||
function f9() {
|
||||
var x = 0;
|
||||
(function () {
|
||||
try {
|
||||
if (!!true) {
|
||||
x = 1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
finally {
|
||||
x; // 0 | 1
|
||||
}
|
||||
x; // 0
|
||||
x = 2;
|
||||
})();
|
||||
x; // 1 | 2
|
||||
}
|
||||
function f10() {
|
||||
var x = 0;
|
||||
(function () {
|
||||
try {
|
||||
x = 1;
|
||||
return;
|
||||
}
|
||||
catch (e) {
|
||||
x = 2;
|
||||
}
|
||||
finally {
|
||||
x; // 0 | 1 | 2
|
||||
}
|
||||
x; // 2
|
||||
x = 3;
|
||||
})();
|
||||
x; // 1 | 3
|
||||
}
|
||||
function f11() {
|
||||
var x = 0;
|
||||
(function () {
|
||||
try {
|
||||
if (!!true) {
|
||||
x = 1;
|
||||
return;
|
||||
}
|
||||
if (!!true) {
|
||||
x = 2;
|
||||
throw 0;
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
x; // 0 | 1 | 2
|
||||
x = 3;
|
||||
}
|
||||
finally {
|
||||
x; // 0 | 1 | 2 | 3
|
||||
if (!!true) {
|
||||
x = 4;
|
||||
}
|
||||
}
|
||||
x; // 0 | 3 | 4
|
||||
x = 5;
|
||||
})();
|
||||
x; // 1 | 4 | 5
|
||||
}
|
||||
function f12() {
|
||||
var x = 0;
|
||||
(function () {
|
||||
try {
|
||||
if (!!true) {
|
||||
x = 1;
|
||||
return;
|
||||
}
|
||||
if (!!true) {
|
||||
x = 2;
|
||||
throw 0;
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
x; // 0 | 1 | 2
|
||||
x = 3;
|
||||
}
|
||||
finally {
|
||||
x; // 0 | 1 | 2 | 3
|
||||
if (!!true) {
|
||||
x = 4;
|
||||
return;
|
||||
}
|
||||
if (!!true) {
|
||||
x = 5;
|
||||
return;
|
||||
}
|
||||
x = 6;
|
||||
return;
|
||||
x; // unreachable
|
||||
}
|
||||
x; // unreachable
|
||||
x = 7; // no effect
|
||||
})();
|
||||
x; // 4 | 5 | 6
|
||||
}
|
||||
// Repro from #35644
|
||||
var main = function () {
|
||||
var hoge = undefined;
|
||||
@@ -244,3 +490,16 @@ var main = function () {
|
||||
return;
|
||||
}
|
||||
};
|
||||
// Repro from #36828
|
||||
function t1() {
|
||||
var x = (function () {
|
||||
try {
|
||||
return 'x';
|
||||
}
|
||||
catch (e) {
|
||||
return null;
|
||||
}
|
||||
x; // Unreachable
|
||||
})();
|
||||
x; // Reachable
|
||||
}
|
||||
|
||||
@@ -187,18 +187,221 @@ function f7() {
|
||||
>x : Symbol(x, Decl(tryCatchFinallyControlFlow.ts, 92, 7))
|
||||
}
|
||||
|
||||
function f8() {
|
||||
>f8 : Symbol(f8, Decl(tryCatchFinallyControlFlow.ts, 105, 1))
|
||||
|
||||
let x: 0 | 1 = 0;
|
||||
>x : Symbol(x, Decl(tryCatchFinallyControlFlow.ts, 108, 7))
|
||||
|
||||
(() => {
|
||||
try {
|
||||
x = 1;
|
||||
>x : Symbol(x, Decl(tryCatchFinallyControlFlow.ts, 108, 7))
|
||||
|
||||
return;
|
||||
}
|
||||
finally {
|
||||
x; // 0 | 1
|
||||
>x : Symbol(x, Decl(tryCatchFinallyControlFlow.ts, 108, 7))
|
||||
}
|
||||
x; // Unreachable
|
||||
>x : Symbol(x, Decl(tryCatchFinallyControlFlow.ts, 108, 7))
|
||||
|
||||
})();
|
||||
x; // 1
|
||||
>x : Symbol(x, Decl(tryCatchFinallyControlFlow.ts, 108, 7))
|
||||
}
|
||||
|
||||
function f9() {
|
||||
>f9 : Symbol(f9, Decl(tryCatchFinallyControlFlow.ts, 120, 1))
|
||||
|
||||
let x: 0 | 1 | 2 = 0;
|
||||
>x : Symbol(x, Decl(tryCatchFinallyControlFlow.ts, 123, 7))
|
||||
|
||||
(() => {
|
||||
try {
|
||||
if (!!true) {
|
||||
x = 1;
|
||||
>x : Symbol(x, Decl(tryCatchFinallyControlFlow.ts, 123, 7))
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
finally {
|
||||
x; // 0 | 1
|
||||
>x : Symbol(x, Decl(tryCatchFinallyControlFlow.ts, 123, 7))
|
||||
}
|
||||
x; // 0
|
||||
>x : Symbol(x, Decl(tryCatchFinallyControlFlow.ts, 123, 7))
|
||||
|
||||
x = 2;
|
||||
>x : Symbol(x, Decl(tryCatchFinallyControlFlow.ts, 123, 7))
|
||||
|
||||
})();
|
||||
x; // 1 | 2
|
||||
>x : Symbol(x, Decl(tryCatchFinallyControlFlow.ts, 123, 7))
|
||||
}
|
||||
|
||||
function f10() {
|
||||
>f10 : Symbol(f10, Decl(tryCatchFinallyControlFlow.ts, 138, 1))
|
||||
|
||||
let x: 0 | 1 | 2 | 3 = 0;
|
||||
>x : Symbol(x, Decl(tryCatchFinallyControlFlow.ts, 141, 7))
|
||||
|
||||
(() => {
|
||||
try {
|
||||
x = 1;
|
||||
>x : Symbol(x, Decl(tryCatchFinallyControlFlow.ts, 141, 7))
|
||||
|
||||
return;
|
||||
}
|
||||
catch (e) {
|
||||
>e : Symbol(e, Decl(tryCatchFinallyControlFlow.ts, 147, 15))
|
||||
|
||||
x = 2;
|
||||
>x : Symbol(x, Decl(tryCatchFinallyControlFlow.ts, 141, 7))
|
||||
}
|
||||
finally {
|
||||
x; // 0 | 1 | 2
|
||||
>x : Symbol(x, Decl(tryCatchFinallyControlFlow.ts, 141, 7))
|
||||
}
|
||||
x; // 2
|
||||
>x : Symbol(x, Decl(tryCatchFinallyControlFlow.ts, 141, 7))
|
||||
|
||||
x = 3;
|
||||
>x : Symbol(x, Decl(tryCatchFinallyControlFlow.ts, 141, 7))
|
||||
|
||||
})();
|
||||
x; // 1 | 3
|
||||
>x : Symbol(x, Decl(tryCatchFinallyControlFlow.ts, 141, 7))
|
||||
}
|
||||
|
||||
function f11() {
|
||||
>f11 : Symbol(f11, Decl(tryCatchFinallyControlFlow.ts, 157, 1))
|
||||
|
||||
let x: 0 | 1 | 2 | 3 | 4 | 5 = 0;
|
||||
>x : Symbol(x, Decl(tryCatchFinallyControlFlow.ts, 160, 7))
|
||||
|
||||
(() => {
|
||||
try {
|
||||
if (!!true) {
|
||||
x = 1;
|
||||
>x : Symbol(x, Decl(tryCatchFinallyControlFlow.ts, 160, 7))
|
||||
|
||||
return;
|
||||
}
|
||||
if (!!true) {
|
||||
x = 2;
|
||||
>x : Symbol(x, Decl(tryCatchFinallyControlFlow.ts, 160, 7))
|
||||
|
||||
throw 0;
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
>e : Symbol(e, Decl(tryCatchFinallyControlFlow.ts, 172, 15))
|
||||
|
||||
x; // 0 | 1 | 2
|
||||
>x : Symbol(x, Decl(tryCatchFinallyControlFlow.ts, 160, 7))
|
||||
|
||||
x = 3;
|
||||
>x : Symbol(x, Decl(tryCatchFinallyControlFlow.ts, 160, 7))
|
||||
}
|
||||
finally {
|
||||
x; // 0 | 1 | 2 | 3
|
||||
>x : Symbol(x, Decl(tryCatchFinallyControlFlow.ts, 160, 7))
|
||||
|
||||
if (!!true) {
|
||||
x = 4;
|
||||
>x : Symbol(x, Decl(tryCatchFinallyControlFlow.ts, 160, 7))
|
||||
}
|
||||
}
|
||||
x; // 0 | 3 | 4
|
||||
>x : Symbol(x, Decl(tryCatchFinallyControlFlow.ts, 160, 7))
|
||||
|
||||
x = 5;
|
||||
>x : Symbol(x, Decl(tryCatchFinallyControlFlow.ts, 160, 7))
|
||||
|
||||
})();
|
||||
x; // 1 | 4 | 5
|
||||
>x : Symbol(x, Decl(tryCatchFinallyControlFlow.ts, 160, 7))
|
||||
}
|
||||
|
||||
function f12() {
|
||||
>f12 : Symbol(f12, Decl(tryCatchFinallyControlFlow.ts, 186, 1))
|
||||
|
||||
let x: 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 = 0;
|
||||
>x : Symbol(x, Decl(tryCatchFinallyControlFlow.ts, 189, 7))
|
||||
|
||||
(() => {
|
||||
try {
|
||||
if (!!true) {
|
||||
x = 1;
|
||||
>x : Symbol(x, Decl(tryCatchFinallyControlFlow.ts, 189, 7))
|
||||
|
||||
return;
|
||||
}
|
||||
if (!!true) {
|
||||
x = 2;
|
||||
>x : Symbol(x, Decl(tryCatchFinallyControlFlow.ts, 189, 7))
|
||||
|
||||
throw 0;
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
>e : Symbol(e, Decl(tryCatchFinallyControlFlow.ts, 201, 15))
|
||||
|
||||
x; // 0 | 1 | 2
|
||||
>x : Symbol(x, Decl(tryCatchFinallyControlFlow.ts, 189, 7))
|
||||
|
||||
x = 3;
|
||||
>x : Symbol(x, Decl(tryCatchFinallyControlFlow.ts, 189, 7))
|
||||
}
|
||||
finally {
|
||||
x; // 0 | 1 | 2 | 3
|
||||
>x : Symbol(x, Decl(tryCatchFinallyControlFlow.ts, 189, 7))
|
||||
|
||||
if (!!true) {
|
||||
x = 4;
|
||||
>x : Symbol(x, Decl(tryCatchFinallyControlFlow.ts, 189, 7))
|
||||
|
||||
return;
|
||||
}
|
||||
if (!!true) {
|
||||
x = 5;
|
||||
>x : Symbol(x, Decl(tryCatchFinallyControlFlow.ts, 189, 7))
|
||||
|
||||
return;
|
||||
}
|
||||
x = 6;
|
||||
>x : Symbol(x, Decl(tryCatchFinallyControlFlow.ts, 189, 7))
|
||||
|
||||
return;
|
||||
x; // unreachable
|
||||
>x : Symbol(x, Decl(tryCatchFinallyControlFlow.ts, 189, 7))
|
||||
}
|
||||
x; // unreachable
|
||||
>x : Symbol(x, Decl(tryCatchFinallyControlFlow.ts, 189, 7))
|
||||
|
||||
x = 7; // no effect
|
||||
>x : Symbol(x, Decl(tryCatchFinallyControlFlow.ts, 189, 7))
|
||||
|
||||
})();
|
||||
x; // 4 | 5 | 6
|
||||
>x : Symbol(x, Decl(tryCatchFinallyControlFlow.ts, 189, 7))
|
||||
}
|
||||
|
||||
// Repro from #35644
|
||||
|
||||
const main = () => {
|
||||
>main : Symbol(main, Decl(tryCatchFinallyControlFlow.ts, 109, 5))
|
||||
>main : Symbol(main, Decl(tryCatchFinallyControlFlow.ts, 227, 5))
|
||||
|
||||
let hoge: string | undefined = undefined;
|
||||
>hoge : Symbol(hoge, Decl(tryCatchFinallyControlFlow.ts, 110, 7))
|
||||
>hoge : Symbol(hoge, Decl(tryCatchFinallyControlFlow.ts, 228, 7))
|
||||
>undefined : Symbol(undefined)
|
||||
|
||||
try {
|
||||
hoge = 'hoge!';
|
||||
>hoge : Symbol(hoge, Decl(tryCatchFinallyControlFlow.ts, 110, 7))
|
||||
>hoge : Symbol(hoge, Decl(tryCatchFinallyControlFlow.ts, 228, 7))
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -207,14 +410,38 @@ const main = () => {
|
||||
}
|
||||
finally {
|
||||
if (hoge) {
|
||||
>hoge : Symbol(hoge, Decl(tryCatchFinallyControlFlow.ts, 110, 7))
|
||||
>hoge : Symbol(hoge, Decl(tryCatchFinallyControlFlow.ts, 228, 7))
|
||||
|
||||
hoge.length;
|
||||
>hoge.length : Symbol(String.length, Decl(lib.es5.d.ts, --, --))
|
||||
>hoge : Symbol(hoge, Decl(tryCatchFinallyControlFlow.ts, 110, 7))
|
||||
>hoge : Symbol(hoge, Decl(tryCatchFinallyControlFlow.ts, 228, 7))
|
||||
>length : Symbol(String.length, Decl(lib.es5.d.ts, --, --))
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Repro from #36828
|
||||
|
||||
function t1() {
|
||||
>t1 : Symbol(t1, Decl(tryCatchFinallyControlFlow.ts, 242, 1))
|
||||
|
||||
const x = (() => {
|
||||
>x : Symbol(x, Decl(tryCatchFinallyControlFlow.ts, 247, 9))
|
||||
|
||||
try {
|
||||
return 'x';
|
||||
}
|
||||
catch (e) {
|
||||
>e : Symbol(e, Decl(tryCatchFinallyControlFlow.ts, 251, 15))
|
||||
|
||||
return null;
|
||||
}
|
||||
x; // Unreachable
|
||||
>x : Symbol(x, Decl(tryCatchFinallyControlFlow.ts, 247, 9))
|
||||
|
||||
})();
|
||||
x; // Reachable
|
||||
>x : Symbol(x, Decl(tryCatchFinallyControlFlow.ts, 247, 9))
|
||||
}
|
||||
|
||||
|
||||
@@ -228,6 +228,304 @@ function f7() {
|
||||
>x : 0 | 1 | 2 | 3
|
||||
}
|
||||
|
||||
function f8() {
|
||||
>f8 : () => void
|
||||
|
||||
let x: 0 | 1 = 0;
|
||||
>x : 0 | 1
|
||||
>0 : 0
|
||||
|
||||
(() => {
|
||||
>(() => { try { x = 1; return; } finally { x; // 0 | 1 } x; // Unreachable })() : void
|
||||
>(() => { try { x = 1; return; } finally { x; // 0 | 1 } x; // Unreachable }) : () => void
|
||||
>() => { try { x = 1; return; } finally { x; // 0 | 1 } x; // Unreachable } : () => void
|
||||
|
||||
try {
|
||||
x = 1;
|
||||
>x = 1 : 1
|
||||
>x : 0 | 1
|
||||
>1 : 1
|
||||
|
||||
return;
|
||||
}
|
||||
finally {
|
||||
x; // 0 | 1
|
||||
>x : 0 | 1
|
||||
}
|
||||
x; // Unreachable
|
||||
>x : 0 | 1
|
||||
|
||||
})();
|
||||
x; // 1
|
||||
>x : 1
|
||||
}
|
||||
|
||||
function f9() {
|
||||
>f9 : () => void
|
||||
|
||||
let x: 0 | 1 | 2 = 0;
|
||||
>x : 0 | 1 | 2
|
||||
>0 : 0
|
||||
|
||||
(() => {
|
||||
>(() => { try { if (!!true) { x = 1; return; } } finally { x; // 0 | 1 } x; // 0 x = 2; })() : void
|
||||
>(() => { try { if (!!true) { x = 1; return; } } finally { x; // 0 | 1 } x; // 0 x = 2; }) : () => void
|
||||
>() => { try { if (!!true) { x = 1; return; } } finally { x; // 0 | 1 } x; // 0 x = 2; } : () => void
|
||||
|
||||
try {
|
||||
if (!!true) {
|
||||
>!!true : true
|
||||
>!true : false
|
||||
>true : true
|
||||
|
||||
x = 1;
|
||||
>x = 1 : 1
|
||||
>x : 0 | 1 | 2
|
||||
>1 : 1
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
finally {
|
||||
x; // 0 | 1
|
||||
>x : 0 | 1
|
||||
}
|
||||
x; // 0
|
||||
>x : 0
|
||||
|
||||
x = 2;
|
||||
>x = 2 : 2
|
||||
>x : 0 | 1 | 2
|
||||
>2 : 2
|
||||
|
||||
})();
|
||||
x; // 1 | 2
|
||||
>x : 1 | 2
|
||||
}
|
||||
|
||||
function f10() {
|
||||
>f10 : () => void
|
||||
|
||||
let x: 0 | 1 | 2 | 3 = 0;
|
||||
>x : 0 | 1 | 2 | 3
|
||||
>0 : 0
|
||||
|
||||
(() => {
|
||||
>(() => { try { x = 1; return; } catch (e) { x = 2; } finally { x; // 0 | 1 | 2 } x; // 2 x = 3; })() : void
|
||||
>(() => { try { x = 1; return; } catch (e) { x = 2; } finally { x; // 0 | 1 | 2 } x; // 2 x = 3; }) : () => void
|
||||
>() => { try { x = 1; return; } catch (e) { x = 2; } finally { x; // 0 | 1 | 2 } x; // 2 x = 3; } : () => void
|
||||
|
||||
try {
|
||||
x = 1;
|
||||
>x = 1 : 1
|
||||
>x : 0 | 1 | 2 | 3
|
||||
>1 : 1
|
||||
|
||||
return;
|
||||
}
|
||||
catch (e) {
|
||||
>e : any
|
||||
|
||||
x = 2;
|
||||
>x = 2 : 2
|
||||
>x : 0 | 1 | 2 | 3
|
||||
>2 : 2
|
||||
}
|
||||
finally {
|
||||
x; // 0 | 1 | 2
|
||||
>x : 0 | 1 | 2
|
||||
}
|
||||
x; // 2
|
||||
>x : 2
|
||||
|
||||
x = 3;
|
||||
>x = 3 : 3
|
||||
>x : 0 | 1 | 2 | 3
|
||||
>3 : 3
|
||||
|
||||
})();
|
||||
x; // 1 | 3
|
||||
>x : 1 | 3
|
||||
}
|
||||
|
||||
function f11() {
|
||||
>f11 : () => void
|
||||
|
||||
let x: 0 | 1 | 2 | 3 | 4 | 5 = 0;
|
||||
>x : 0 | 1 | 2 | 3 | 4 | 5
|
||||
>0 : 0
|
||||
|
||||
(() => {
|
||||
>(() => { try { if (!!true) { x = 1; return; } if (!!true) { x = 2; throw 0; } } catch (e) { x; // 0 | 1 | 2 x = 3; } finally { x; // 0 | 1 | 2 | 3 if (!!true) { x = 4; } } x; // 0 | 3 | 4 x = 5; })() : void
|
||||
>(() => { try { if (!!true) { x = 1; return; } if (!!true) { x = 2; throw 0; } } catch (e) { x; // 0 | 1 | 2 x = 3; } finally { x; // 0 | 1 | 2 | 3 if (!!true) { x = 4; } } x; // 0 | 3 | 4 x = 5; }) : () => void
|
||||
>() => { try { if (!!true) { x = 1; return; } if (!!true) { x = 2; throw 0; } } catch (e) { x; // 0 | 1 | 2 x = 3; } finally { x; // 0 | 1 | 2 | 3 if (!!true) { x = 4; } } x; // 0 | 3 | 4 x = 5; } : () => void
|
||||
|
||||
try {
|
||||
if (!!true) {
|
||||
>!!true : true
|
||||
>!true : false
|
||||
>true : true
|
||||
|
||||
x = 1;
|
||||
>x = 1 : 1
|
||||
>x : 0 | 1 | 2 | 3 | 4 | 5
|
||||
>1 : 1
|
||||
|
||||
return;
|
||||
}
|
||||
if (!!true) {
|
||||
>!!true : true
|
||||
>!true : false
|
||||
>true : true
|
||||
|
||||
x = 2;
|
||||
>x = 2 : 2
|
||||
>x : 0 | 1 | 2 | 3 | 4 | 5
|
||||
>2 : 2
|
||||
|
||||
throw 0;
|
||||
>0 : 0
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
>e : any
|
||||
|
||||
x; // 0 | 1 | 2
|
||||
>x : 0 | 1 | 2
|
||||
|
||||
x = 3;
|
||||
>x = 3 : 3
|
||||
>x : 0 | 1 | 2 | 3 | 4 | 5
|
||||
>3 : 3
|
||||
}
|
||||
finally {
|
||||
x; // 0 | 1 | 2 | 3
|
||||
>x : 0 | 1 | 2 | 3
|
||||
|
||||
if (!!true) {
|
||||
>!!true : true
|
||||
>!true : false
|
||||
>true : true
|
||||
|
||||
x = 4;
|
||||
>x = 4 : 4
|
||||
>x : 0 | 1 | 2 | 3 | 4 | 5
|
||||
>4 : 4
|
||||
}
|
||||
}
|
||||
x; // 0 | 3 | 4
|
||||
>x : 0 | 3 | 4
|
||||
|
||||
x = 5;
|
||||
>x = 5 : 5
|
||||
>x : 0 | 1 | 2 | 3 | 4 | 5
|
||||
>5 : 5
|
||||
|
||||
})();
|
||||
x; // 1 | 4 | 5
|
||||
>x : 1 | 4 | 5
|
||||
}
|
||||
|
||||
function f12() {
|
||||
>f12 : () => void
|
||||
|
||||
let x: 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 = 0;
|
||||
>x : 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7
|
||||
>0 : 0
|
||||
|
||||
(() => {
|
||||
>(() => { try { if (!!true) { x = 1; return; } if (!!true) { x = 2; throw 0; } } catch (e) { x; // 0 | 1 | 2 x = 3; } finally { x; // 0 | 1 | 2 | 3 if (!!true) { x = 4; return; } if (!!true) { x = 5; return; } x = 6; return; x; // unreachable } x; // unreachable x = 7; // no effect })() : void
|
||||
>(() => { try { if (!!true) { x = 1; return; } if (!!true) { x = 2; throw 0; } } catch (e) { x; // 0 | 1 | 2 x = 3; } finally { x; // 0 | 1 | 2 | 3 if (!!true) { x = 4; return; } if (!!true) { x = 5; return; } x = 6; return; x; // unreachable } x; // unreachable x = 7; // no effect }) : () => void
|
||||
>() => { try { if (!!true) { x = 1; return; } if (!!true) { x = 2; throw 0; } } catch (e) { x; // 0 | 1 | 2 x = 3; } finally { x; // 0 | 1 | 2 | 3 if (!!true) { x = 4; return; } if (!!true) { x = 5; return; } x = 6; return; x; // unreachable } x; // unreachable x = 7; // no effect } : () => void
|
||||
|
||||
try {
|
||||
if (!!true) {
|
||||
>!!true : true
|
||||
>!true : false
|
||||
>true : true
|
||||
|
||||
x = 1;
|
||||
>x = 1 : 1
|
||||
>x : 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7
|
||||
>1 : 1
|
||||
|
||||
return;
|
||||
}
|
||||
if (!!true) {
|
||||
>!!true : true
|
||||
>!true : false
|
||||
>true : true
|
||||
|
||||
x = 2;
|
||||
>x = 2 : 2
|
||||
>x : 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7
|
||||
>2 : 2
|
||||
|
||||
throw 0;
|
||||
>0 : 0
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
>e : any
|
||||
|
||||
x; // 0 | 1 | 2
|
||||
>x : 0 | 1 | 2
|
||||
|
||||
x = 3;
|
||||
>x = 3 : 3
|
||||
>x : 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7
|
||||
>3 : 3
|
||||
}
|
||||
finally {
|
||||
x; // 0 | 1 | 2 | 3
|
||||
>x : 0 | 1 | 2 | 3
|
||||
|
||||
if (!!true) {
|
||||
>!!true : true
|
||||
>!true : false
|
||||
>true : true
|
||||
|
||||
x = 4;
|
||||
>x = 4 : 4
|
||||
>x : 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7
|
||||
>4 : 4
|
||||
|
||||
return;
|
||||
}
|
||||
if (!!true) {
|
||||
>!!true : true
|
||||
>!true : false
|
||||
>true : true
|
||||
|
||||
x = 5;
|
||||
>x = 5 : 5
|
||||
>x : 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7
|
||||
>5 : 5
|
||||
|
||||
return;
|
||||
}
|
||||
x = 6;
|
||||
>x = 6 : 6
|
||||
>x : 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7
|
||||
>6 : 6
|
||||
|
||||
return;
|
||||
x; // unreachable
|
||||
>x : 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7
|
||||
}
|
||||
x; // unreachable
|
||||
>x : 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7
|
||||
|
||||
x = 7; // no effect
|
||||
>x = 7 : 7
|
||||
>x : 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7
|
||||
>7 : 7
|
||||
|
||||
})();
|
||||
x; // 4 | 5 | 6
|
||||
>x : 4 | 5 | 6
|
||||
}
|
||||
|
||||
// Repro from #35644
|
||||
|
||||
const main = () => {
|
||||
@@ -262,3 +560,32 @@ const main = () => {
|
||||
}
|
||||
}
|
||||
|
||||
// Repro from #36828
|
||||
|
||||
function t1() {
|
||||
>t1 : () => void
|
||||
|
||||
const x = (() => {
|
||||
>x : "x" | null
|
||||
>(() => { try { return 'x'; } catch (e) { return null; } x; // Unreachable })() : "x" | null
|
||||
>(() => { try { return 'x'; } catch (e) { return null; } x; // Unreachable }) : () => "x" | null
|
||||
>() => { try { return 'x'; } catch (e) { return null; } x; // Unreachable } : () => "x" | null
|
||||
|
||||
try {
|
||||
return 'x';
|
||||
>'x' : "x"
|
||||
}
|
||||
catch (e) {
|
||||
>e : any
|
||||
|
||||
return null;
|
||||
>null : null
|
||||
}
|
||||
x; // Unreachable
|
||||
>x : "x" | null
|
||||
|
||||
})();
|
||||
x; // Reachable
|
||||
>x : "x" | null
|
||||
}
|
||||
|
||||
|
||||
@@ -108,6 +108,124 @@ function f7() {
|
||||
x; // Unreachable
|
||||
}
|
||||
|
||||
function f8() {
|
||||
let x: 0 | 1 = 0;
|
||||
(() => {
|
||||
try {
|
||||
x = 1;
|
||||
return;
|
||||
}
|
||||
finally {
|
||||
x; // 0 | 1
|
||||
}
|
||||
x; // Unreachable
|
||||
})();
|
||||
x; // 1
|
||||
}
|
||||
|
||||
function f9() {
|
||||
let x: 0 | 1 | 2 = 0;
|
||||
(() => {
|
||||
try {
|
||||
if (!!true) {
|
||||
x = 1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
finally {
|
||||
x; // 0 | 1
|
||||
}
|
||||
x; // 0
|
||||
x = 2;
|
||||
})();
|
||||
x; // 1 | 2
|
||||
}
|
||||
|
||||
function f10() {
|
||||
let x: 0 | 1 | 2 | 3 = 0;
|
||||
(() => {
|
||||
try {
|
||||
x = 1;
|
||||
return;
|
||||
}
|
||||
catch (e) {
|
||||
x = 2;
|
||||
}
|
||||
finally {
|
||||
x; // 0 | 1 | 2
|
||||
}
|
||||
x; // 2
|
||||
x = 3;
|
||||
})();
|
||||
x; // 1 | 3
|
||||
}
|
||||
|
||||
function f11() {
|
||||
let x: 0 | 1 | 2 | 3 | 4 | 5 = 0;
|
||||
(() => {
|
||||
try {
|
||||
if (!!true) {
|
||||
x = 1;
|
||||
return;
|
||||
}
|
||||
if (!!true) {
|
||||
x = 2;
|
||||
throw 0;
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
x; // 0 | 1 | 2
|
||||
x = 3;
|
||||
}
|
||||
finally {
|
||||
x; // 0 | 1 | 2 | 3
|
||||
if (!!true) {
|
||||
x = 4;
|
||||
}
|
||||
}
|
||||
x; // 0 | 3 | 4
|
||||
x = 5;
|
||||
})();
|
||||
x; // 1 | 4 | 5
|
||||
}
|
||||
|
||||
function f12() {
|
||||
let x: 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 = 0;
|
||||
(() => {
|
||||
try {
|
||||
if (!!true) {
|
||||
x = 1;
|
||||
return;
|
||||
}
|
||||
if (!!true) {
|
||||
x = 2;
|
||||
throw 0;
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
x; // 0 | 1 | 2
|
||||
x = 3;
|
||||
}
|
||||
finally {
|
||||
x; // 0 | 1 | 2 | 3
|
||||
if (!!true) {
|
||||
x = 4;
|
||||
return;
|
||||
}
|
||||
if (!!true) {
|
||||
x = 5;
|
||||
return;
|
||||
}
|
||||
x = 6;
|
||||
return;
|
||||
x; // unreachable
|
||||
}
|
||||
x; // unreachable
|
||||
x = 7; // no effect
|
||||
})();
|
||||
x; // 4 | 5 | 6
|
||||
}
|
||||
|
||||
// Repro from #35644
|
||||
|
||||
const main = () => {
|
||||
@@ -126,3 +244,18 @@ const main = () => {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Repro from #36828
|
||||
|
||||
function t1() {
|
||||
const x = (() => {
|
||||
try {
|
||||
return 'x';
|
||||
}
|
||||
catch (e) {
|
||||
return null;
|
||||
}
|
||||
x; // Unreachable
|
||||
})();
|
||||
x; // Reachable
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user