Skip to content

Commit 6f5b94f

Browse files
committed
Add exhaustiveness checking for try,catch,re-throw.
1 parent 51411c1 commit 6f5b94f

File tree

6 files changed

+552
-9
lines changed

6 files changed

+552
-9
lines changed

src/compiler/checker.ts

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23648,15 +23648,16 @@ namespace ts {
2364823648
return eachTypeContainedIn(mapType(type, getRegularTypeOfLiteralType), switchTypes);
2364923649
}
2365023650

23651-
function functionHasImplicitReturn(func: FunctionLikeDeclaration) {
23652-
if (!(func.flags & NodeFlags.HasImplicitReturn)) {
23653-
return false;
23654-
}
23651+
function isTotalStatement(statement: Statement): boolean {
23652+
return statement.kind === SyntaxKind.SwitchStatement && isExhaustiveSwitchStatement(<SwitchStatement>statement) ||
23653+
statement.kind === SyntaxKind.TryStatement &&
23654+
((<TryStatement>statement).catchClause !== undefined) &&
23655+
some((<TryStatement>statement).catchClause!.block.statements, statement => statement.kind === SyntaxKind.ThrowStatement) &&
23656+
some((<TryStatement>statement).tryBlock.statements, isTotalStatement);
23657+
}
2365523658

23656-
if (some((<Block>func.body).statements, statement => statement.kind === SyntaxKind.SwitchStatement && isExhaustiveSwitchStatement(<SwitchStatement>statement))) {
23657-
return false;
23658-
}
23659-
return true;
23659+
function functionHasImplicitReturn(func: FunctionLikeDeclaration) {
23660+
return func.flags & NodeFlags.HasImplicitReturn && !some((<Block>func.body).statements, isTotalStatement);
2366023661
}
2366123662

2366223663
/** NOTE: Return value of `[]` means a different thing than `undefined`. `[]` means func returns `void`, `undefined` means it returns `never`. */

tests/baselines/reference/exhaustiveSwitchImplicitReturn.errors.txt

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
tests/cases/compiler/exhaustiveSwitchImplicitReturn.ts(35,32): error TS7030: Not all code paths return a value.
2+
tests/cases/compiler/exhaustiveSwitchImplicitReturn.ts(82,28): error TS7030: Not all code paths return a value.
23

34

4-
==== tests/cases/compiler/exhaustiveSwitchImplicitReturn.ts (1 errors) ====
5+
==== tests/cases/compiler/exhaustiveSwitchImplicitReturn.ts (2 errors) ====
56
function foo1(bar: "a"): number {
67
switch(bar) {
78
case "a":
@@ -44,4 +45,63 @@ tests/cases/compiler/exhaustiveSwitchImplicitReturn.ts(35,32): error TS7030: Not
4445
return 1;
4546
}
4647
}
48+
49+
// Repro from #32905.
50+
51+
enum Foo {
52+
One,
53+
Two,
54+
Three
55+
}
56+
57+
function test2(type: Foo): number {
58+
try {
59+
switch (type) {
60+
case Foo.One:
61+
return 0;
62+
case Foo.Two:
63+
return 0;
64+
case Foo.Three:
65+
return 0;
66+
}
67+
} catch (e) {
68+
throw new Error('some error')
69+
}
70+
}
71+
72+
function test3(type: Foo): number {
73+
try {
74+
console.log('some switch')
75+
switch (type) {
76+
case Foo.One:
77+
return 0;
78+
case Foo.Two:
79+
return 0;
80+
case Foo.Three:
81+
return 0;
82+
}
83+
} catch (e) {
84+
console.log('some error')
85+
throw new Error('some error')
86+
}
87+
}
88+
89+
function test4(type: Foo): number {
90+
~~~~~~
91+
!!! error TS7030: Not all code paths return a value.
92+
try {
93+
console.log('some switch')
94+
switch (type) {
95+
case Foo.One:
96+
return 0;
97+
case Foo.Two:
98+
return 0;
99+
case Foo.Three:
100+
0;
101+
}
102+
} catch (e) {
103+
console.log('some error')
104+
throw new Error('some error')
105+
}
106+
}
47107

tests/baselines/reference/exhaustiveSwitchImplicitReturn.js

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,63 @@ function foo5(bar: "a" | "b"): number {
3939
return 1;
4040
}
4141
}
42+
43+
// Repro from #32905.
44+
45+
enum Foo {
46+
One,
47+
Two,
48+
Three
49+
}
50+
51+
function test2(type: Foo): number {
52+
try {
53+
switch (type) {
54+
case Foo.One:
55+
return 0;
56+
case Foo.Two:
57+
return 0;
58+
case Foo.Three:
59+
return 0;
60+
}
61+
} catch (e) {
62+
throw new Error('some error')
63+
}
64+
}
65+
66+
function test3(type: Foo): number {
67+
try {
68+
console.log('some switch')
69+
switch (type) {
70+
case Foo.One:
71+
return 0;
72+
case Foo.Two:
73+
return 0;
74+
case Foo.Three:
75+
return 0;
76+
}
77+
} catch (e) {
78+
console.log('some error')
79+
throw new Error('some error')
80+
}
81+
}
82+
83+
function test4(type: Foo): number {
84+
try {
85+
console.log('some switch')
86+
switch (type) {
87+
case Foo.One:
88+
return 0;
89+
case Foo.Two:
90+
return 0;
91+
case Foo.Three:
92+
0;
93+
}
94+
} catch (e) {
95+
console.log('some error')
96+
throw new Error('some error')
97+
}
98+
}
4299

