Skip to content

Commit a685ac4

Browse files
authored
Merge pull request #34212 from microsoft/fix34021
Fix control flow analysis for --noFallthroughCasesInSwitch
2 parents c9d407b + 00505ad commit a685ac4

11 files changed

+431
-13
lines changed

src/compiler/binder.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1341,7 +1341,7 @@ namespace ts {
13411341
bind(clause);
13421342
fallthroughFlow = currentFlow;
13431343
if (!(currentFlow.flags & FlowFlags.Unreachable) && i !== clauses.length - 1 && options.noFallthroughCasesInSwitch) {
1344-
errorOnFirstToken(clause, Diagnostics.Fallthrough_case_in_switch);
1344+
clause.fallthroughFlowNode = currentFlow;
13451345
}
13461346
}
13471347
clauses.transformFlags = subtreeTransformFlags | TransformFlags.HasComputedFlags;

src/compiler/checker.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31054,10 +31054,7 @@ namespace ts {
3105431054
firstDefaultClause = clause;
3105531055
}
3105631056
else {
31057-
const sourceFile = getSourceFileOfNode(node);
31058-
const start = skipTrivia(sourceFile.text, clause.pos);
31059-
const end = clause.statements.length > 0 ? clause.statements[0].pos : clause.end;
31060-
grammarErrorAtPos(sourceFile, start, end - start, Diagnostics.A_default_clause_cannot_appear_more_than_once_in_a_switch_statement);
31057+
grammarErrorOnNode(clause, Diagnostics.A_default_clause_cannot_appear_more_than_once_in_a_switch_statement);
3106131058
hasDuplicateDefaultClause = true;
3106231059
}
3106331060
}
@@ -31079,6 +31076,9 @@ namespace ts {
3107931076
}
3108031077
}
3108131078
forEach(clause.statements, checkSourceElement);
31079+
if (compilerOptions.noFallthroughCasesInSwitch && clause.fallthroughFlowNode && isReachableFlowNode(clause.fallthroughFlowNode)) {
31080+
error(clause, Diagnostics.Fallthrough_case_in_switch);
31081+
}
3108231082
});
3108331083
if (node.caseBlock.locals) {
3108431084
registerForUnusedIdentifiersCheck(node.caseBlock);

src/compiler/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2250,12 +2250,14 @@ namespace ts {
22502250
parent: CaseBlock;
22512251
expression: Expression;
22522252
statements: NodeArray<Statement>;
2253+
/* @internal */ fallthroughFlowNode?: FlowNode;
22532254
}
22542255

22552256
export interface DefaultClause extends Node {
22562257
kind: SyntaxKind.DefaultClause;
22572258
parent: CaseBlock;
22582259
statements: NodeArray<Statement>;
2260+
/* @internal */ fallthroughFlowNode?: FlowNode;
22592261
}
22602262

22612263
export type CaseOrDefaultClause = CaseClause | DefaultClause;

src/compiler/utilities.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -961,6 +961,11 @@ namespace ts {
961961
break;
962962
case SyntaxKind.ArrowFunction:
963963
return getErrorSpanForArrowFunction(sourceFile, <ArrowFunction>node);
964+
case SyntaxKind.CaseClause:
965+
case SyntaxKind.DefaultClause:
966+
const start = skipTrivia(sourceFile.text, (<CaseOrDefaultClause>node).pos);
967+
const end = (<CaseOrDefaultClause>node).statements.length > 0 ? (<CaseOrDefaultClause>node).statements[0].pos : (<CaseOrDefaultClause>node).end;
968+
return createTextSpanFromBounds(start, end);
964969
}
965970

966971
if (errorNode === undefined) {

tests/baselines/reference/fallFromLastCase2.errors.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ tests/cases/compiler/fallFromLastCase2.ts(21,9): error TS7029: Fallthrough case
1111
use("1");
1212
break;
1313
case 2:
14-
~~~~
14+
~~~~~~~
1515
!!! error TS7029: Fallthrough case in switch.
1616
use("2");
1717
case 3:
@@ -26,7 +26,7 @@ tests/cases/compiler/fallFromLastCase2.ts(21,9): error TS7029: Fallthrough case
2626
use("1");
2727
break;
2828
default:
29-
~~~~~~~
29+
~~~~~~~~
3030
!!! error TS7029: Fallthrough case in switch.
3131
use("2");
3232
case 2:

tests/baselines/reference/jsFileCompilationBindReachabilityErrors.errors.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ tests/cases/compiler/a.js(19,1): error TS7028: Unused label.
77
function foo(a, b) {
88
switch (a) {
99
case 10:
10-
~~~~
10+
~~~~~~~~
1111
!!! error TS7029: Fallthrough case in switch.
1212
if (b) {
1313
return b;
Lines changed: 58 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,74 @@
11
tests/cases/compiler/reachabilityChecks4.ts(6,9): error TS7029: Fallthrough case in switch.
2+
tests/cases/compiler/reachabilityChecks4.ts(22,9): error TS7029: Fallthrough case in switch.
23

34

4-
==== tests/cases/compiler/reachabilityChecks4.ts (1 errors) ====
5+
==== tests/cases/compiler/reachabilityChecks4.ts (2 errors) ====
56
function foo(x, y) {
67
switch (x) {
78
case 1:
89
case 2:
910
return 1;
1011
case 3:
11-
~~~~
12+
~~~~~~~
1213
!!! error TS7029: Fallthrough case in switch.
1314
if (y) {
1415
return 2;
1516
}
1617
case 4:
1718
return 3;
1819
}
19-
}
20+
}
21+
22+
declare function noop(): void;
23+
declare function fail(): never;
24+
25+
function f1(x: 0 | 1 | 2) {
26+
switch (x) {
27+
case 0:
28+
fail();
29+
case 1:
30+
~~~~~~~
31+
!!! error TS7029: Fallthrough case in switch.
32+
noop();
33+
case 2:
34+
return;
35+
}
36+
}
37+
38+
// Repro from #34021
39+
40+
type Behavior = 'SLIDE' | 'SLIDE_OUT'
41+
type Direction = 'LEFT' | 'RIGHT' | 'TOP' | 'BOTTOM'
42+
43+
interface Transition {
44+
behavior: Behavior
45+
direction: Direction
46+
}
47+
48+
function f2(transition: Transition): any {
49+
switch (transition.behavior) {
50+
case 'SLIDE':
51+
switch (transition.direction) {
52+
case 'LEFT':
53+
return []
54+
case 'RIGHT':
55+
return []
56+
case 'TOP':
57+
return []
58+
case 'BOTTOM':
59+
return []
60+
}
61+
case 'SLIDE_OUT':
62+
switch (transition.direction) {
63+
case 'LEFT':
64+
return []
65+
case 'RIGHT':
66+
return []
67+
case 'TOP':
68+
return []
69+
case 'BOTTOM':
70+
return []
71+
}
72+
}
73+
}
74+

tests/baselines/reference/reachabilityChecks4.js

Lines changed: 89 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,59 @@ function foo(x, y) {
1111
case 4:
1212
return 3;
1313
}
14-
}
14+
}
15+
16+
declare function noop(): void;
17+
declare function fail(): never;
18+
19+
function f1(x: 0 | 1 | 2) {
20+
switch (x) {
21+
case 0:
22+
fail();
23+
case 1:
24+
noop();
25+
case 2:
26+
return;
27+
}
28+
}
29+
30+
// Repro from #34021
31+
32+
type Behavior = 'SLIDE' | 'SLIDE_OUT'
33+
type Direction = 'LEFT' | 'RIGHT' | 'TOP' | 'BOTTOM'
34+
35+
interface Transition {
36+
behavior: Behavior
37+
direction: Direction
38+
}
39+
40+
function f2(transition: Transition): any {
41+
switch (transition.behavior) {
42+
case 'SLIDE':
43+
switch (transition.direction) {
44+
case 'LEFT':
45+
return []
46+
case 'RIGHT':
47+
return []
48+
case 'TOP':
49+
return []
50+
case 'BOTTOM':
51+
return []
52+
}
53+
case 'SLIDE_OUT':
54+
switch (transition.direction) {
55+
case 'LEFT':
56+
return []
57+
case 'RIGHT':
58+
return []
59+
case 'TOP':
60+
return []
61+
case 'BOTTOM':
62+
return []
63+
}
64+
}
65+
}
66+
1567

1668
//// [reachabilityChecks4.js]
1769
function foo(x, y) {
@@ -27,3 +79,39 @@ function foo(x, y) {
2779
return 3;
2880
}
2981
}
82+
function f1(x) {
83+
switch (x) {
84+
case 0:
85+
fail();
86+
case 1:
87+
noop();
88+
case 2:
89+
return;
90+
}
91+
}
92+
function f2(transition) {
93+
switch (transition.behavior) {
94+
case 'SLIDE':
95+
switch (transition.direction) {
96+
case 'LEFT':
97+
return [];
98+
case 'RIGHT':
99+
return [];
100+
case 'TOP':
101+
return [];
102+
case 'BOTTOM':
103+
return [];
104+
}
105+
case 'SLIDE_OUT':
106+
switch (transition.direction) {
107+
case 'LEFT':
108+
return [];
109+
case 'RIGHT':
110+
return [];
111+
case 'TOP':
112+
return [];
113+
case 'BOTTOM':
114+
return [];
115+
}
116+
}
117+
}

tests/baselines/reference/reachabilityChecks4.symbols

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,93 @@ function foo(x, y) {
2020
return 3;
2121
}
2222
}
23+
24+
declare function noop(): void;
25+
>noop : Symbol(noop, Decl(reachabilityChecks4.ts, 12, 1))
26+
27+
declare function fail(): never;
28+
>fail : Symbol(fail, Decl(reachabilityChecks4.ts, 14, 30))
29+
30+
function f1(x: 0 | 1 | 2) {
31+
>f1 : Symbol(f1, Decl(reachabilityChecks4.ts, 15, 31))
32+
>x : Symbol(x, Decl(reachabilityChecks4.ts, 17, 12))
33+
34+
switch (x) {
35+
>x : Symbol(x, Decl(reachabilityChecks4.ts, 17, 12))
36+
37+
case 0:
38+
fail();
39+
>fail : Symbol(fail, Decl(reachabilityChecks4.ts, 14, 30))
40+
41+
case 1:
42+
noop();
43+
>noop : Symbol(noop, Decl(reachabilityChecks4.ts, 12, 1))
44+
45+
case 2:
46+
return;
47+
}
48+
}
49+
50+
// Repro from #34021
51+
52+
type Behavior = 'SLIDE' | 'SLIDE_OUT'
53+
>Behavior : Symbol(Behavior, Decl(reachabilityChecks4.ts, 26, 1))
54+
55+
type Direction = 'LEFT' | 'RIGHT' | 'TOP' | 'BOTTOM'
56+
>Direction : Symbol(Direction, Decl(reachabilityChecks4.ts, 30, 37))
57+
58+
interface Transition {
59+
>Transition : Symbol(Transition, Decl(reachabilityChecks4.ts, 31, 52))
60+
61+
behavior: Behavior
62+
>behavior : Symbol(Transition.behavior, Decl(reachabilityChecks4.ts, 33, 22))
63+
>Behavior : Symbol(Behavior, Decl(reachabilityChecks4.ts, 26, 1))
64+
65+
direction: Direction
66+
>direction : Symbol(Transition.direction, Decl(reachabilityChecks4.ts, 34, 20))
67+
>Direction : Symbol(Direction, Decl(reachabilityChecks4.ts, 30, 37))
68+
}
69+
70+
function f2(transition: Transition): any {
71+
>f2 : Symbol(f2, Decl(reachabilityChecks4.ts, 36, 1))
72+
>transition : Symbol(transition, Decl(reachabilityChecks4.ts, 38, 12))
73+
>Transition : Symbol(Transition, Decl(reachabilityChecks4.ts, 31, 52))
74+
75+
switch (transition.behavior) {
76+
>transition.behavior : Symbol(Transition.behavior, Decl(reachabilityChecks4.ts, 33, 22))
77+
>transition : Symbol(transition, Decl(reachabilityChecks4.ts, 38, 12))
78+
>behavior : Symbol(Transition.behavior, Decl(reachabilityChecks4.ts, 33, 22))
79+
80+
case 'SLIDE':
81+
switch (transition.direction) {
82+
>transition.direction : Symbol(Transition.direction, Decl(reachabilityChecks4.ts, 34, 20))
83+
>transition : Symbol(transition, Decl(reachabilityChecks4.ts, 38, 12))
84+
>direction : Symbol(Transition.direction, Decl(reachabilityChecks4.ts, 34, 20))
85+
86+
case 'LEFT':
87+
return []
88+
case 'RIGHT':
89+
return []
90+
case 'TOP':
91+
return []
92+
case 'BOTTOM':
93+
return []
94+
}
95+
case 'SLIDE_OUT':
96+
switch (transition.direction) {
97+
>transition.direction : Symbol(Transition.direction, Decl(reachabilityChecks4.ts, 34, 20))
98+
>transition : Symbol(transition, Decl(reachabilityChecks4.ts, 38, 12))
99+
>direction : Symbol(Transition.direction, Decl(reachabilityChecks4.ts, 34, 20))
100+
101+
case 'LEFT':
102+
return []
103+
case 'RIGHT':
104+
return []
105+
case 'TOP':
106+
return []
107+
case 'BOTTOM':
108+
return []
109+
}
110+
}
111+
}
112+

0 commit comments

Comments
 (0)