Skip to content

Commit ce336bc

Browse files
committed
added folding for references to enum members in enum member initializers, added tests
1 parent 97460f5 commit ce336bc

File tree

5 files changed

+566
-15
lines changed

5 files changed

+566
-15
lines changed

src/compiler/checker.ts

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7473,18 +7473,31 @@ module ts {
74737473
case SyntaxKind.PropertyAccess:
74747474
if (!program.getCompilerOptions().propagateEnumConstants) return undefined;
74757475

7476-
var refSymbol =
7477-
e.kind === SyntaxKind.Identifier
7478-
? resolveName(member, (<Identifier>e).text, SymbolFlags.EnumMember, /*nameNotFoundMessage*/ undefined, /*nameArg*/ undefined)
7479-
: resolveEntityName(member, e, SymbolFlags.EnumMember, /*suppressErrors*/ true);
7480-
7481-
if (!refSymbol) return undefined;
7482-
var refDecl = <EnumMember>refSymbol.valueDeclaration;
7483-
// self references are not permitted
7484-
// non-qualified names are permitted only to members defined in the same enum
7485-
if (member === refDecl || (e.kind === SyntaxKind.Identifier && refDecl.parent !== member.parent)) return undefined;
7486-
// enumMemberValue might be undefined if corresponding enum value was not yet computed and it is ok to return undefined in this case
7487-
return <number>getNodeLinks(refDecl).enumMemberValue;
7476+
var enumSymbol: Symbol;
7477+
var propertyName: string;
7478+
7479+
if (e.kind === SyntaxKind.Identifier) {
7480+
// unqualified names can refer to member that reside in different declaration of the enum so just doing name resolution won't work.
7481+
// instead pick symbol that correspond of enum declaration and later try to fetch member from the symbol
7482+
enumSymbol = getSymbolOfNode(member.parent);
7483+
propertyName = (<Identifier>e).text;
7484+
}
7485+
else {
7486+
// left part in PropertyAccess should be resolved to the symbol of enum that declared 'member'
7487+
enumSymbol = resolveEntityName(member, (<PropertyAccess>e).left, SymbolFlags.Enum, /*suppressErrors*/ true);
7488+
7489+
if (enumSymbol !== getSymbolOfNode(member.parent)) return undefined;
7490+
propertyName = (<Identifier>(<PropertyAccess>e).right).text;
7491+
}
7492+
7493+
var propertySymbol = enumSymbol.exports[propertyName];
7494+
if (!propertyName || !(propertySymbol.flags & SymbolFlags.EnumMember)) return undefined;
7495+
var propertyDecl = <EnumMember>propertySymbol.valueDeclaration;
7496+
// self references are illegal
7497+
if (member === propertyDecl) return undefined;
7498+
// enumMemberValue might be undefined if corresponding enum value was not yet computed
7499+
// and it is ok to return undefined in this case (use before defition)
7500+
return <number>getNodeLinks(propertyDecl).enumMemberValue;
74887501
}
74897502
}
74907503
}