43100

44101
//// [exhaustiveSwitchImplicitReturn.js]
@@ -75,3 +132,59 @@ function foo5(bar) {
75132
return 1;
76133
}
77134
}
135+
// Repro from #32905.
136+
var Foo;
137+
(function (Foo) {
138+
Foo[Foo["One"] = 0] = "One";
139+
Foo[Foo["Two"] = 1] = "Two";
140+
Foo[Foo["Three"] = 2] = "Three";
141+
})(Foo || (Foo = {}));
142+
function test2(type) {
143+
try {
144+
switch (type) {
145+
case Foo.One:
146+
return 0;
147+
case Foo.Two:
148+
return 0;
149+
case Foo.Three:
150+
return 0;
151+
}
152+
}
153+
catch (e) {
154+
throw new Error('some error');
155+
}
156+
}
157+
function test3(type) {
158+
try {
159+
console.log('some switch');
160+
switch (type) {
161+
case Foo.One:
162+
return 0;
163+
case Foo.Two:
164+
return 0;
165+
case Foo.Three:
166+
return 0;
167+
}
168+
}
169+
catch (e) {
170+
console.log('some error');
171+
throw new Error('some error');
172+
}
173+
}
174+
function test4(type) {
175+
try {
176+
console.log('some switch');
177+
switch (type) {
178+
case Foo.One:
179+
return 0;
180+
case Foo.Two:
181+
return 0;
182+
case Foo.Three:
183+
0;
184+
}
185+
}
186+
catch (e) {
187+
console.log('some error');
188+
throw new Error('some error');
189+
}
190+
}

tests/baselines/reference/exhaustiveSwitchImplicitReturn.symbols

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,3 +69,146 @@ function foo5(bar: "a" | "b"): number {
6969
}
7070
}
7171

