mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-14 19:16:17 -06:00
Properly reflect CFA effects of return in try or catch blocks (#35730)
* Properly reflect CFA effects of return in try or catch blocks * Add tests * Accept new baselines
This commit is contained in:
parent
f90cde4706
commit
cafa175501
@ -1198,8 +1198,10 @@ namespace ts {
|
||||
// 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;
|
||||
currentExceptionTarget = createBranchLabel();
|
||||
currentReturnTarget = createBranchLabel();
|
||||
currentExceptionTarget = node.catchClause ? createBranchLabel() : currentReturnTarget;
|
||||
addAntecedent(currentExceptionTarget, currentFlow);
|
||||
bind(node.tryBlock);
|
||||
addAntecedent(preFinallyLabel, currentFlow);
|
||||
@ -1211,22 +1213,24 @@ namespace ts {
|
||||
// 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 = createBranchLabel();
|
||||
currentExceptionTarget = currentReturnTarget;
|
||||
addAntecedent(currentExceptionTarget, currentFlow);
|
||||
bind(node.catchClause);
|
||||
addAntecedent(preFinallyLabel, currentFlow);
|
||||
flowAfterCatch = currentFlow;
|
||||
}
|
||||
const exceptionTarget = finishFlowLabel(currentExceptionTarget);
|
||||
currentReturnTarget = saveReturnTarget;
|
||||
currentExceptionTarget = saveExceptionTarget;
|
||||
if (node.finallyBlock) {
|
||||
// Possible ways control can reach the finally block:
|
||||
// 1) Normal completion of try block of a try-finally or try-catch-finally
|
||||
// 2) Normal completion of catch block (following exception in try block) of a try-catch-finally
|
||||
// 3) Exception in try block of a try-finally
|
||||
// 4) Exception in catch block of a try-catch-finally
|
||||
// 3) Return in try or catch block of a try-finally or try-catch-finally
|
||||
// 4) Exception in try block of a try-finally
|
||||
// 5) Exception in catch block of a try-catch-finally
|
||||
// When analyzing a control flow graph that starts inside a finally block we want to consider all
|
||||
// four possibilities above. However, when analyzing a control flow graph that starts outside (past)
|
||||
// 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
|
||||
|
||||
132
tests/baselines/reference/tryCatchFinallyControlFlow.errors.txt
Normal file
132
tests/baselines/reference/tryCatchFinallyControlFlow.errors.txt
Normal file
@ -0,0 +1,132 @@
|
||||
tests/cases/compiler/tryCatchFinallyControlFlow.ts(105,5): error TS7027: Unreachable code detected.
|
||||
|
||||
|
||||
==== tests/cases/compiler/tryCatchFinallyControlFlow.ts (1 errors) ====
|
||||
// Repro from #34797
|
||||
|
||||
function f1() {
|
||||
let a: number | null = null;
|
||||
try {
|
||||
a = 123;
|
||||
return a;
|
||||
}
|
||||
catch (e) {
|
||||
throw e;
|
||||
}
|
||||
finally {
|
||||
if (a != null && a.toFixed(0) == "123") {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function f2() {
|
||||
let x: 0 | 1 | 2 | 3 = 0;
|
||||
try {
|
||||
x = 1;
|
||||
}
|
||||
catch (e) {
|
||||
x = 2;
|
||||
throw e;
|
||||
}
|
||||
finally {
|
||||
x; // 0 | 1 | 2
|
||||
}
|
||||
x; // 1
|
||||
}
|
||||
|
||||
function f3() {
|
||||
let x: 0 | 1 | 2 | 3 = 0;
|
||||
try {
|
||||
x = 1;
|
||||
}
|
||||
catch (e) {
|
||||
x = 2;
|
||||
return;
|
||||
}
|
||||
finally {
|
||||
x; // 0 | 1 | 2
|
||||
}
|
||||
x; // 1
|
||||
}
|
||||
|
||||
function f4() {
|
||||
let x: 0 | 1 | 2 | 3 = 0;
|
||||
try {
|
||||
x = 1;
|
||||
}
|
||||
catch (e) {
|
||||
x = 2;
|
||||
}
|
||||
finally {
|
||||
x; // 0 | 1 | 2
|
||||
}
|
||||
x; // 1 | 2
|
||||
}
|
||||
|
||||
function f5() {
|
||||
let x: 0 | 1 | 2 | 3 = 0;
|
||||
try {
|
||||
x = 1;
|
||||
return;
|
||||
}
|
||||
catch (e) {
|
||||
x = 2;
|
||||
}
|
||||
finally {
|
||||
x; // 0 | 1 | 2
|
||||
}
|
||||
x; // 2
|
||||
}
|
||||
|
||||
function f6() {
|
||||
let x: 0 | 1 | 2 | 3 = 0;
|
||||
try {
|
||||
x = 1;
|
||||
}
|
||||
catch (e) {
|
||||
x = 2;
|
||||
return;
|
||||
}
|
||||
finally {
|
||||
x; // 0 | 1 | 2
|
||||
}
|
||||
x; // 1
|
||||
}
|
||||
|
||||
function f7() {
|
||||
let x: 0 | 1 | 2 | 3 = 0;
|
||||
try {
|
||||
x = 1;
|
||||
return;
|
||||
}
|
||||
catch (e) {
|
||||
x = 2;
|
||||
return;
|
||||
}
|
||||
finally {
|
||||
x; // 0 | 1 | 2
|
||||
}
|
||||
x; // Unreachable
|
||||
~~
|
||||
!!! error TS7027: Unreachable code detected.
|
||||
}
|
||||
|
||||
// Repro from #35644
|
||||
|
||||
const main = () => {
|
||||
let hoge: string | undefined = undefined;
|
||||
try {
|
||||
hoge = 'hoge!';
|
||||
return;
|
||||
}
|
||||
catch {
|
||||
return;
|
||||
}
|
||||
finally {
|
||||
if (hoge) {
|
||||
hoge.length;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@ -59,6 +59,71 @@ function f4() {
|
||||
}
|
||||
x; // 1 | 2
|
||||
}
|
||||
|
||||
function f5() {
|
||||
let x: 0 | 1 | 2 | 3 = 0;
|
||||
try {
|
||||
x = 1;
|
||||
return;
|
||||
}
|
||||
catch (e) {
|
||||
x = 2;
|
||||
}
|
||||
finally {
|
||||
x; // 0 | 1 | 2
|
||||
}
|
||||
x; // 2
|
||||
}
|
||||
|
||||
function f6() {
|
||||
let x: 0 | 1 | 2 | 3 = 0;
|
||||
try {
|
||||
x = 1;
|
||||
}
|
||||
catch (e) {
|
||||
x = 2;
|
||||
return;
|
||||
}
|
||||
finally {
|
||||
x; // 0 | 1 | 2
|
||||
}
|
||||
x; // 1
|
||||
}
|
||||
|
||||
function f7() {
|
||||
let x: 0 | 1 | 2 | 3 = 0;
|
||||
try {
|
||||
x = 1;
|
||||
return;
|
||||
}
|
||||
catch (e) {
|
||||
x = 2;
|
||||
return;
|
||||
}
|
||||
finally {
|
||||
x; // 0 | 1 | 2
|
||||
}
|
||||
x; // Unreachable
|
||||
}
|
||||
|
||||
// Repro from #35644
|
||||
|
||||
const main = () => {
|
||||
let hoge: string | undefined = undefined;
|
||||
try {
|
||||
hoge = 'hoge!';
|
||||
return;
|
||||
}
|
||||
catch {
|
||||
return;
|
||||
}
|
||||
finally {
|
||||
if (hoge) {
|
||||
hoge.length;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//// [tryCatchFinallyControlFlow.js]
|
||||
@ -119,3 +184,63 @@ function f4() {
|
||||
}
|
||||
x; // 1 | 2
|
||||
}
|
||||
function f5() {
|
||||
var x = 0;
|
||||
try {
|
||||
x = 1;
|
||||
return;
|
||||
}
|
||||
catch (e) {
|
||||
x = 2;
|
||||
}
|
||||
finally {
|
||||
x; // 0 | 1 | 2
|
||||
}
|
||||
x; // 2
|
||||
}
|
||||
function f6() {
|
||||
var x = 0;
|
||||
try {
|
||||
x = 1;
|
||||
}
|
||||
catch (e) {
|
||||
x = 2;
|
||||
return;
|
||||
}
|
||||
finally {
|
||||
x; // 0 | 1 | 2
|
||||
}
|
||||
x; // 1
|
||||
}
|
||||
function f7() {
|
||||
var x = 0;
|
||||
try {
|
||||
x = 1;
|
||||
return;
|
||||
}
|
||||
catch (e) {
|
||||
x = 2;
|
||||
return;
|
||||
}
|
||||
finally {
|
||||
x; // 0 | 1 | 2
|
||||
}
|
||||
x; // Unreachable
|
||||
}
|
||||
// Repro from #35644
|
||||
var main = function () {
|
||||
var hoge = undefined;
|
||||
try {
|
||||
hoge = 'hoge!';
|
||||
return;
|
||||
}
|
||||
catch (_a) {
|
||||
return;
|
||||
}
|
||||
finally {
|
||||
if (hoge) {
|
||||
hoge.length;
|
||||
}
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
@ -107,3 +107,114 @@ function f4() {
|
||||
>x : Symbol(x, Decl(tryCatchFinallyControlFlow.ts, 48, 7))
|
||||
}
|
||||
|
||||
function f5() {
|
||||
>f5 : Symbol(f5, Decl(tryCatchFinallyControlFlow.ts, 59, 1))
|
||||
|
||||
let x: 0 | 1 | 2 | 3 = 0;
|
||||
>x : Symbol(x, Decl(tryCatchFinallyControlFlow.ts, 62, 7))
|
||||
|
||||
try {
|
||||
x = 1;
|
||||
>x : Symbol(x, Decl(tryCatchFinallyControlFlow.ts, 62, 7))
|
||||
|
||||
return;
|
||||
}
|
||||
catch (e) {
|
||||
>e : Symbol(e, Decl(tryCatchFinallyControlFlow.ts, 67, 11))
|
||||
|
||||
x = 2;
|
||||
>x : Symbol(x, Decl(tryCatchFinallyControlFlow.ts, 62, 7))
|
||||
}
|
||||
finally {
|
||||
x; // 0 | 1 | 2
|
||||
>x : Symbol(x, Decl(tryCatchFinallyControlFlow.ts, 62, 7))
|
||||
}
|
||||
x; // 2
|
||||
>x : Symbol(x, Decl(tryCatchFinallyControlFlow.ts, 62, 7))
|
||||
}
|
||||
|
||||
function f6() {
|
||||
>f6 : Symbol(f6, Decl(tryCatchFinallyControlFlow.ts, 74, 1))
|
||||
|
||||
let x: 0 | 1 | 2 | 3 = 0;
|
||||
>x : Symbol(x, Decl(tryCatchFinallyControlFlow.ts, 77, 7))
|
||||
|
||||
try {
|
||||
x = 1;
|
||||
>x : Symbol(x, Decl(tryCatchFinallyControlFlow.ts, 77, 7))
|
||||
}
|
||||
catch (e) {
|
||||
>e : Symbol(e, Decl(tryCatchFinallyControlFlow.ts, 81, 11))
|
||||
|
||||
x = 2;
|
||||
>x : Symbol(x, Decl(tryCatchFinallyControlFlow.ts, 77, 7))
|
||||
|
||||
return;
|
||||
}
|
||||
finally {
|
||||
x; // 0 | 1 | 2
|
||||
>x : Symbol(x, Decl(tryCatchFinallyControlFlow.ts, 77, 7))
|
||||
}
|
||||
x; // 1
|
||||
>x : Symbol(x, Decl(tryCatchFinallyControlFlow.ts, 77, 7))
|
||||
}
|
||||
|
||||
function f7() {
|
||||
>f7 : Symbol(f7, Decl(tryCatchFinallyControlFlow.ts, 89, 1))
|
||||
|
||||
let x: 0 | 1 | 2 | 3 = 0;
|
||||
>x : Symbol(x, Decl(tryCatchFinallyControlFlow.ts, 92, 7))
|
||||
|
||||
try {
|
||||
x = 1;
|
||||
>x : Symbol(x, Decl(tryCatchFinallyControlFlow.ts, 92, 7))
|
||||
|
||||
return;
|
||||
}
|
||||
catch (e) {
|
||||
>e : Symbol(e, Decl(tryCatchFinallyControlFlow.ts, 97, 11))
|
||||
|
||||
x = 2;
|
||||
>x : Symbol(x, Decl(tryCatchFinallyControlFlow.ts, 92, 7))
|
||||
|
||||
return;
|
||||
}
|
||||
finally {
|
||||
x; // 0 | 1 | 2
|
||||
>x : Symbol(x, Decl(tryCatchFinallyControlFlow.ts, 92, 7))
|
||||
}
|
||||
x; // Unreachable
|
||||
>x : Symbol(x, Decl(tryCatchFinallyControlFlow.ts, 92, 7))
|
||||
}
|
||||
|
||||
// Repro from #35644
|
||||
|
||||
const main = () => {
|
||||
>main : Symbol(main, Decl(tryCatchFinallyControlFlow.ts, 109, 5))
|
||||
|
||||
let hoge: string | undefined = undefined;
|
||||
>hoge : Symbol(hoge, Decl(tryCatchFinallyControlFlow.ts, 110, 7))
|
||||
>undefined : Symbol(undefined)
|
||||
|
||||
try {
|
||||
hoge = 'hoge!';
|
||||
>hoge : Symbol(hoge, Decl(tryCatchFinallyControlFlow.ts, 110, 7))
|
||||
|
||||
return;
|
||||
}
|
||||
catch {
|
||||
return;
|
||||
}
|
||||
finally {
|
||||
if (hoge) {
|
||||
>hoge : Symbol(hoge, Decl(tryCatchFinallyControlFlow.ts, 110, 7))
|
||||
|
||||
hoge.length;
|
||||
>hoge.length : Symbol(String.length, Decl(lib.es5.d.ts, --, --))
|
||||
>hoge : Symbol(hoge, Decl(tryCatchFinallyControlFlow.ts, 110, 7))
|
||||
>length : Symbol(String.length, Decl(lib.es5.d.ts, --, --))
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -133,3 +133,132 @@ function f4() {
|
||||
>x : 1 | 2
|
||||
}
|
||||
|
||||
function f5() {
|
||||
>f5 : () => void
|
||||
|
||||
let x: 0 | 1 | 2 | 3 = 0;
|
||||
>x : 0 | 1 | 2 | 3
|
||||
>0 : 0
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
function f6() {
|
||||
>f6 : () => void
|
||||
|
||||
let x: 0 | 1 | 2 | 3 = 0;
|
||||
>x : 0 | 1 | 2 | 3
|
||||
>0 : 0
|
||||
|
||||
try {
|
||||
x = 1;
|
||||
>x = 1 : 1
|
||||
>x : 0 | 1 | 2 | 3
|
||||
>1 : 1
|
||||
}
|
||||
catch (e) {
|
||||
>e : any
|
||||
|
||||
x = 2;
|
||||
>x = 2 : 2
|
||||
>x : 0 | 1 | 2 | 3
|
||||
>2 : 2
|
||||
|
||||
return;
|
||||
}
|
||||
finally {
|
||||
x; // 0 | 1 | 2
|
||||
>x : 0 | 1 | 2
|
||||
}
|
||||
x; // 1
|
||||
>x : 1
|
||||
}
|
||||
|
||||
function f7() {
|
||||
>f7 : () => void
|
||||
|
||||
let x: 0 | 1 | 2 | 3 = 0;
|
||||
>x : 0 | 1 | 2 | 3
|
||||
>0 : 0
|
||||
|
||||
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
|
||||
|
||||
return;
|
||||
}
|
||||
finally {
|
||||
x; // 0 | 1 | 2
|
||||
>x : 0 | 1 | 2
|
||||
}
|
||||
x; // Unreachable
|
||||
>x : 0 | 1 | 2 | 3
|
||||
}
|
||||
|
||||
// Repro from #35644
|
||||
|
||||
const main = () => {
|
||||
>main : () => void
|
||||
>() => { let hoge: string | undefined = undefined; try { hoge = 'hoge!'; return; } catch { return; } finally { if (hoge) { hoge.length; } return; }} : () => void
|
||||
|
||||
let hoge: string | undefined = undefined;
|
||||
>hoge : string | undefined
|
||||
>undefined : undefined
|
||||
|
||||
try {
|
||||
hoge = 'hoge!';
|
||||
>hoge = 'hoge!' : "hoge!"
|
||||
>hoge : string | undefined
|
||||
>'hoge!' : "hoge!"
|
||||
|
||||
return;
|
||||
}
|
||||
catch {
|
||||
return;
|
||||
}
|
||||
finally {
|
||||
if (hoge) {
|
||||
>hoge : string | undefined
|
||||
|
||||
hoge.length;
|
||||
>hoge.length : number
|
||||
>hoge : string
|
||||
>length : number
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
// @strict: true
|
||||
// @allowUnreachableCode: false
|
||||
|
||||
// Repro from #34797
|
||||
|
||||
@ -60,3 +61,68 @@ function f4() {
|
||||
}
|
||||
x; // 1 | 2
|
||||
}
|
||||
|
||||
function f5() {
|
||||
let x: 0 | 1 | 2 | 3 = 0;
|
||||
try {
|
||||
x = 1;
|
||||
return;
|
||||
}
|
||||
catch (e) {
|
||||
x = 2;
|
||||
}
|
||||
finally {
|
||||
x; // 0 | 1 | 2
|
||||
}
|
||||
x; // 2
|
||||
}
|
||||
|
||||
function f6() {
|
||||
let x: 0 | 1 | 2 | 3 = 0;
|
||||
try {
|
||||
x = 1;
|
||||
}
|
||||
catch (e) {
|
||||
x = 2;
|
||||
return;
|
||||
}
|
||||
finally {
|
||||
x; // 0 | 1 | 2
|
||||
}
|
||||
x; // 1
|
||||
}
|
||||
|
||||
function f7() {
|
||||
let x: 0 | 1 | 2 | 3 = 0;
|
||||
try {
|
||||
x = 1;
|
||||
return;
|
||||
}
|
||||
catch (e) {
|
||||
x = 2;
|
||||
return;
|
||||
}
|
||||
finally {
|
||||
x; // 0 | 1 | 2
|
||||
}
|
||||
x; // Unreachable
|
||||
}
|
||||
|
||||
// Repro from #35644
|
||||
|
||||
const main = () => {
|
||||
let hoge: string | undefined = undefined;
|
||||
try {
|
||||
hoge = 'hoge!';
|
||||
return;
|
||||
}
|
||||
catch {
|
||||
return;
|
||||
}
|
||||
finally {
|
||||
if (hoge) {
|
||||
hoge.length;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user