Skip to content

Commit 1fc1495

Browse files
author
Andy
authored
Increase span of unreachable code error (microsoft#25388)
* Increase span of unreachable code error * Add a new diagnostic for each range of unreachable statements * Update baselines
1 parent d7ce0ea commit 1fc1495

17 files changed

+165
-88
lines changed

src/compiler/binder.ts

+44-5
Original file line numberDiff line numberDiff line change
@@ -1211,7 +1211,7 @@ namespace ts {
12111211
bind(node.statement);
12121212
popActiveLabel();
12131213
if (!activeLabel.referenced && !options.allowUnusedLabels) {
1214-
errorOrSuggestionOnFirstToken(unusedLabelIsError(options), node, Diagnostics.Unused_label);
1214+
errorOrSuggestionOnNode(unusedLabelIsError(options), node.label, Diagnostics.Unused_label);
12151215
}
12161216
if (!node.statement || node.statement.kind !== SyntaxKind.DoStatement) {
12171217
// do statement sets current flow inside bindDoStatement
@@ -1918,9 +1918,16 @@ namespace ts {
19181918
file.bindDiagnostics.push(createFileDiagnostic(file, span.start, span.length, message, arg0, arg1, arg2));
19191919
}
19201920

1921-
function errorOrSuggestionOnFirstToken(isError: boolean, node: Node, message: DiagnosticMessage, arg0?: any, arg1?: any, arg2?: any) {
1922-
const span = getSpanOfTokenAtPosition(file, node.pos);
1923-
const diag = createFileDiagnostic(file, span.start, span.length, message, arg0, arg1, arg2);
1921+
function errorOrSuggestionOnNode(isError: boolean, node: Node, message: DiagnosticMessage): void {
1922+
errorOrSuggestionOnRange(isError, node, node, message);
1923+
}
1924+
1925+
function errorOrSuggestionOnRange(isError: boolean, startNode: Node, endNode: Node, message: DiagnosticMessage): void {
1926+
addErrorOrSuggestionDiagnostic(isError, { pos: getTokenPosOfNode(startNode, file), end: endNode.end }, message);
1927+
}
1928+
1929+
function addErrorOrSuggestionDiagnostic(isError: boolean, range: TextRange, message: DiagnosticMessage): void {
1930+
const diag = createFileDiagnostic(file, range.pos, range.end - range.pos, message);
19241931
if (isError) {
19251932
file.bindDiagnostics.push(diag);
19261933
}
@@ -2792,14 +2799,46 @@ namespace ts {
27922799
node.declarationList.declarations.some(d => !!d.initializer)
27932800
);
27942801

2795-
errorOrSuggestionOnFirstToken(isError, node, Diagnostics.Unreachable_code_detected);
2802+
eachUnreachableRange(node, (start, end) => errorOrSuggestionOnRange(isError, start, end, Diagnostics.Unreachable_code_detected));
27962803
}
27972804
}
27982805
}
27992806
return true;
28002807
}
28012808
}
28022809

2810+
function eachUnreachableRange(node: Node, cb: (start: Node, last: Node) => void): void {
2811+
if (isStatement(node) && isExecutableStatement(node) && isBlock(node.parent)) {
2812+
const { statements } = node.parent;
2813+
const slice = sliceAfter(statements, node);
2814+
getRangesWhere(slice, isExecutableStatement, (start, afterEnd) => cb(slice[start], slice[afterEnd - 1]));
2815+
}
2816+
else {
2817+
cb(node, node);
2818+
}
2819+
}
2820+
// As opposed to a pure declaration like an `interface`
2821+
function isExecutableStatement(s: Statement): boolean {
2822+
// Don't remove statements that can validly be used before they appear.
2823+
return !isFunctionDeclaration(s) && !isPurelyTypeDeclaration(s) &&
2824+
// `var x;` may declare a variable used above
2825+
!(isVariableStatement(s) && !(getCombinedNodeFlags(s) & (NodeFlags.Let | NodeFlags.Const)) && s.declarationList.declarations.some(d => !d.initializer));
2826+
}
2827+
2828+
function isPurelyTypeDeclaration(s: Statement): boolean {
2829+
switch (s.kind) {
2830+
case SyntaxKind.InterfaceDeclaration:
2831+
case SyntaxKind.TypeAliasDeclaration:
2832+
return true;
2833+
case SyntaxKind.ModuleDeclaration:
2834+
return getModuleInstanceState(s as ModuleDeclaration) !== ModuleInstanceState.Instantiated;
2835+
case SyntaxKind.EnumDeclaration:
2836+
return hasModifier(s, ModifierFlags.Const);
2837+
default:
2838+
return false;
2839+
}
2840+
}
2841+
28032842
/* @internal */
28042843
export function isExportsOrModuleExportsOrAlias(sourceFile: SourceFile, node: Expression): boolean {
28052844
return isExportsIdentifier(node) ||

src/compiler/utilities.ts

+6
Original file line numberDiff line numberDiff line change
@@ -8084,4 +8084,10 @@ namespace ts {
80848084
}
80858085

80868086
export type Mutable<T extends object> = { -readonly [K in keyof T]: T[K] };
8087+
8088+
export function sliceAfter<T>(arr: ReadonlyArray<T>, value: T): ReadonlyArray<T> {
8089+
const index = arr.indexOf(value);
8090+
Debug.assert(index !== -1);
8091+
return arr.slice(index);
8092+
}
80878093
}

