From b9d0e17298124a617b50a529ac3a4da5d800fe18 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Wed, 7 Dec 2022 16:19:09 -0800 Subject: [PATCH] Ignore switch statement bypass control flows that produce `never` (#51703) * Ignore switch statement bypass control flows that produce 'never' * Add regression test --- src/compiler/checker.ts | 2 +- ...xhaustiveSwitchCheckCircularity.errors.txt | 18 +++++++++ .../exhaustiveSwitchCheckCircularity.js | 31 ++++++++++++++ .../exhaustiveSwitchCheckCircularity.symbols | 32 +++++++++++++++ .../exhaustiveSwitchCheckCircularity.types | 40 +++++++++++++++++++ .../exhaustiveSwitchCheckCircularity.ts | 18 +++++++++ 6 files changed, 140 insertions(+), 1 deletion(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 1b46c0e0911..3542a1e6c44 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -26073,7 +26073,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { // 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 // the risk of circularities, we only want to perform them when they make a difference. - if (!contains(antecedentTypes, type) && !isExhaustiveSwitchStatement(bypassFlow.switchStatement)) { + if (!(type.flags & TypeFlags.Never) && !contains(antecedentTypes, type) && !isExhaustiveSwitchStatement(bypassFlow.switchStatement)) { if (type === declaredType && declaredType === initialType) { return type; } diff --git a/tests/baselines/reference/exhaustiveSwitchCheckCircularity.errors.txt b/tests/baselines/reference/exhaustiveSwitchCheckCircularity.errors.txt index 96eea203d5f..356474d73bc 100644 --- a/tests/baselines/reference/exhaustiveSwitchCheckCircularity.errors.txt +++ b/tests/baselines/reference/exhaustiveSwitchCheckCircularity.errors.txt @@ -22,4 +22,22 @@ tests/cases/compiler/exhaustiveSwitchCheckCircularity.ts(14,26): error TS2345: A } } } + + // Repro from #51688 + + declare function functionB(key: string): string; + + function functionC(): void { + let unionVal: "A" | "B" = "A"; + while (true) { + let key: string; + switch (unionVal) { + case "A": { + key = "AA"; + break; + } + } + functionB(key); + } + } \ No newline at end of file diff --git a/tests/baselines/reference/exhaustiveSwitchCheckCircularity.js b/tests/baselines/reference/exhaustiveSwitchCheckCircularity.js index 89a043ec4a2..e1a7709ac91 100644 --- a/tests/baselines/reference/exhaustiveSwitchCheckCircularity.js +++ b/tests/baselines/reference/exhaustiveSwitchCheckCircularity.js @@ -17,6 +17,24 @@ function f() { } } } + +// Repro from #51688 + +declare function functionB(key: string): string; + +function functionC(): void { + let unionVal: "A" | "B" = "A"; + while (true) { + let key: string; + switch (unionVal) { + case "A": { + key = "AA"; + break; + } + } + functionB(key); + } +} //// [exhaustiveSwitchCheckCircularity.js] @@ -36,3 +54,16 @@ function f() { } } } +function functionC() { + var unionVal = "A"; + while (true) { + var key = void 0; + switch (unionVal) { + case "A": { + key = "AA"; + break; + } + } + functionB(key); + } +} diff --git a/tests/baselines/reference/exhaustiveSwitchCheckCircularity.symbols b/tests/baselines/reference/exhaustiveSwitchCheckCircularity.symbols index a3d1cac8a9e..2c77515d7a1 100644 --- a/tests/baselines/reference/exhaustiveSwitchCheckCircularity.symbols +++ b/tests/baselines/reference/exhaustiveSwitchCheckCircularity.symbols @@ -32,3 +32,35 @@ function f() { } } +// Repro from #51688 + +declare function functionB(key: string): string; +>functionB : Symbol(functionB, Decl(exhaustiveSwitchCheckCircularity.ts, 17, 1)) +>key : Symbol(key, Decl(exhaustiveSwitchCheckCircularity.ts, 21, 27)) + +function functionC(): void { +>functionC : Symbol(functionC, Decl(exhaustiveSwitchCheckCircularity.ts, 21, 48)) + + let unionVal: "A" | "B" = "A"; +>unionVal : Symbol(unionVal, Decl(exhaustiveSwitchCheckCircularity.ts, 24, 7)) + + while (true) { + let key: string; +>key : Symbol(key, Decl(exhaustiveSwitchCheckCircularity.ts, 26, 11)) + + switch (unionVal) { +>unionVal : Symbol(unionVal, Decl(exhaustiveSwitchCheckCircularity.ts, 24, 7)) + + case "A": { + key = "AA"; +>key : Symbol(key, Decl(exhaustiveSwitchCheckCircularity.ts, 26, 11)) + + break; + } + } + functionB(key); +>functionB : Symbol(functionB, Decl(exhaustiveSwitchCheckCircularity.ts, 17, 1)) +>key : Symbol(key, Decl(exhaustiveSwitchCheckCircularity.ts, 26, 11)) + } +} + diff --git a/tests/baselines/reference/exhaustiveSwitchCheckCircularity.types b/tests/baselines/reference/exhaustiveSwitchCheckCircularity.types index 16051e20461..66a42bd71c2 100644 --- a/tests/baselines/reference/exhaustiveSwitchCheckCircularity.types +++ b/tests/baselines/reference/exhaustiveSwitchCheckCircularity.types @@ -41,3 +41,43 @@ function f() { } } +// Repro from #51688 + +declare function functionB(key: string): string; +>functionB : (key: string) => string +>key : string + +function functionC(): void { +>functionC : () => void + + let unionVal: "A" | "B" = "A"; +>unionVal : "A" | "B" +>"A" : "A" + + while (true) { +>true : true + + let key: string; +>key : string + + switch (unionVal) { +>unionVal : "A" + + case "A": { +>"A" : "A" + + key = "AA"; +>key = "AA" : "AA" +>key : string +>"AA" : "AA" + + break; + } + } + functionB(key); +>functionB(key) : string +>functionB : (key: string) => string +>key : string + } +} + diff --git a/tests/cases/compiler/exhaustiveSwitchCheckCircularity.ts b/tests/cases/compiler/exhaustiveSwitchCheckCircularity.ts index 7db35fd1d00..a3cd9f10354 100644 --- a/tests/cases/compiler/exhaustiveSwitchCheckCircularity.ts +++ b/tests/cases/compiler/exhaustiveSwitchCheckCircularity.ts @@ -18,3 +18,21 @@ function f() { } } } + +// Repro from #51688 + +declare function functionB(key: string): string; + +function functionC(): void { + let unionVal: "A" | "B" = "A"; + while (true) { + let key: string; + switch (unionVal) { + case "A": { + key = "AA"; + break; + } + } + functionB(key); + } +}