src/harness/harness.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -749,6 +749,9 @@ module Harness {
749749
case 'usecasesensitivefilenames':
750750
useCaseSensitiveFileNames = setting.value === 'true';
751751
break;
752+
case 'propagateenumconstants':
753+
options.propagateEnumConstants = setting.value === 'true';
754+
break;
752755

753756
case 'mapsourcefiles':
754757
case 'maproot':
@@ -757,7 +760,6 @@ module Harness {
757760
case 'codepage':
758761
case 'createFileLog':
759762
case 'filename':
760-
case 'propagateenumconstants':
761763
case 'removecomments':
762764
case 'watch':
763765
case 'allowautomaticsemicoloninsertion':
@@ -772,7 +774,6 @@ module Harness {
772774
case 'errortruncation':
773775
options.noErrorTruncation = setting.value === 'false';
774776
break;
775-
776777
default:
777778
throw new Error('Unsupported compiler setting ' + setting.flag);
778779
}
@@ -1147,7 +1148,7 @@ module Harness {
11471148
var optionRegex = /^[\/]{2}\s*@(\w+)\s*:\s*(\S*)/gm; // multiple matches on multiple lines
11481149

11491150
// List of allowed metadata names
1150-
var fileMetadataNames = ["filename", "comments", "declaration", "module", "nolib", "sourcemap", "target", "out", "outdir", "noimplicitany", "noresolve", "newline", "newlines", "emitbom", "errortruncation", "usecasesensitivefilenames"];
1151+
var fileMetadataNames = ["filename", "comments", "declaration", "module", "nolib", "sourcemap", "target", "out", "outdir", "noimplicitany", "noresolve", "newline", "newlines", "emitbom", "errortruncation", "usecasesensitivefilenames", "propagateenumconstants"];
11511152

11521153
function extractCompilerSettings(content: string): CompilerSetting[] {
11531154

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
//// [constantsInEnumMembers.ts]
2+
3+
enum Enum1 {
4+
A0 = 100,
5+
}
6+
7+
enum Enum1 {
8+
// correct cases
9+
A,
10+
B,
11+
C = 10,
12+
D = A + B,
13+
E = A + 1,
14+
F = 1 + A,
15+
G = 1 + 1,
16+
H = A - B,
17+
I = A - 1,
18+
J = 1 - A,
19+
K = 1 - 1,
20+
L = ~D,
21+
M = E << B,
22+
N = E << 1,
23+
O = E >> B,
24+
P = E >> 1,
25+
Q = -D,
26+
R = C & 5,
27+
S = 5 & C,
28+
T = C | D,
29+
U = C | 1,
30+
V = 10 | D,
31+
W = Enum1.V,
32+
33+
// correct cases: reference to the enum member from different enum declaration
34+
W1 = A0,
35+
W2 = Enum1.A0,
36+
37+
// illegal case
38+
// forward reference to the element of the same enum
39+
X = Y,
40+
// forward reference to the element of the same enum
41+
Y = Enum1.Z,
42+
Z = 100,
43+
}
44+
45+
46+
function foo(x: Enum1) {
47+
switch (x) {
48+
case Enum1.A:
49+
case Enum1.B:
50+
case Enum1.C:
51+
case Enum1.D:
52+
case Enum1.E:
53+
case Enum1.F:
54+
case Enum1.G:
55+
case Enum1.H:
56+
case Enum1.I:
57+
case Enum1.J:
58+
case Enum1.K:
59+
case Enum1.L:
60+
case Enum1.M:
61+
case Enum1.N:
62+
case Enum1.O:
63+
case Enum1.P:
64+
case Enum1.Q:
65+
case Enum1.R:
66+
case Enum1.S:
67+
case Enum1.T:
68+
case Enum1.U:
69+
case Enum1.V:
70+
case Enum1.W:
71+
case Enum1.W1:
72+
case Enum1.W2:
73+
case Enum1.X:
74+
case Enum1.Y:
75+
case Enum1.Z:
76+
break;
77+
}
78+
}
79+
80+
//// [constantsInEnumMembers.js]
81+
var Enum1;
82+
(function (Enum1) {
83+
Enum1[Enum1["A0"] = 100] = "A0";
84+
})(Enum1 || (Enum1 = {}));
85+
var Enum1;
86+
(function (Enum1) {
87+
// correct cases
88+
Enum1[Enum1["A"] = 0] = "A";
89+
Enum1[Enum1["B"] = 1] = "B";
90+
Enum1[Enum1["C"] = 10] = "C";
91+
Enum1[Enum1["D"] = A + B] = "D";
92+
Enum1[Enum1["E"] = A + 1] = "E";
93+
Enum1[Enum1["F"] = 1 + A] = "F";
94+
Enum1[Enum1["G"] = 1 + 1] = "G";
95+
Enum1[Enum1["H"] = A - B] = "H";
96+
Enum1[Enum1["I"] = A - 1] = "I";
97+
Enum1[Enum1["J"] = 1 - A] = "J";
98+
Enum1[Enum1["K"] = 1 - 1] = "K";
99+
Enum1[Enum1["L"] = ~D] = "L";
100+
Enum1[Enum1["M"] = E << B] = "M";
101+
Enum1[Enum1["N"] = E << 1] = "N";
102+
Enum1[Enum1["O"] = E >> B] = "O";
103+
Enum1[Enum1["P"] = E >> 1] = "P";
104+
Enum1[Enum1["Q"] = -D] = "Q";
105+
Enum1[Enum1["R"] = C & 5] = "R";
106+
Enum1[Enum1["S"] = 5 & C] = "S";
107+
Enum1[Enum1["T"] = C | D] = "T";
108+
Enum1[Enum1["U"] = C | 1] = "U";
109+
Enum1[Enum1["V"] = 10 | D] = "V";
110+
Enum1[Enum1["W"] = Enum1.V] = "W";
111+
// correct cases: reference to the enum member from different enum declaration
112+
Enum1[Enum1["W1"] = A0] = "W1";
113+
Enum1[Enum1["W2"] = Enum1.A0] = "W2";
114+
// illegal case
115+
// forward reference to the element of the same enum
116+
Enum1[Enum1["X"] = Enum1.Y] = "X";
117+
// forward reference to the element of the same enum
118+
Enum1[Enum1["Y"] = 100 /* Z */] = "Y";
119+
Enum1[Enum1["Z"] = 100] = "Z";
120+
})(Enum1 || (Enum1 = {}));
121+
function foo(x) {
122+
switch (x) {
123+
case 0 /* A */:
124+
case 1 /* B */:
125+
case 10 /* C */:
126+
case 1 /* D */:
127+
case 1 /* E */:
128+
case 1 /* F */:
129+
case 2 /* G */:
130+
case -1 /* H */:
131+
case -1 /* I */:
132+
case 1 /* J */:
133+
case 0 /* K */:
134+
case -2 /* L */:
135+
case 2 /* M */:
136+
case 2 /* N */:
137+
case 0 /* O */:
138+
case 0 /* P */:
139+
case -1 /* Q */:
140+
case 0 /* R */:
141+
case 0 /* S */:
142+
case 11 /* T */:
143+
case 11 /* U */:
144+
case 11 /* V */:
145+
case 11 /* W */:
146+
case 100 /* W1 */:
147+
case 100 /* W2 */:
148+
case Enum1.X:
149+
case Enum1.Y:
150+
case 100 /* Z */:
151+
break;
152+
}
153+
}

0 commit comments

Comments
 (0)