Skip to content

improve enum rechability check #25822

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Sep 5, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 2 additions & 4 deletions src/compiler/binder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2789,9 +2789,7 @@ namespace ts {
// report error on class declarations
node.kind === SyntaxKind.ClassDeclaration ||
// report error on instantiated modules or const-enums only modules if preserveConstEnums is set
(node.kind === SyntaxKind.ModuleDeclaration && shouldReportErrorOnModuleDeclaration(<ModuleDeclaration>node)) ||
// report error on regular enums and const enums if preserveConstEnums is set
(isEnumDeclaration(node) && (!isEnumConst(node) || options.preserveConstEnums));
(node.kind === SyntaxKind.ModuleDeclaration && shouldReportErrorOnModuleDeclaration(<ModuleDeclaration>node));

if (reportError) {
currentFlow = reportedUnreachableFlow;
Expand Down Expand Up @@ -2836,7 +2834,7 @@ namespace ts {
// As opposed to a pure declaration like an `interface`
function isExecutableStatement(s: Statement): boolean {
// Don't remove statements that can validly be used before they appear.
return !isFunctionDeclaration(s) && !isPurelyTypeDeclaration(s) &&
return !isFunctionDeclaration(s) && !isPurelyTypeDeclaration(s) && !isEnumDeclaration(s) &&
// `var x;` may declare a variable used above
!(isVariableStatement(s) && !(getCombinedNodeFlags(s) & (NodeFlags.Let | NodeFlags.Const)) && s.declarationList.declarations.some(d => !d.initializer));
}
Expand Down
15 changes: 9 additions & 6 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1679,6 +1679,9 @@ namespace ts {
}
else {
Debug.assert(!!(result.flags & SymbolFlags.ConstEnum));
if (compilerOptions.preserveConstEnums) {
diagnosticMessage = error(errorLocation, Diagnostics.Class_0_used_before_its_declaration, declarationName);
}
}

if (diagnosticMessage) {
Expand Down Expand Up @@ -22786,6 +22789,12 @@ namespace ts {
}
}

const enum DeclarationSpaces {
None = 0,
ExportValue = 1 << 0,
ExportType = 1 << 1,
ExportNamespace = 1 << 2,
}
function checkExportsOnMergedDeclarations(node: Node): void {
if (!produceDiagnostics) {
return;
Expand Down Expand Up @@ -22850,12 +22859,6 @@ namespace ts {
}
}

const enum DeclarationSpaces {
None = 0,
ExportValue = 1 << 0,
ExportType = 1 << 1,
ExportNamespace = 1 << 2,
}
function getDeclarationSpaces(decl: Declaration): DeclarationSpaces {
let d = decl as Node;
switch (d.kind) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
tests/cases/compiler/blockScopedEnumVariablesUseBeforeDef.ts(2,12): error TS2450: Enum 'E' used before its declaration.


==== tests/cases/compiler/blockScopedEnumVariablesUseBeforeDef.ts (1 errors) ====
function foo1() {
return E.A
~
!!! error TS2450: Enum 'E' used before its declaration.
!!! related TS2728 tests/cases/compiler/blockScopedEnumVariablesUseBeforeDef.ts:3:10: 'E' is declared here.
enum E { A }
}

function foo2() {
return E.A
const enum E { A }
}
22 changes: 22 additions & 0 deletions tests/baselines/reference/blockScopedEnumVariablesUseBeforeDef.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
//// [blockScopedEnumVariablesUseBeforeDef.ts]
function foo1() {
return E.A
enum E { A }
}

function foo2() {
return E.A
const enum E { A }
}

//// [blockScopedEnumVariablesUseBeforeDef.js]
function foo1() {
return E.A;
var E;
(function (E) {
E[E["A"] = 0] = "A";
})(E || (E = {}));
}
function foo2() {
return 0 /* A */;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
=== tests/cases/compiler/blockScopedEnumVariablesUseBeforeDef.ts ===
function foo1() {
>foo1 : Symbol(foo1, Decl(blockScopedEnumVariablesUseBeforeDef.ts, 0, 0))

return E.A
>E.A : Symbol(E.A, Decl(blockScopedEnumVariablesUseBeforeDef.ts, 2, 12))
>E : Symbol(E, Decl(blockScopedEnumVariablesUseBeforeDef.ts, 1, 14))
>A : Symbol(E.A, Decl(blockScopedEnumVariablesUseBeforeDef.ts, 2, 12))

enum E { A }
>E : Symbol(E, Decl(blockScopedEnumVariablesUseBeforeDef.ts, 1, 14))
>A : Symbol(E.A, Decl(blockScopedEnumVariablesUseBeforeDef.ts, 2, 12))
}

function foo2() {
>foo2 : Symbol(foo2, Decl(blockScopedEnumVariablesUseBeforeDef.ts, 3, 1))

return E.A
>E.A : Symbol(E.A, Decl(blockScopedEnumVariablesUseBeforeDef.ts, 7, 18))
>E : Symbol(E, Decl(blockScopedEnumVariablesUseBeforeDef.ts, 6, 14))
>A : Symbol(E.A, Decl(blockScopedEnumVariablesUseBeforeDef.ts, 7, 18))

const enum E { A }
>E : Symbol(E, Decl(blockScopedEnumVariablesUseBeforeDef.ts, 6, 14))
>A : Symbol(E.A, Decl(blockScopedEnumVariablesUseBeforeDef.ts, 7, 18))
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
=== tests/cases/compiler/blockScopedEnumVariablesUseBeforeDef.ts ===
function foo1() {
>foo1 : () => E

return E.A
>E.A : E
>E : typeof E
>A : E

enum E { A }
>E : E
>A : E
}

function foo2() {
>foo2 : () => E

return E.A
>E.A : E
>E : typeof E
>A : E

const enum E { A }
>E : E
>A : E
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
tests/cases/compiler/blockScopedEnumVariablesUseBeforeDef_preserve.ts(2,12): error TS2450: Enum 'E' used before its declaration.
tests/cases/compiler/blockScopedEnumVariablesUseBeforeDef_preserve.ts(7,12): error TS2449: Class 'E' used before its declaration.


==== tests/cases/compiler/blockScopedEnumVariablesUseBeforeDef_preserve.ts (2 errors) ====
function foo1() {
return E.A
~
!!! error TS2450: Enum 'E' used before its declaration.
!!! related TS2728 tests/cases/compiler/blockScopedEnumVariablesUseBeforeDef_preserve.ts:3:10: 'E' is declared here.
enum E { A }
}

function foo2() {
return E.A
~
!!! error TS2449: Class 'E' used before its declaration.
!!! related TS2728 tests/cases/compiler/blockScopedEnumVariablesUseBeforeDef_preserve.ts:8:16: 'E' is declared here.
const enum E { A }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
//// [blockScopedEnumVariablesUseBeforeDef_preserve.ts]
function foo1() {
return E.A
enum E { A }
}

function foo2() {
return E.A
const enum E { A }
}

//// [blockScopedEnumVariablesUseBeforeDef_preserve.js]
function foo1() {
return E.A;
var E;
(function (E) {
E[E["A"] = 0] = "A";
})(E || (E = {}));
}
function foo2() {
return 0 /* A */;
var E;
(function (E) {
E[E["A"] = 0] = "A";
})(E || (E = {}));
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
=== tests/cases/compiler/blockScopedEnumVariablesUseBeforeDef_preserve.ts ===
function foo1() {
>foo1 : Symbol(foo1, Decl(blockScopedEnumVariablesUseBeforeDef_preserve.ts, 0, 0))

return E.A
>E.A : Symbol(E.A, Decl(blockScopedEnumVariablesUseBeforeDef_preserve.ts, 2, 12))
>E : Symbol(E, Decl(blockScopedEnumVariablesUseBeforeDef_preserve.ts, 1, 14))
>A : Symbol(E.A, Decl(blockScopedEnumVariablesUseBeforeDef_preserve.ts, 2, 12))

enum E { A }
>E : Symbol(E, Decl(blockScopedEnumVariablesUseBeforeDef_preserve.ts, 1, 14))
>A : Symbol(E.A, Decl(blockScopedEnumVariablesUseBeforeDef_preserve.ts, 2, 12))
}

function foo2() {
>foo2 : Symbol(foo2, Decl(blockScopedEnumVariablesUseBeforeDef_preserve.ts, 3, 1))

return E.A
>E.A : Symbol(E.A, Decl(blockScopedEnumVariablesUseBeforeDef_preserve.ts, 7, 18))
>E : Symbol(E, Decl(blockScopedEnumVariablesUseBeforeDef_preserve.ts, 6, 14))
>A : Symbol(E.A, Decl(blockScopedEnumVariablesUseBeforeDef_preserve.ts, 7, 18))

const enum E { A }
>E : Symbol(E, Decl(blockScopedEnumVariablesUseBeforeDef_preserve.ts, 6, 14))
>A : Symbol(E.A, Decl(blockScopedEnumVariablesUseBeforeDef_preserve.ts, 7, 18))
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
=== tests/cases/compiler/blockScopedEnumVariablesUseBeforeDef_preserve.ts ===
function foo1() {
>foo1 : () => E

return E.A
>E.A : E
>E : typeof E
>A : E

enum E { A }
>E : E
>A : E
}

function foo2() {
>foo2 : () => E

return E.A
>E.A : E
>E : typeof E
>A : E

const enum E { A }
>E : E
>A : E
}
Original file line number Diff line number Diff line change
Expand Up @@ -119,4 +119,5 @@ tests/cases/compiler/blockScopedVariablesUseBeforeDef.ts(100,12): error TS2448:
!!! related TS2728 tests/cases/compiler/blockScopedVariablesUseBeforeDef.ts:102:9: 'x' is declared here.
}
let x
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,8 @@ function foo14() {
a: x
}
let x
}
}


//// [blockScopedVariablesUseBeforeDef.js]
function foo0() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -212,3 +212,4 @@ function foo14() {
let x
>x : Symbol(x, Decl(blockScopedVariablesUseBeforeDef.ts, 101, 7))
}

Original file line number Diff line number Diff line change
Expand Up @@ -224,3 +224,4 @@ function foo14() {
let x
>x : any
}

12 changes: 1 addition & 11 deletions tests/baselines/reference/reachabilityChecks1.errors.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,9 @@ tests/cases/compiler/reachabilityChecks1.ts(6,5): error TS7027: Unreachable code
tests/cases/compiler/reachabilityChecks1.ts(18,5): error TS7027: Unreachable code detected.
tests/cases/compiler/reachabilityChecks1.ts(30,5): error TS7027: Unreachable code detected.
tests/cases/compiler/reachabilityChecks1.ts(47,5): error TS7027: Unreachable code detected.
tests/cases/compiler/reachabilityChecks1.ts(60,5): error TS7027: Unreachable code detected.
tests/cases/compiler/reachabilityChecks1.ts(69,5): error TS7027: Unreachable code detected.


==== tests/cases/compiler/reachabilityChecks1.ts (7 errors) ====
==== tests/cases/compiler/reachabilityChecks1.ts (5 errors) ====
while (true);
var x = 1;
~~~~~~~~~~
Expand Down Expand Up @@ -83,25 +81,17 @@ tests/cases/compiler/reachabilityChecks1.ts(69,5): error TS7027: Unreachable cod
do {
} while (true);
enum E {
~~~~~~~~
X = 1
~~~~~~~~~~~~~
}
~~~~~
!!! error TS7027: Unreachable code detected.
}

function f4() {
if (true) {
throw new Error();
}
const enum E {
~~~~~~~~~~~~~~
X = 1
~~~~~~~~~~~~~
}
~~~~~
!!! error TS7027: Unreachable code detected.
}


10 changes: 10 additions & 0 deletions tests/cases/compiler/blockScopedEnumVariablesUseBeforeDef.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// @target: ES5
function foo1() {
return E.A
enum E { A }
}

function foo2() {
return E.A
const enum E { A }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// @target: ES5
// @preserveConstEnums: true

function foo1() {
return E.A
enum E { A }
}

function foo2() {
return E.A
const enum E { A }
}
2 changes: 1 addition & 1 deletion tests/cases/compiler/blockScopedVariablesUseBeforeDef.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,4 +101,4 @@ function foo14() {
a: x
}
let x
}
}
18 changes: 15 additions & 3 deletions tests/cases/fourslash/codeFixUnreachableCode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@
////function f() {
//// return f();
//// [|return 1;|]
//// function f() {}
//// function f(a?: EE) { return a; }
//// [|return 2;|]
//// type T = number;
//// interface I {}
//// const enum E {}
//// [|enum E {}|]
//// enum EE {}
//// namespace N { export type T = number; }
//// [|namespace N { export const x: T = 0; }|]
//// var x: I;
Expand All @@ -29,11 +29,23 @@ verify.codeFixAll({
newFileContent:
`function f() {
return f();
function f() {}
function f(a?: EE) { return a; }
type T = number;
interface I {}
const enum E {}
enum EE {}
namespace N { export type T = number; }
var x: I;
}`,
});

function f() {
return f();
function f(a?: EE) { return a; }
type T = number;
interface I {}
const enum E {}
enum EE {}
namespace N { export type T = number; }
var x: I;
}