Merge pull request #34212 from microsoft/fix34021

Fix control flow analysis for --noFallthroughCasesInSwitch
This commit is contained in:
Anders Hejlsberg
2019-10-15 12:10:53 -07:00
committed by GitHub
11 changed files with 431 additions and 13 deletions
+1 -1
View File
@@ -1341,7 +1341,7 @@ namespace ts {
bind(clause);
fallthroughFlow = currentFlow;
if (!(currentFlow.flags & FlowFlags.Unreachable) && i !== clauses.length - 1 && options.noFallthroughCasesInSwitch) {
errorOnFirstToken(clause, Diagnostics.Fallthrough_case_in_switch);
clause.fallthroughFlowNode = currentFlow;
}
}
clauses.transformFlags = subtreeTransformFlags | TransformFlags.HasComputedFlags;
+4 -4
View File
@@ -31054,10 +31054,7 @@ namespace ts {
firstDefaultClause = clause;
}
else {
const sourceFile = getSourceFileOfNode(node);
const start = skipTrivia(sourceFile.text, clause.pos);
const end = clause.statements.length > 0 ? clause.statements[0].pos : clause.end;
grammarErrorAtPos(sourceFile, start, end - start, Diagnostics.A_default_clause_cannot_appear_more_than_once_in_a_switch_statement);
grammarErrorOnNode(clause, Diagnostics.A_default_clause_cannot_appear_more_than_once_in_a_switch_statement);
hasDuplicateDefaultClause = true;
}
}
@@ -31079,6 +31076,9 @@ namespace ts {
}
}
forEach(clause.statements, checkSourceElement);
if (compilerOptions.noFallthroughCasesInSwitch && clause.fallthroughFlowNode && isReachableFlowNode(clause.fallthroughFlowNode)) {
error(clause, Diagnostics.Fallthrough_case_in_switch);
}
});
if (node.caseBlock.locals) {
registerForUnusedIdentifiersCheck(node.caseBlock);
+2
View File
@@ -2250,12 +2250,14 @@ namespace ts {
parent: CaseBlock;
expression: Expression;
statements: NodeArray<Statement>;
/* @internal */ fallthroughFlowNode?: FlowNode;
}
export interface DefaultClause extends Node {
kind: SyntaxKind.DefaultClause;
parent: CaseBlock;
statements: NodeArray<Statement>;
/* @internal */ fallthroughFlowNode?: FlowNode;
}
export type CaseOrDefaultClause = CaseClause | DefaultClause;
+5
View File
@@ -961,6 +961,11 @@ namespace ts {
break;
case SyntaxKind.ArrowFunction:
return getErrorSpanForArrowFunction(sourceFile, <ArrowFunction>node);
case SyntaxKind.CaseClause:
case SyntaxKind.DefaultClause:
const start = skipTrivia(sourceFile.text, (<CaseOrDefaultClause>node).pos);
const end = (<CaseOrDefaultClause>node).statements.length > 0 ? (<CaseOrDefaultClause>node).statements[0].pos : (<CaseOrDefaultClause>node).end;
return createTextSpanFromBounds(start, end);
}
if (errorNode === undefined) {
@@ -11,7 +11,7 @@ tests/cases/compiler/fallFromLastCase2.ts(21,9): error TS7029: Fallthrough case
use("1");
break;
case 2:
~~~~
~~~~~~~
!!! error TS7029: Fallthrough case in switch.
use("2");
case 3:
@@ -26,7 +26,7 @@ tests/cases/compiler/fallFromLastCase2.ts(21,9): error TS7029: Fallthrough case
use("1");
break;
default:
~~~~~~~
~~~~~~~~
!!! error TS7029: Fallthrough case in switch.
use("2");
case 2:
@@ -7,7 +7,7 @@ tests/cases/compiler/a.js(19,1): error TS7028: Unused label.
function foo(a, b) {
switch (a) {
case 10:
~~~~
~~~~~~~~
!!! error TS7029: Fallthrough case in switch.
if (b) {
return b;
@@ -1,14 +1,15 @@
tests/cases/compiler/reachabilityChecks4.ts(6,9): error TS7029: Fallthrough case in switch.
tests/cases/compiler/reachabilityChecks4.ts(22,9): error TS7029: Fallthrough case in switch.
==== tests/cases/compiler/reachabilityChecks4.ts (1 errors) ====
==== tests/cases/compiler/reachabilityChecks4.ts (2 errors) ====
function foo(x, y) {
switch (x) {
case 1:
case 2:
return 1;
case 3:
~~~~
~~~~~~~
!!! error TS7029: Fallthrough case in switch.
if (y) {
return 2;
@@ -16,4 +17,58 @@ tests/cases/compiler/reachabilityChecks4.ts(6,9): error TS7029: Fallthrough case
case 4:
return 3;
}
}
}
declare function noop(): void;
declare function fail(): never;
function f1(x: 0 | 1 | 2) {
switch (x) {
case 0:
fail();
case 1:
~~~~~~~
!!! error TS7029: Fallthrough case in switch.
noop();
case 2:
return;
}
}
// Repro from #34021
type Behavior = 'SLIDE' | 'SLIDE_OUT'
type Direction = 'LEFT' | 'RIGHT' | 'TOP' | 'BOTTOM'
interface Transition {
behavior: Behavior
direction: Direction
}
function f2(transition: Transition): any {
switch (transition.behavior) {
case 'SLIDE':
switch (transition.direction) {
case 'LEFT':
return []
case 'RIGHT':
return []
case 'TOP':
return []
case 'BOTTOM':
return []
}
case 'SLIDE_OUT':
switch (transition.direction) {
case 'LEFT':
return []
case 'RIGHT':
return []
case 'TOP':
return []
case 'BOTTOM':
return []
}
}
}
@@ -11,7 +11,59 @@ function foo(x, y) {
case 4:
return 3;
}
}
}
declare function noop(): void;
declare function fail(): never;
function f1(x: 0 | 1 | 2) {
switch (x) {
case 0:
fail();
case 1:
noop();
case 2:
return;
}
}
// Repro from #34021
type Behavior = 'SLIDE' | 'SLIDE_OUT'
type Direction = 'LEFT' | 'RIGHT' | 'TOP' | 'BOTTOM'
interface Transition {
behavior: Behavior
direction: Direction
}
function f2(transition: Transition): any {
switch (transition.behavior) {
case 'SLIDE':
switch (transition.direction) {
case 'LEFT':
return []
case 'RIGHT':
return []
case 'TOP':
return []
case 'BOTTOM':
return []
}
case 'SLIDE_OUT':
switch (transition.direction) {
case 'LEFT':
return []
case 'RIGHT':
return []
case 'TOP':
return []
case 'BOTTOM':
return []
}
}
}
//// [reachabilityChecks4.js]
function foo(x, y) {
@@ -27,3 +79,39 @@ function foo(x, y) {
return 3;
}
}
function f1(x) {
switch (x) {
case 0:
fail();
case 1:
noop();
case 2:
return;
}
}
function f2(transition) {
switch (transition.behavior) {
case 'SLIDE':
switch (transition.direction) {
case 'LEFT':
return [];
case 'RIGHT':
return [];
case 'TOP':
return [];
case 'BOTTOM':
return [];
}
case 'SLIDE_OUT':
switch (transition.direction) {
case 'LEFT':
return [];
case 'RIGHT':
return [];
case 'TOP':
return [];
case 'BOTTOM':
return [];
}
}
}
@@ -20,3 +20,93 @@ function foo(x, y) {
return 3;
}
}
declare function noop(): void;
>noop : Symbol(noop, Decl(reachabilityChecks4.ts, 12, 1))
declare function fail(): never;
>fail : Symbol(fail, Decl(reachabilityChecks4.ts, 14, 30))
function f1(x: 0 | 1 | 2) {
>f1 : Symbol(f1, Decl(reachabilityChecks4.ts, 15, 31))
>x : Symbol(x, Decl(reachabilityChecks4.ts, 17, 12))
switch (x) {
>x : Symbol(x, Decl(reachabilityChecks4.ts, 17, 12))
case 0:
fail();
>fail : Symbol(fail, Decl(reachabilityChecks4.ts, 14, 30))
case 1:
noop();
>noop : Symbol(noop, Decl(reachabilityChecks4.ts, 12, 1))
case 2:
return;
}
}
// Repro from #34021
type Behavior = 'SLIDE' | 'SLIDE_OUT'
>Behavior : Symbol(Behavior, Decl(reachabilityChecks4.ts, 26, 1))
type Direction = 'LEFT' | 'RIGHT' | 'TOP' | 'BOTTOM'
>Direction : Symbol(Direction, Decl(reachabilityChecks4.ts, 30, 37))
interface Transition {
>Transition : Symbol(Transition, Decl(reachabilityChecks4.ts, 31, 52))
behavior: Behavior
>behavior : Symbol(Transition.behavior, Decl(reachabilityChecks4.ts, 33, 22))
>Behavior : Symbol(Behavior, Decl(reachabilityChecks4.ts, 26, 1))
direction: Direction
>direction : Symbol(Transition.direction, Decl(reachabilityChecks4.ts, 34, 20))
>Direction : Symbol(Direction, Decl(reachabilityChecks4.ts, 30, 37))
}
function f2(transition: Transition): any {
>f2 : Symbol(f2, Decl(reachabilityChecks4.ts, 36, 1))
>transition : Symbol(transition, Decl(reachabilityChecks4.ts, 38, 12))
>Transition : Symbol(Transition, Decl(reachabilityChecks4.ts, 31, 52))
switch (transition.behavior) {
>transition.behavior : Symbol(Transition.behavior, Decl(reachabilityChecks4.ts, 33, 22))
>transition : Symbol(transition, Decl(reachabilityChecks4.ts, 38, 12))
>behavior : Symbol(Transition.behavior, Decl(reachabilityChecks4.ts, 33, 22))
case 'SLIDE':
switch (transition.direction) {
>transition.direction : Symbol(Transition.direction, Decl(reachabilityChecks4.ts, 34, 20))
>transition : Symbol(transition, Decl(reachabilityChecks4.ts, 38, 12))
>direction : Symbol(Transition.direction, Decl(reachabilityChecks4.ts, 34, 20))
case 'LEFT':
return []
case 'RIGHT':
return []
case 'TOP':
return []
case 'BOTTOM':
return []
}
case 'SLIDE_OUT':
switch (transition.direction) {
>transition.direction : Symbol(Transition.direction, Decl(reachabilityChecks4.ts, 34, 20))
>transition : Symbol(transition, Decl(reachabilityChecks4.ts, 38, 12))
>direction : Symbol(Transition.direction, Decl(reachabilityChecks4.ts, 34, 20))
case 'LEFT':
return []
case 'RIGHT':
return []
case 'TOP':
return []
case 'BOTTOM':
return []
}
}
}
@@ -32,3 +32,130 @@ function foo(x, y) {
>3 : 3
}
}
declare function noop(): void;
>noop : () => void
declare function fail(): never;
>fail : () => never
function f1(x: 0 | 1 | 2) {
>f1 : (x: 0 | 1 | 2) => void
>x : 0 | 1 | 2
switch (x) {
>x : 0 | 1 | 2
case 0:
>0 : 0
fail();
>fail() : never
>fail : () => never
case 1:
>1 : 1
noop();
>noop() : void
>noop : () => void
case 2:
>2 : 2
return;
}
}
// Repro from #34021
type Behavior = 'SLIDE' | 'SLIDE_OUT'
>Behavior : Behavior
type Direction = 'LEFT' | 'RIGHT' | 'TOP' | 'BOTTOM'
>Direction : Direction
interface Transition {
behavior: Behavior
>behavior : Behavior
direction: Direction
>direction : Direction
}
function f2(transition: Transition): any {
>f2 : (transition: Transition) => any
>transition : Transition
switch (transition.behavior) {
>transition.behavior : Behavior
>transition : Transition
>behavior : Behavior
case 'SLIDE':
>'SLIDE' : "SLIDE"
switch (transition.direction) {
>transition.direction : Direction
>transition : Transition
>direction : Direction
case 'LEFT':
>'LEFT' : "LEFT"
return []
>[] : undefined[]
case 'RIGHT':
>'RIGHT' : "RIGHT"
return []
>[] : undefined[]
case 'TOP':
>'TOP' : "TOP"
return []
>[] : undefined[]
case 'BOTTOM':
>'BOTTOM' : "BOTTOM"
return []
>[] : undefined[]
}
case 'SLIDE_OUT':
>'SLIDE_OUT' : "SLIDE_OUT"
switch (transition.direction) {
>transition.direction : Direction
>transition : Transition
>direction : Direction
case 'LEFT':
>'LEFT' : "LEFT"
return []
>[] : undefined[]
case 'RIGHT':
>'RIGHT' : "RIGHT"
return []
>[] : undefined[]
case 'TOP':
>'TOP' : "TOP"
return []
>[] : undefined[]
case 'BOTTOM':
>'BOTTOM' : "BOTTOM"
return []
>[] : undefined[]
}
}
}
+52 -1
View File
@@ -12,4 +12,55 @@ function foo(x, y) {
case 4:
return 3;
}
}
}
declare function noop(): void;
declare function fail(): never;
function f1(x: 0 | 1 | 2) {
switch (x) {
case 0:
fail();
case 1:
noop();
case 2:
return;
}
}
// Repro from #34021
type Behavior = 'SLIDE' | 'SLIDE_OUT'
type Direction = 'LEFT' | 'RIGHT' | 'TOP' | 'BOTTOM'
interface Transition {
behavior: Behavior
direction: Direction
}
function f2(transition: Transition): any {
switch (transition.behavior) {
case 'SLIDE':
switch (transition.direction) {
case 'LEFT':
return []
case 'RIGHT':
return []
case 'TOP':
return []
case 'BOTTOM':
return []
}
case 'SLIDE_OUT':
switch (transition.direction) {
case 'LEFT':
return []
case 'RIGHT':
return []
case 'TOP':
return []
case 'BOTTOM':
return []
}
}
}