src/services/codefixes/fixUnreachableCode.ts

+12-33
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,14 @@ namespace ts.codefix {
55
registerCodeFix({
66
errorCodes,
77
getCodeActions(context) {
8-
const changes = textChanges.ChangeTracker.with(context, t => doChange(t, context.sourceFile, context.span.start));
8+
const changes = textChanges.ChangeTracker.with(context, t => doChange(t, context.sourceFile, context.span.start, context.span.length));
99
return [createCodeFixAction(fixId, changes, Diagnostics.Remove_unreachable_code, fixId, Diagnostics.Remove_all_unreachable_code)];
1010
},
1111
fixIds: [fixId],
12-
getAllCodeActions: context => codeFixAll(context, errorCodes, (changes, diag) => doChange(changes, diag.file, diag.start)),
12+
getAllCodeActions: context => codeFixAll(context, errorCodes, (changes, diag) => doChange(changes, diag.file, diag.start, diag.length)),
1313
});
1414

15-
function doChange(changes: textChanges.ChangeTracker, sourceFile: SourceFile, start: number): void {
15+
function doChange(changes: textChanges.ChangeTracker, sourceFile: SourceFile, start: number, length: number): void {
1616
const token = getTokenAtPosition(sourceFile, start);
1717
const statement = findAncestor(token, isStatement)!;
1818
Debug.assert(statement.getStart(sourceFile) === token.getStart(sourceFile));
@@ -36,43 +36,22 @@ namespace ts.codefix {
3636
break;
3737
default:
3838
if (isBlock(statement.parent)) {
39-
split(sliceAfter(statement.parent.statements, statement), shouldRemove, (start, end) => changes.deleteNodeRange(sourceFile, start, end));
39+
const end = start + length;
40+
const lastStatement = Debug.assertDefined(lastWhere(sliceAfter(statement.parent.statements, statement), s => s.pos < end));
41+
changes.deleteNodeRange(sourceFile, statement, lastStatement);
4042
}
4143
else {
4244
changes.delete(sourceFile, statement);
4345
}
4446
}
4547
}
4648

47-
function shouldRemove(s: Statement): boolean {
48-
// Don't remove statements that can validly be used before they appear.
49-
return !isFunctionDeclaration(s) && !isPurelyTypeDeclaration(s) &&
50-
// `var x;` may declare a variable used above
51-
!(isVariableStatement(s) && !(getCombinedNodeFlags(s) & (NodeFlags.Let | NodeFlags.Const)) && s.declarationList.declarations.some(d => !d.initializer));
52-
}
53-
54-
function isPurelyTypeDeclaration(s: Statement): boolean {
55-
switch (s.kind) {
56-
case SyntaxKind.InterfaceDeclaration:
57-
case SyntaxKind.TypeAliasDeclaration:
58-
return true;
59-
case SyntaxKind.ModuleDeclaration:
60-
return getModuleInstanceState(s as ModuleDeclaration) !== ModuleInstanceState.Instantiated;
61-
case SyntaxKind.EnumDeclaration:
62-
return hasModifier(s, ModifierFlags.Const);
63-
default:
64-
return false;
49+
function lastWhere<T>(a: ReadonlyArray<T>, pred: (value: T) => boolean): T | undefined {
50+
let last: T | undefined;
51+
for (const value of a) {
52+
if (!pred(value)) break;
53+
last = value;
6554
}
66-
}
67-
68-
function sliceAfter<T>(arr: ReadonlyArray<T>, value: T): ReadonlyArray<T> {
69-
const index = arr.indexOf(value);
70-
Debug.assert(index !== -1);
71-
return arr.slice(index);
72-
}
73-
74-
// Calls 'cb' with the start and end of each range where 'pred' is true.
75-
function split<T>(arr: ReadonlyArray<T>, pred: (t: T) => boolean, cb: (start: T, end: T) => void): void {
76-
getRangesWhere(arr, pred, (start, afterEnd) => cb(arr[start], arr[afterEnd - 1]));
55+
return last;
7756
}
7857
}

tests/baselines/reference/api/tsserverlibrary.d.ts

+1
Original file line numberDiff line numberDiff line change
@@ -7399,6 +7399,7 @@ declare namespace ts {
73997399
type Mutable<T extends object> = {
74007400
-readonly [K in keyof T]: T[K];
74017401
};
7402+
function sliceAfter<T>(arr: ReadonlyArray<T>, value: T): ReadonlyArray<T>;
74027403
}
74037404
declare namespace ts {
74047405
function createNode(kind: SyntaxKind, pos?: number, end?: number): Node;

tests/baselines/reference/cf.errors.txt

+4-4
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ tests/cases/compiler/cf.ts(36,13): error TS7027: Unreachable code detected.
1414
if (y==7) {
1515
continue L1;
1616
x=11;
17-
~
17+
~~~~~
1818
!!! error TS7027: Unreachable code detected.
1919
}
2020
if (y==3) {
@@ -28,7 +28,7 @@ tests/cases/compiler/cf.ts(36,13): error TS7027: Unreachable code detected.
2828
if (y==20) {
2929
break;
3030
x=12;
31-
~
31+
~~~~~
3232
!!! error TS7027: Unreachable code detected.
3333
}
3434
} while (y<41);
@@ -41,13 +41,13 @@ tests/cases/compiler/cf.ts(36,13): error TS7027: Unreachable code detected.
4141
L3: if (x<y) {
4242
break L2;
4343
x=13;
44-
~
44+
~~~~~
4545
!!! error TS7027: Unreachable code detected.
4646
}
4747
else {
4848
break L3;
4949
x=14;
50-
~
50+
~~~~~
5151
!!! error TS7027: Unreachable code detected.
5252
}
5353
}

tests/baselines/reference/jsFileCompilationBindErrors.errors.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ tests/cases/compiler/a.js(11,9): error TS1100: Invalid use of 'arguments' in str
1515
function f() {
1616
return;
1717
return; // Error: Unreachable code detected.
18-
~~~~~~
18+
~~~~~~~
1919
!!! error TS7027: Unreachable code detected.
2020
}
2121

tests/baselines/reference/jsFileCompilationBindReachabilityErrors.errors.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ tests/cases/compiler/a.js(19,1): error TS7028: Unused label.
2222
function bar2() {
2323
}
2424
var x = 10; // error
25-
~~~
25+
~~~~~~~~~~~
2626
!!! error TS7027: Unreachable code detected.
2727
}
2828

tests/baselines/reference/reachabilityChecks1.errors.txt

+21-12
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,13 @@ tests/cases/compiler/reachabilityChecks1.ts(69,5): error TS7027: Unreachable cod
1010
==== tests/cases/compiler/reachabilityChecks1.ts (7 errors) ====
1111
while (true);
1212
var x = 1;
13-
~~~
13+
~~~~~~~~~~
1414
!!! error TS7027: Unreachable code detected.
1515

1616
module A {
1717
while (true);
1818
let x;
19-
~~~
19+
~~~~~~
2020
!!! error TS7027: Unreachable code detected.
2121
}
2222

@@ -30,10 +30,12 @@ tests/cases/compiler/reachabilityChecks1.ts(69,5): error TS7027: Unreachable cod
3030
module A2 {
3131
while (true);
3232
module A {
33-
~~~~~~
34-
!!! error TS7027: Unreachable code detected.
33+
~~~~~~~~~~
3534
var x = 1;
35+
~~~~~~~~~~~~~~~~~~
3636
}
37+
~~~~~
38+
!!! error TS7027: Unreachable code detected.
3739
}
3840

3941
module A3 {
@@ -44,10 +46,12 @@ tests/cases/compiler/reachabilityChecks1.ts(69,5): error TS7027: Unreachable cod
4446
module A4 {
4547
while (true);
4648
module A {
47-
~~~~~~
48-
!!! error TS7027: Unreachable code detected.
49+
~~~~~~~~~~
4950
const enum E { X }
51+
~~~~~~~~~~~~~~~~~~~~~~~~~~
5052
}
53+
~~~~~
54+
!!! error TS7027: Unreachable code detected.
5155
}
5256

5357
function f1(x) {
@@ -63,9 +67,10 @@ tests/cases/compiler/reachabilityChecks1.ts(69,5): error TS7027: Unreachable cod
6367
function f2() {
6468
return;
6569
class A {
66-
~~~~~
67-
!!! error TS7027: Unreachable code detected.
70+
~~~~~~~~~
6871
}
72+
~~~~~
73+
!!! error TS7027: Unreachable code detected.
6974
}
7075

7176
module B {
@@ -78,21 +83,25 @@ tests/cases/compiler/reachabilityChecks1.ts(69,5): error TS7027: Unreachable cod
7883
do {
7984
} while (true);
8085
enum E {
81-
~~~~
82-
!!! error TS7027: Unreachable code detected.
86+
~~~~~~~~
8387
X = 1
88+
~~~~~~~~~~~~~
8489
}
90+
~~~~~
91+
!!! error TS7027: Unreachable code detected.
8592
}
8693

8794
function f4() {
8895
if (true) {
8996
throw new Error();
9097
}
9198
const enum E {
92-
~~~~~
93-
!!! error TS7027: Unreachable code detected.
99+
~~~~~~~~~~~~~~
94100
X = 1
101+
~~~~~~~~~~~~~
95102
}
103+
~~~~~
104+
!!! error TS7027: Unreachable code detected.
96105
}
97106

98107

tests/baselines/reference/reachabilityChecks2.errors.txt

+7-2
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,17 @@ tests/cases/compiler/reachabilityChecks2.ts(4,1): error TS7027: Unreachable code
66
const enum E { X }
77

88
module A4 {
9-
~~~~~~
10-
!!! error TS7027: Unreachable code detected.
9+
~~~~~~~~~~~
1110
while (true);
11+
~~~~~~~~~~~~~~~~~
1212
module A {
13+
~~~~~~~~~~~~~~
1314
const enum E { X }
15+
~~~~~~~~~~~~~~~~~~~~~~~~~~
1416
}
17+
~~~~~
1518
}
19+
~
20+
!!! error TS7027: Unreachable code detected.
1621

1722

tests/baselines/reference/reachabilityChecks5.errors.txt

+3-3
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ tests/cases/compiler/reachabilityChecks5.ts(122,13): error TS7027: Unreachable c
109109
}
110110
else {
111111
return 1;
112-
~~~~~~
112+
~~~~~~~~~
113113
!!! error TS7027: Unreachable code detected.
114114
}
115115
}
@@ -124,7 +124,7 @@ tests/cases/compiler/reachabilityChecks5.ts(122,13): error TS7027: Unreachable c
124124
try {
125125
while (false) {
126126
return 1;
127-
~~~~~~
127+
~~~~~~~~~
128128
!!! error TS7027: Unreachable code detected.
129129
}
130130
}
@@ -154,7 +154,7 @@ tests/cases/compiler/reachabilityChecks5.ts(122,13): error TS7027: Unreachable c
154154
break test;
155155
} while (true);
156156
x++;
157-
~
157+
~~~~
158158
!!! error TS7027: Unreachable code detected.
159159
} while (true);
160160
}

tests/baselines/reference/reachabilityChecks6.errors.txt

+3-3
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ tests/cases/compiler/reachabilityChecks6.ts(122,13): error TS7027: Unreachable c
106106
}
107107
else {
108108
return 1;
109-
~~~~~~
109+
~~~~~~~~~
110110
!!! error TS7027: Unreachable code detected.
111111
}
112112
}
@@ -121,7 +121,7 @@ tests/cases/compiler/reachabilityChecks6.ts(122,13): error TS7027: Unreachable c
121121
try {
122122
while (false) {
123123
return 1;
124-
~~~~~~
124+
~~~~~~~~~
125125
!!! error TS7027: Unreachable code detected.
126126
}
127127
}
@@ -151,7 +151,7 @@ tests/cases/compiler/reachabilityChecks6.ts(122,13): error TS7027: Unreachable c
151151
break test;
152152
} while (true);
153153
x++;
154-
~
154+
~~~~
155155
!!! error TS7027: Unreachable code detected.
156156
} while (true);
157157
}

0 commit comments

Comments
 (0)