Skip to content

Commit 039c6e5

Browse files
authored
Merge pull request #11675 from Microsoft/vladima/fix-11665
fix flow in finally blocks
2 parents ebb6678 + 09b9ea5 commit 039c6e5

File tree

5 files changed

+140
-8
lines changed

5 files changed

+140
-8
lines changed

src/compiler/binder.ts

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -984,24 +984,44 @@ namespace ts {
984984
}
985985

986986
function bindTryStatement(node: TryStatement): void {
987-
const postFinallyLabel = createBranchLabel();
987+
const preFinallyLabel = createBranchLabel();
988988
const preTryFlow = currentFlow;
989989
// TODO: Every statement in try block is potentially an exit point!
990990
bind(node.tryBlock);
991-
addAntecedent(postFinallyLabel, currentFlow);
991+
addAntecedent(preFinallyLabel, currentFlow);
992+
993+
const flowAfterTry = currentFlow;
994+
let flowAfterCatch = unreachableFlow;
995+
992996
if (node.catchClause) {
993997
currentFlow = preTryFlow;
994998
bind(node.catchClause);
995-
addAntecedent(postFinallyLabel, currentFlow);
999+
addAntecedent(preFinallyLabel, currentFlow);
1000+
1001+
flowAfterCatch = currentFlow;
9961002
}
9971003
if (node.finallyBlock) {
998-
currentFlow = preTryFlow;
1004+
// in finally flow is combined from pre-try/flow from try/flow from catch
1005+
// pre-flow is necessary to make sure that finally is reachable even if finally flows in both try and finally blocks are unreachable
1006+
addAntecedent(preFinallyLabel, preTryFlow);
1007+
currentFlow = finishFlowLabel(preFinallyLabel);
9991008
bind(node.finallyBlock);
1009+
// if flow after finally is unreachable - keep it
1010+
// otherwise check if flows after try and after catch are unreachable
1011+
// if yes - convert current flow to unreachable
1012+
// i.e.
1013+
// try { return "1" } finally { console.log(1); }
1014+
// console.log(2); // this line should be unreachable even if flow falls out of finally block
1015+
if (!(currentFlow.flags & FlowFlags.Unreachable)) {
1016+
if ((flowAfterTry.flags & FlowFlags.Unreachable) && (flowAfterCatch.flags & FlowFlags.Unreachable)) {
1017+
currentFlow = flowAfterTry === reportedUnreachableFlow || flowAfterCatch === reportedUnreachableFlow
1018+
? reportedUnreachableFlow
1019+
: unreachableFlow;
1020+
}
1021+
}
10001022
}
1001-
// if try statement has finally block and flow after finally block is unreachable - keep it
1002-
// otherwise use whatever flow was accumulated at postFinallyLabel
1003-
if (!node.finallyBlock || !(currentFlow.flags & FlowFlags.Unreachable)) {
1004-
currentFlow = finishFlowLabel(postFinallyLabel);
1023+
else {
1024+
currentFlow = finishFlowLabel(preFinallyLabel);
10051025
}
10061026
}
10071027

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
//// [flowInFinally1.ts]
2+
3+
class A {
4+
constructor() { }
5+
method() { }
6+
}
7+
8+
let a: A | null = null;
9+
10+
try {
11+
a = new A();
12+
} finally {
13+
if (a) {
14+
a.method();
15+
}
16+
}
17+
18+
//// [flowInFinally1.js]
19+
var A = (function () {
20+
function A() {
21+
}
22+
A.prototype.method = function () { };
23+
return A;
24+
}());
25+
var a = null;
26+
try {
27+
a = new A();
28+
}
29+
finally {
30+
if (a) {
31+
a.method();
32+
}
33+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
=== tests/cases/compiler/flowInFinally1.ts ===
2+
3+
class A {
4+
>A : Symbol(A, Decl(flowInFinally1.ts, 0, 0))
5+
6+
constructor() { }
7+
method() { }
8+
>method : Symbol(A.method, Decl(flowInFinally1.ts, 2, 19))
9+
}
10+
11+
let a: A | null = null;
12+
>a : Symbol(a, Decl(flowInFinally1.ts, 6, 3))
13+
>A : Symbol(A, Decl(flowInFinally1.ts, 0, 0))
14+
15+
try {
16+
a = new A();
17+
>a : Symbol(a, Decl(flowInFinally1.ts, 6, 3))
18+
>A : Symbol(A, Decl(flowInFinally1.ts, 0, 0))
19+
20+
} finally {
21+
if (a) {
22+
>a : Symbol(a, Decl(flowInFinally1.ts, 6, 3))
23+
24+
a.method();
25+
>a.method : Symbol(A.method, Decl(flowInFinally1.ts, 2, 19))
26+
>a : Symbol(a, Decl(flowInFinally1.ts, 6, 3))
27+
>method : Symbol(A.method, Decl(flowInFinally1.ts, 2, 19))
28+
}
29+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
=== tests/cases/compiler/flowInFinally1.ts ===
2+
3+
class A {
4+
>A : A
5+
6+
constructor() { }
7+
method() { }
8+
>method : () => void
9+
}
10+
11+
let a: A | null = null;
12+
>a : A | null
13+
>A : A
14+
>null : null
15+
>null : null
16+
17+
try {
18+
a = new A();
19+
>a = new A() : A
20+
>a : A | null
21+
>new A() : A
22+
>A : typeof A
23+
24+
} finally {
25+
if (a) {
26+
>a : A | null
27+
28+
a.method();
29+
>a.method() : void
30+
>a.method : () => void
31+
>a : A
32+
>method : () => void
33+
}
34+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// @strictNullChecks: true
2+
3+
class A {
4+
constructor() { }
5+
method() { }
6+
}
7+
8+
let a: A | null = null;
9+
10+
try {
11+
a = new A();
12+
} finally {
13+
if (a) {
14+
a.method();
15+
}
16+
}

0 commit comments

Comments
 (0)