Skip to content

Commit 5c3ede7

Browse files
committed
Error for more than one enum omitting an initial initializer
1 parent b11c9c8 commit 5c3ede7

11 files changed

+150
-7
lines changed

src/compiler/checker.ts

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5212,7 +5212,8 @@ module ts {
52125212
function checkEnumDeclaration(node: EnumDeclaration) {
52135213
checkDeclarationModifiers(node);
52145214
checkNameIsReserved(node.name, Diagnostics.Enum_name_cannot_be_0);
5215-
var enumType = getDeclaredTypeOfSymbol(getSymbolOfNode(node));
5215+
var enumSymbol = getSymbolOfNode(node);
5216+
var enumType = getDeclaredTypeOfSymbol(enumSymbol);
52165217
var autoValue = 0;
52175218
var ambient = isInAmbientContext(node);
52185219
forEach(node.members, member => {
@@ -5235,7 +5236,38 @@ module ts {
52355236
getNodeLinks(member).enumMemberValue = autoValue++;
52365237
}
52375238
});
5238-
// TODO: Only one enum declaration can omit the initial value
5239+
5240+
// Spec 2014 - Section 9.3:
5241+
// It isn't possible for one enum declaration to continue the automatic numbering sequence of another,
5242+
// and when an enum type has multiple declarations, only one declaration is permitted to omit a value
5243+
// for the first member.
5244+
//
5245+
// Only perform this check once per symbol
5246+
var firstDeclaration = getDeclarationOfKind(enumSymbol, node.kind);
5247+
if (node === firstDeclaration) {
5248+
var seenEnumMissingInitialInitializer = false;
5249+
forEach(enumSymbol.declarations, declaration => {
5250+
// return true if we hit a violation of the rule, false otherwise
5251+
if (declaration.kind !== SyntaxKind.EnumDeclaration) {
5252+
return false;
5253+
}
5254+
5255+
var enumDeclaration = <EnumDeclaration>declaration;
5256+
if (!enumDeclaration.members.length) {
5257+
return false;
5258+
}
5259+
5260+
var firstEnumMember = enumDeclaration.members[0];
5261+
if (!firstEnumMember.initializer) {
5262+
if (seenEnumMissingInitialInitializer) {
5263+
error(firstEnumMember.name, Diagnostics.In_an_enum_with_multiple_declarations_only_one_declaration_can_omit_an_initializer_for_its_first_enum_element);
5264+
}
5265+
else {
5266+
seenEnumMissingInitialInitializer = true;
5267+
}
5268+
}
5269+
});
5270+
}
52395271
}
52405272

52415273
function checkModuleDeclaration(node: ModuleDeclaration) {

src/compiler/diagnosticInformationMap.generated.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,7 @@ module ts {
191191
Class_0_defines_instance_member_property_1_but_extended_class_2_defines_it_as_instance_member_function: { code: 4017, category: DiagnosticCategory.NoPrefix, key: "Class '{0}' defines instance member property '{1}', but extended class '{2}' defines it as instance member function." },
192192
Class_0_defines_instance_member_function_1_but_extended_class_2_defines_it_as_instance_member_accessor: { code: 4018, category: DiagnosticCategory.NoPrefix, key: "Class '{0}' defines instance member function '{1}', but extended class '{2}' defines it as instance member accessor." },
193193
Class_0_defines_instance_member_function_1_but_extended_class_2_defines_it_as_instance_member_property: { code: 4019, category: DiagnosticCategory.NoPrefix, key: "Class '{0}' defines instance member function '{1}', but extended class '{2}' defines it as instance member property." },
194+
In_an_enum_with_multiple_declarations_only_one_declaration_can_omit_an_initializer_for_its_first_enum_element: { code: 4024, category: DiagnosticCategory.Error, key: "In an enum with multiple declarations, only one declaration can omit an initializer for its first enum element." },
194195
Named_properties_0_of_types_1_and_2_are_not_identical: { code: 4032, category: DiagnosticCategory.NoPrefix, key: "Named properties '{0}' of types '{1}' and '{2}' are not identical." },
195196
Cannot_find_the_common_subdirectory_path_for_the_input_files: { code: 5009, category: DiagnosticCategory.Error, key: "Cannot find the common subdirectory path for the input files." },
196197
Cannot_read_file_0_Colon_1: { code: 5012, category: DiagnosticCategory.Error, key: "Cannot read file '{0}': {1}" },

src/compiler/diagnosticMessages.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -763,6 +763,10 @@
763763
"category": "NoPrefix",
764764
"code": 4019
765765
},
766+
"In an enum with multiple declarations, only one declaration can omit an initializer for its first enum element.": {
767+
"category": "Error",
768+
"code": 4024
769+
},
766770
"Named properties '{0}' of types '{1}' and '{2}' are not identical.": {
767771
"category": "NoPrefix",
768772
"code": 4032

tests/baselines/reference/augmentedTypesEnum.errors.txt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
==== tests/cases/compiler/augmentedTypesEnum.ts (5 errors) ====
1+
==== tests/cases/compiler/augmentedTypesEnum.ts (7 errors) ====
22
// enum then var
33
enum e1111 { One }
44
var e1111 = 1; // error
@@ -25,11 +25,15 @@
2525
// enum then enum
2626
enum e5 { One }
2727
enum e5 { Two }
28+
~~~
29+
!!! In an enum with multiple declarations, only one declaration can omit an initializer for its first enum element.
2830

2931
enum e5a { One }
3032
enum e5a { One } // error
3133
~~~
3234
!!! Duplicate identifier 'One'.
35+
~~~
36+
!!! In an enum with multiple declarations, only one declaration can omit an initializer for its first enum element.
3337

3438
// enum then internal module
3539
enum e6 { One }
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
==== tests/cases/compiler/augmentedTypesEnum3.ts (1 errors) ====
2+
module E {
3+
var t;
4+
}
5+
enum E { }
6+
7+
enum F { }
8+
module F { var t; }
9+
10+
module A {
11+
var o;
12+
}
13+
enum A {
14+
b
15+
}
16+
enum A {
17+
c
18+
~
19+
!!! In an enum with multiple declarations, only one declaration can omit an initializer for its first enum element.
20+
}
21+
module A {
22+
var p;
23+
}

tests/baselines/reference/enumBasics1.errors.txt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
==== tests/cases/compiler/enumBasics1.ts (1 errors) ====
1+
==== tests/cases/compiler/enumBasics1.ts (2 errors) ====
22
enum E {
33
A = 1,
44
B,
@@ -34,7 +34,9 @@
3434
B,
3535
}
3636

37-
enum E2 { // shouldn't error
37+
enum E2 { // should error for continued autonumbering
3838
C,
39+
~
40+
!!! In an enum with multiple declarations, only one declaration can omit an initializer for its first enum element.
3941
D,
4042
}

tests/baselines/reference/enumBasics1.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ enum E2 {
3232
B,
3333
}
3434

35-
enum E2 { // shouldn't error
35+
enum E2 { // should error for continued autonumbering
3636
C,
3737
D,
3838
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
==== tests/cases/conformance/enums/enumMergingErrors.ts (2 errors) ====
2+
// Enum with constant, computed, constant members split across 3 declarations with the same root module
3+
module M {
4+
export enum E1 { A = 0 }
5+
export enum E2 { C }
6+
export enum E3 { A = 0 }
7+
}
8+
module M {
9+
export enum E1 { B = 'foo'.length }
10+
export enum E2 { B = 'foo'.length }
11+
export enum E3 { C }
12+
}
13+
module M {
14+
export enum E1 { C }
15+
export enum E2 { A = 0 }
16+
export enum E3 { B = 'foo'.length }
17+
}
18+
19+
// Enum with no initializer in either declaration with constant members with the same root module
20+
module M1 {
21+
export enum E1 { A = 0 }
22+
}
23+
module M1 {
24+
export enum E1 { B }
25+
}
26+
module M1 {
27+
export enum E1 { C }
28+
~
29+
!!! In an enum with multiple declarations, only one declaration can omit an initializer for its first enum element.
30+
}
31+
32+
33+
// Enum with initializer in only one of three declarations with constant members with the same root module
34+
module M2 {
35+
export enum E1 { A }
36+
}
37+
module M2 {
38+
export enum E1 { B = 0 }
39+
}
40+
module M2 {
41+
export enum E1 { C }
42+
~
43+
!!! In an enum with multiple declarations, only one declaration can omit an initializer for its first enum element.
44+
}
45+
46+
47+
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
==== tests/cases/compiler/enumsWithMultipleDeclarations1.ts (2 errors) ====
2+
enum E {
3+
A
4+
}
5+
6+
enum E {
7+
B
8+
~
9+
!!! In an enum with multiple declarations, only one declaration can omit an initializer for its first enum element.
10+
}
11+
12+
enum E {
13+
C
14+
~
15+
!!! In an enum with multiple declarations, only one declaration can omit an initializer for its first enum element.
16+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
==== tests/cases/compiler/enumsWithMultipleDeclarations2.ts (1 errors) ====
2+
enum E {
3+
A
4+
}
5+
6+
enum E {
7+
B = 1
8+
}
9+
10+
enum E {
11+
C
12+
~
13+
!!! In an enum with multiple declarations, only one declaration can omit an initializer for its first enum element.
14+
}

tests/cases/compiler/enumBasics1.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ enum E2 {
3131
B,
3232
}
3333

34-
enum E2 { // shouldn't error
34+
enum E2 { // should error for continued autonumbering
3535
C,
3636
D,
3737
}

0 commit comments

Comments
 (0)