From 38db7ae59e1c4e539881ae4a8dd15dcdd13d237d Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Wed, 13 Nov 2019 09:22:18 -0800 Subject: [PATCH] Properly analyze switch statement bypass control flow (#35087) * Properly analyze switch statement bypass control flow * Add regression test * Accept new baselines --- src/compiler/checker.ts | 2 +- .../exhaustiveSwitchStatements1.errors.txt | 21 +++++++++- .../reference/exhaustiveSwitchStatements1.js | 30 +++++++++++++ .../exhaustiveSwitchStatements1.symbols | 38 +++++++++++++++++ .../exhaustiveSwitchStatements1.types | 42 +++++++++++++++++++ .../exhaustiveSwitchStatements1.ts | 16 +++++++ 6 files changed, 147 insertions(+), 2 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 94ca212921..0ecf7d3114 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -19333,7 +19333,7 @@ namespace ts { } } if (bypassFlow) { - const flowType = getTypeAtFlowNode(bypassFlow.antecedent); + const flowType = getTypeAtFlowNode(bypassFlow); const type = getTypeFromFlowType(flowType); // If the bypass flow contributes a type we haven't seen yet and the switch statement // isn't exhaustive, process the bypass flow type. Since exhaustiveness checks increase diff --git a/tests/baselines/reference/exhaustiveSwitchStatements1.errors.txt b/tests/baselines/reference/exhaustiveSwitchStatements1.errors.txt index 61f80f6ed3..e958397f81 100644 --- a/tests/baselines/reference/exhaustiveSwitchStatements1.errors.txt +++ b/tests/baselines/reference/exhaustiveSwitchStatements1.errors.txt @@ -1,7 +1,8 @@ tests/cases/conformance/controlFlow/exhaustiveSwitchStatements1.ts(7,9): error TS7027: Unreachable code detected. +tests/cases/conformance/controlFlow/exhaustiveSwitchStatements1.ts(235,5): error TS2367: This condition will always return 'false' since the types '"a" | "b"' and '"c"' have no overlap. -==== tests/cases/conformance/controlFlow/exhaustiveSwitchStatements1.ts (1 errors) ==== +==== tests/cases/conformance/controlFlow/exhaustiveSwitchStatements1.ts (2 errors) ==== function f1(x: 1 | 2): string { if (!!true) { switch (x) { @@ -225,4 +226,22 @@ tests/cases/conformance/controlFlow/exhaustiveSwitchStatements1.ts(7,9): error T } } } + + // Repro from #35070 + + type O = { + a: number, + b: number + }; + type K = keyof O | 'c'; + function ff(o: O, k: K) { + switch(k) { + case 'c': + k = 'a'; + } + k === 'c'; // Error + ~~~~~~~~~ +!!! error TS2367: This condition will always return 'false' since the types '"a" | "b"' and '"c"' have no overlap. + return o[k]; + } \ No newline at end of file diff --git a/tests/baselines/reference/exhaustiveSwitchStatements1.js b/tests/baselines/reference/exhaustiveSwitchStatements1.js index c29ecfe8af..e2287cd4f8 100644 --- a/tests/baselines/reference/exhaustiveSwitchStatements1.js +++ b/tests/baselines/reference/exhaustiveSwitchStatements1.js @@ -220,6 +220,22 @@ function foo() { } } } + +// Repro from #35070 + +type O = { + a: number, + b: number +}; +type K = keyof O | 'c'; +function ff(o: O, k: K) { + switch(k) { + case 'c': + k = 'a'; + } + k === 'c'; // Error + return o[k]; +} //// [exhaustiveSwitchStatements1.js] @@ -429,6 +445,14 @@ function foo() { } } } +function ff(o, k) { + switch (k) { + case 'c': + k = 'a'; + } + k === 'c'; // Error + return o[k]; +} //// [exhaustiveSwitchStatements1.d.ts] @@ -494,3 +518,9 @@ declare const zoo: { } | undefined; declare function expression(): Animal; declare function foo(): void; +declare type O = { + a: number; + b: number; +}; +declare type K = keyof O | 'c'; +declare function ff(o: O, k: K): number; diff --git a/tests/baselines/reference/exhaustiveSwitchStatements1.symbols b/tests/baselines/reference/exhaustiveSwitchStatements1.symbols index 9dade3d21c..6ee1e4a988 100644 --- a/tests/baselines/reference/exhaustiveSwitchStatements1.symbols +++ b/tests/baselines/reference/exhaustiveSwitchStatements1.symbols @@ -565,3 +565,41 @@ function foo() { } } +// Repro from #35070 + +type O = { +>O : Symbol(O, Decl(exhaustiveSwitchStatements1.ts, 220, 1)) + + a: number, +>a : Symbol(a, Decl(exhaustiveSwitchStatements1.ts, 224, 10)) + + b: number +>b : Symbol(b, Decl(exhaustiveSwitchStatements1.ts, 225, 14)) + +}; +type K = keyof O | 'c'; +>K : Symbol(K, Decl(exhaustiveSwitchStatements1.ts, 227, 2)) +>O : Symbol(O, Decl(exhaustiveSwitchStatements1.ts, 220, 1)) + +function ff(o: O, k: K) { +>ff : Symbol(ff, Decl(exhaustiveSwitchStatements1.ts, 228, 23)) +>o : Symbol(o, Decl(exhaustiveSwitchStatements1.ts, 229, 12)) +>O : Symbol(O, Decl(exhaustiveSwitchStatements1.ts, 220, 1)) +>k : Symbol(k, Decl(exhaustiveSwitchStatements1.ts, 229, 17)) +>K : Symbol(K, Decl(exhaustiveSwitchStatements1.ts, 227, 2)) + + switch(k) { +>k : Symbol(k, Decl(exhaustiveSwitchStatements1.ts, 229, 17)) + + case 'c': + k = 'a'; +>k : Symbol(k, Decl(exhaustiveSwitchStatements1.ts, 229, 17)) + } + k === 'c'; // Error +>k : Symbol(k, Decl(exhaustiveSwitchStatements1.ts, 229, 17)) + + return o[k]; +>o : Symbol(o, Decl(exhaustiveSwitchStatements1.ts, 229, 12)) +>k : Symbol(k, Decl(exhaustiveSwitchStatements1.ts, 229, 17)) +} + diff --git a/tests/baselines/reference/exhaustiveSwitchStatements1.types b/tests/baselines/reference/exhaustiveSwitchStatements1.types index 6a9f9109d1..79e4483212 100644 --- a/tests/baselines/reference/exhaustiveSwitchStatements1.types +++ b/tests/baselines/reference/exhaustiveSwitchStatements1.types @@ -662,3 +662,45 @@ function foo() { } } +// Repro from #35070 + +type O = { +>O : O + + a: number, +>a : number + + b: number +>b : number + +}; +type K = keyof O | 'c'; +>K : K + +function ff(o: O, k: K) { +>ff : (o: O, k: K) => number +>o : O +>k : K + + switch(k) { +>k : K + + case 'c': +>'c' : "c" + + k = 'a'; +>k = 'a' : "a" +>k : K +>'a' : "a" + } + k === 'c'; // Error +>k === 'c' : boolean +>k : "a" | "b" +>'c' : "c" + + return o[k]; +>o[k] : number +>o : O +>k : "a" | "b" +} + diff --git a/tests/cases/conformance/controlFlow/exhaustiveSwitchStatements1.ts b/tests/cases/conformance/controlFlow/exhaustiveSwitchStatements1.ts index 5f59a6d00d..9c03f039a8 100644 --- a/tests/cases/conformance/controlFlow/exhaustiveSwitchStatements1.ts +++ b/tests/cases/conformance/controlFlow/exhaustiveSwitchStatements1.ts @@ -223,3 +223,19 @@ function foo() { } } } + +// Repro from #35070 + +type O = { + a: number, + b: number +}; +type K = keyof O | 'c'; +function ff(o: O, k: K) { + switch(k) { + case 'c': + k = 'a'; + } + k === 'c'; // Error + return o[k]; +}