72+
// Repro from #32905.
73+
74+
enum Foo {
75+
>Foo : Symbol(Foo, Decl(exhaustiveSwitchImplicitReturn.ts, 39, 1))
76+
77+
One,
78+
>One : Symbol(Foo.One, Decl(exhaustiveSwitchImplicitReturn.ts, 43, 10))
79+
80+
Two,
81+
>Two : Symbol(Foo.Two, Decl(exhaustiveSwitchImplicitReturn.ts, 44, 8))
82+
83+
Three
84+
>Three : Symbol(Foo.Three, Decl(exhaustiveSwitchImplicitReturn.ts, 45, 8))
85+
}
86+
87+
function test2(type: Foo): number {
88+
>test2 : Symbol(test2, Decl(exhaustiveSwitchImplicitReturn.ts, 47, 1))
89+
>type : Symbol(type, Decl(exhaustiveSwitchImplicitReturn.ts, 49, 15))
90+
>Foo : Symbol(Foo, Decl(exhaustiveSwitchImplicitReturn.ts, 39, 1))
91+
92+
try {
93+
switch (type) {
94+
>type : Symbol(type, Decl(exhaustiveSwitchImplicitReturn.ts, 49, 15))
95+
96+
case Foo.One:
97+
>Foo.One : Symbol(Foo.One, Decl(exhaustiveSwitchImplicitReturn.ts, 43, 10))
98+
>Foo : Symbol(Foo, Decl(exhaustiveSwitchImplicitReturn.ts, 39, 1))
99+
>One : Symbol(Foo.One, Decl(exhaustiveSwitchImplicitReturn.ts, 43, 10))
100+
101+
return 0;
102+
case Foo.Two:
103+
>Foo.Two : Symbol(Foo.Two, Decl(exhaustiveSwitchImplicitReturn.ts, 44, 8))
104+
>Foo : Symbol(Foo, Decl(exhaustiveSwitchImplicitReturn.ts, 39, 1))
105+
>Two : Symbol(Foo.Two, Decl(exhaustiveSwitchImplicitReturn.ts, 44, 8))
106+
107+
return 0;
108+
case Foo.Three:
109+
>Foo.Three : Symbol(Foo.Three, Decl(exhaustiveSwitchImplicitReturn.ts, 45, 8))
110+
>Foo : Symbol(Foo, Decl(exhaustiveSwitchImplicitReturn.ts, 39, 1))
111+
>Three : Symbol(Foo.Three, Decl(exhaustiveSwitchImplicitReturn.ts, 45, 8))
112+
113+
return 0;
114+
}
115+
} catch (e) {
116+
>e : Symbol(e, Decl(exhaustiveSwitchImplicitReturn.ts, 59, 13))
117+
118+
throw new Error('some error')
119+
>Error : Symbol(Error, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
120+
}
121+
}
122+
123+
function test3(type: Foo): number {
124+
>test3 : Symbol(test3, Decl(exhaustiveSwitchImplicitReturn.ts, 62, 1))
125+
>type : Symbol(type, Decl(exhaustiveSwitchImplicitReturn.ts, 64, 15))
126+
>Foo : Symbol(Foo, Decl(exhaustiveSwitchImplicitReturn.ts, 39, 1))
127+
128+
try {
129+
console.log('some switch')
130+
>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
131+
>console : Symbol(console, Decl(lib.dom.d.ts, --, --))
132+
>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
133+
134+
switch (type) {
135+
>type : Symbol(type, Decl(exhaustiveSwitchImplicitReturn.ts, 64, 15))
136+
137+
case Foo.One:
138+
>Foo.One : Symbol(Foo.One, Decl(exhaustiveSwitchImplicitReturn.ts, 43, 10))
139+
>Foo : Symbol(Foo, Decl(exhaustiveSwitchImplicitReturn.ts, 39, 1))
140+
>One : Symbol(Foo.One, Decl(exhaustiveSwitchImplicitReturn.ts, 43, 10))
141+
142+
return 0;
143+
case Foo.Two:
144+
>Foo.Two : Symbol(Foo.Two, Decl(exhaustiveSwitchImplicitReturn.ts, 44, 8))
145+
>Foo : Symbol(Foo, Decl(exhaustiveSwitchImplicitReturn.ts, 39, 1))
146+
>Two : Symbol(Foo.Two, Decl(exhaustiveSwitchImplicitReturn.ts, 44, 8))
147+
148+
return 0;
149+
case Foo.Three:
150+
>Foo.Three : Symbol(Foo.Three, Decl(exhaustiveSwitchImplicitReturn.ts, 45, 8))
151+
>Foo : Symbol(Foo, Decl(exhaustiveSwitchImplicitReturn.ts, 39, 1))
152+
>Three : Symbol(Foo.Three, Decl(exhaustiveSwitchImplicitReturn.ts, 45, 8))
153+
154+
return 0;
155+
}
156+
} catch (e) {
157+
>e : Symbol(e, Decl(exhaustiveSwitchImplicitReturn.ts, 75, 13))
158+
159+
console.log('some error')
160+
>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
161+
>console : Symbol(console, Decl(lib.dom.d.ts, --, --))
162+
>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
163+
164+
throw new Error('some error')
165+
>Error : Symbol(Error, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
166+
}
167+
}
168+
169+
function test4(type: Foo): number {
170+
>test4 : Symbol(test4, Decl(exhaustiveSwitchImplicitReturn.ts, 79, 1))
171+
>type : Symbol(type, Decl(exhaustiveSwitchImplicitReturn.ts, 81, 15))
172+
>Foo : Symbol(Foo, Decl(exhaustiveSwitchImplicitReturn.ts, 39, 1))
173+
174+
try {
175+
console.log('some switch')
176+
>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
177+
>console : Symbol(console, Decl(lib.dom.d.ts, --, --))
178+
>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
179+
180+
switch (type) {
181+
>type : Symbol(type, Decl(exhaustiveSwitchImplicitReturn.ts, 81, 15))
182+
183+
case Foo.One:
184+
>Foo.One : Symbol(Foo.One, Decl(exhaustiveSwitchImplicitReturn.ts, 43, 10))
185+
>Foo : Symbol(Foo, Decl(exhaustiveSwitchImplicitReturn.ts, 39, 1))
186+
>One : Symbol(Foo.One, Decl(exhaustiveSwitchImplicitReturn.ts, 43, 10))
187+
188+
return 0;
189+
case Foo.Two:
190+
>Foo.Two : Symbol(Foo.Two, Decl(exhaustiveSwitchImplicitReturn.ts, 44, 8))
191+
>Foo : Symbol(Foo, Decl(exhaustiveSwitchImplicitReturn.ts, 39, 1))
192+
>Two : Symbol(Foo.Two, Decl(exhaustiveSwitchImplicitReturn.ts, 44, 8))
193+
194+
return 0;
195+
case Foo.Three:
196+
>Foo.Three : Symbol(Foo.Three, Decl(exhaustiveSwitchImplicitReturn.ts, 45, 8))
197+
>Foo : Symbol(Foo, Decl(exhaustiveSwitchImplicitReturn.ts, 39, 1))
198+
>Three : Symbol(Foo.Three, Decl(exhaustiveSwitchImplicitReturn.ts, 45, 8))
199+
200+
0;
201+
}
202+
} catch (e) {
203+
>e : Symbol(e, Decl(exhaustiveSwitchImplicitReturn.ts, 92, 13))
204+
205+
console.log('some error')
206+
>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
207+
>console : Symbol(console, Decl(lib.dom.d.ts, --, --))
208+
>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
209+
210+
throw new Error('some error')
211+
>Error : Symbol(Error, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
212+
}
213+
}
214+

0 commit comments

Comments
 (0)