Skip to content

Commit 365587f

Browse files
committed
addressed CR feedback, added support for indexed access
1 parent ce336bc commit 365587f

File tree

4 files changed

+327
-43
lines changed

4 files changed

+327
-43
lines changed

src/compiler/checker.ts

+78-41
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,22 @@ module ts {
306306
// return undefined if we can't find a symbol.
307307
}
308308

309+
/** Returns true if node1 is defined before node 2**/
310+
function isDefinedBefore(node1: Node, node2: Node): boolean {
311+
var file1 = getSourceFileOfNode(node1);
312+
var file2 = getSourceFileOfNode(node2);
313+
if (file1 === file2) {
314+
return node1.pos <= node2.pos;
315+
}
316+
317+
if (!compilerOptions.out) {
318+
return true;
319+
}
320+
321+
var sourceFiles = program.getSourceFiles();
322+
return sourceFiles.indexOf(file1) <= sourceFiles.indexOf(file2);
323+
}
324+
309325
function resolveName(location: Node, name: string, meaning: SymbolFlags, nameNotFoundMessage: DiagnosticMessage, nameArg: string): Symbol {
310326
var errorLocation = location;
311327
var result: Symbol;
@@ -330,18 +346,8 @@ module ts {
330346
// Block-scoped variables can not be used before their definition
331347
var declaration = forEach(s.declarations, d => d.flags & NodeFlags.BlockScoped ? d : undefined);
332348
Debug.assert(declaration, "Block-scoped variable declaration is undefined");
333-
var declarationSourceFile = getSourceFileOfNode(declaration);
334-
var referenceSourceFile = getSourceFileOfNode(errorLocation);
335-
if (declarationSourceFile === referenceSourceFile) {
336-
if (declaration.pos > errorLocation.pos) {
337-
error(errorLocation, Diagnostics.Block_scoped_variable_0_used_before_its_declaration, identifierToString(declaration.name));
338-
}
339-
}
340-
else if (compilerOptions.out) {
341-
var sourceFiles = program.getSourceFiles();
342-
if (sourceFiles.indexOf(referenceSourceFile) < sourceFiles.indexOf(declarationSourceFile)) {
343-
error(errorLocation, Diagnostics.Block_scoped_variable_0_used_before_its_declaration, identifierToString(declaration.name));
344-
}
349+
if (!isDefinedBefore(declaration, errorLocation)) {
350+
error(errorLocation, Diagnostics.Block_scoped_variable_0_used_before_its_declaration, identifierToString(declaration.name));
345351
}
346352
}
347353
return s;
@@ -489,12 +495,10 @@ module ts {
489495
}
490496

491497
// Resolves a qualified name and any involved import aliases
492-
function resolveEntityName(location: Node, name: EntityName, meaning: SymbolFlags, suppressErrors?: boolean): Symbol {
498+
function resolveEntityName(location: Node, name: EntityName, meaning: SymbolFlags): Symbol {
493499
if (name.kind === SyntaxKind.Identifier) {
494500
// TODO: Investigate error recovery for symbols not found
495-
var nameNotFoundMessage = !suppressErrors && Diagnostics.Cannot_find_name_0;
496-
var nameArg = !suppressErrors && identifierToString(<Identifier>name);
497-
var symbol = resolveName(location, (<Identifier>name).text, meaning, nameNotFoundMessage, nameArg);
501+
var symbol = resolveName(location, (<Identifier>name).text, meaning, Diagnostics.Cannot_find_name_0, identifierToString(<Identifier>name));
498502
if (!symbol) {
499503
return;
500504
}
@@ -504,10 +508,8 @@ module ts {
504508
if (!namespace || namespace === unknownSymbol || (<QualifiedName>name).right.kind === SyntaxKind.Missing) return;
505509
var symbol = getSymbol(namespace.exports, (<QualifiedName>name).right.text, meaning);
506510
if (!symbol) {
507-
if (!suppressErrors) {
508-
error(location, Diagnostics.Module_0_has_no_exported_member_1, getFullyQualifiedName(namespace),
509-
identifierToString((<QualifiedName>name).right));
510-
}
511+
error(location, Diagnostics.Module_0_has_no_exported_member_1, getFullyQualifiedName(namespace),
512+
identifierToString((<QualifiedName>name).right));
511513
return;
512514
}
513515
}
@@ -7410,13 +7412,13 @@ module ts {
74107412
var autoValue = 0;
74117413
var ambient = isInAmbientContext(node);
74127414

7413-
forEach(node.members, member => {
7415+
forEach(node.members, member => {
74147416
if(isNumericName(member.name.text)) {
74157417
error(member.name, Diagnostics.An_enum_member_cannot_have_a_numeric_name);
74167418
}
74177419
var initializer = member.initializer;
74187420
if (initializer) {
7419-
autoValue = getConstantValueForEnumMemberInitializer(member, initializer);
7421+
autoValue = getConstantValueForEnumMemberInitializer(initializer);
74207422
if (autoValue === undefined && !ambient) {
74217423
// Only here do we need to check that the initializer is assignable to the enum type.
74227424
// If it is a constant value (not undefined), it is syntactically constrained to be a number.
@@ -7437,66 +7439,101 @@ module ts {
74377439
nodeLinks.flags |= NodeCheckFlags.EnumValuesComputed;
74387440
}
74397441

7440-
function getConstantValueForEnumMemberInitializer(member: EnumMember, initializer: Expression): number {
7442+
function getConstantValueForEnumMemberInitializer(initializer: Expression): number {
74417443
return evalConstant(initializer);
74427444

74437445
function evalConstant(e: Node): number {
74447446
switch (e.kind) {
74457447
case SyntaxKind.PrefixOperator:
74467448
var value = evalConstant((<UnaryExpression>e).operand);
7447-
if (value === undefined) return undefined;
7449+
if (value === undefined) {
7450+
return undefined;
7451+
}
74487452
switch ((<UnaryExpression>e).operator) {
74497453
case SyntaxKind.PlusToken: return value;
74507454
case SyntaxKind.MinusToken: return -value;
7451-
case SyntaxKind.TildeToken: return ~value;
7455+
case SyntaxKind.TildeToken: return compilerOptions.propagateEnumConstants ? ~value : undefined;
74527456
}
74537457
return undefined;
74547458
case SyntaxKind.BinaryExpression:
7455-
if (!program.getCompilerOptions().propagateEnumConstants) return undefined;
7459+
if (!compilerOptions.propagateEnumConstants) {
7460+
return undefined;
7461+
}
74567462

74577463
var left = evalConstant((<BinaryExpression>e).left);
7458-
if (left === undefined) return undefined;
7464+
if (left === undefined) {
7465+
return undefined;
7466+
}
74597467
var right = evalConstant((<BinaryExpression>e).right);
7460-
if (right === undefined) return undefined;
7468+
if (right === undefined) {
7469+
return undefined;
7470+
}
74617471
switch ((<BinaryExpression>e).operator) {
74627472
case SyntaxKind.BarToken: return left | right;
74637473
case SyntaxKind.AmpersandToken: return left & right;
74647474
case SyntaxKind.PlusToken: return left + right;
74657475
case SyntaxKind.MinusToken: return left - right;
74667476
case SyntaxKind.GreaterThanGreaterThanToken: return left >> right;
7477+
case SyntaxKind.GreaterThanGreaterThanGreaterThanToken: return left >>> right;
74677478
case SyntaxKind.LessThanLessThanToken: return left << right;
74687479
}
74697480
return undefined;
74707481
case SyntaxKind.NumericLiteral:
74717482
return +(<LiteralExpression>e).text;
74727483
case SyntaxKind.Identifier:
7484+
case SyntaxKind.IndexedAccess:
74737485
case SyntaxKind.PropertyAccess:
7474-
if (!program.getCompilerOptions().propagateEnumConstants) return undefined;
7486+
if (!compilerOptions.propagateEnumConstants) {
7487+
return undefined;
7488+
}
74757489

7476-
var enumSymbol: Symbol;
7490+
var member = initializer.parent;
7491+
var currentType = getTypeOfSymbol(getSymbolOfNode(member.parent));
7492+
var enumType: Type;
74777493
var propertyName: string;
74787494

74797495
if (e.kind === SyntaxKind.Identifier) {
74807496
// unqualified names can refer to member that reside in different declaration of the enum so just doing name resolution won't work.
74817497
// instead pick symbol that correspond of enum declaration and later try to fetch member from the symbol
7482-
enumSymbol = getSymbolOfNode(member.parent);
7498+
enumType = currentType;
74837499
propertyName = (<Identifier>e).text;
74847500
}
7501+
else if (e.kind === SyntaxKind.IndexedAccess) {
7502+
if ((<IndexedAccess>e).index.kind !== SyntaxKind.StringLiteral) {
7503+
return undefined;
7504+
}
7505+
var enumType = getTypeOfNode((<IndexedAccess>e).object);
7506+
if (enumType !== currentType) {
7507+
return undefined;
7508+
}
7509+
propertyName = (<LiteralExpression>(<IndexedAccess>e).index).text;
7510+
}
74857511
else {
74867512
// 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;
7513+
var enumType = getTypeOfNode((<PropertyAccess>e).left);
7514+
if (enumType !== currentType) {
7515+
return undefined;
7516+
}
7517+
propertyName = (<PropertyAccess>e).right.text;
74917518
}
74927519

7493-
var propertySymbol = enumSymbol.exports[propertyName];
7494-
if (!propertyName || !(propertySymbol.flags & SymbolFlags.EnumMember)) return undefined;
7495-
var propertyDecl = <EnumMember>propertySymbol.valueDeclaration;
7520+
if (propertyName === undefined) {
7521+
return undefined;
7522+
}
7523+
var property = getPropertyOfObjectType(enumType, propertyName);
7524+
if (!(property.flags & SymbolFlags.EnumMember)) {
7525+
return undefined;
7526+
}
7527+
var propertyDecl = <EnumMember>property.valueDeclaration;
74967528
// 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)
7529+
if (member === propertyDecl) {
7530+
return undefined;
7531+
}
7532+
7533+
// illegal case: forward reference
7534+
if (!isDefinedBefore(propertyDecl, member)) {
7535+
return undefined;
7536+
}
75007537
return <number>getNodeLinks(propertyDecl).enumMemberValue;
75017538
}
75027539
}

tests/baselines/reference/constantsInEnumMembers.js

+78-1
Original file line numberDiff line numberDiff line change
@@ -33,16 +33,39 @@ enum Enum1 {
3333
// correct cases: reference to the enum member from different enum declaration
3434
W1 = A0,
3535
W2 = Enum1.A0,
36-
36+
W3 = Enum1["A0"],
37+
W4 = Enum1["W"],
3738
// illegal case
3839
// forward reference to the element of the same enum
3940
X = Y,
4041
// forward reference to the element of the same enum
4142
Y = Enum1.Z,
43+
Y1 = Enum1["Z"],
4244
Z = 100,
4345
}
4446

4547

48+
module A {
49+
export module B {
50+
export module C {
51+
export enum E {
52+
V1 = 1,
53+
V2 = A.B.C.E.V1 + 100
54+
}
55+
}
56+
}
57+
}
58+
59+
module A {
60+
export module B {
61+
export module C {
62+
export enum E {
63+
V3 = A.B.C.E["V2"] + 200,
64+
}
65+
}
66+
}
67+
}
68+
4669
function foo(x: Enum1) {
4770
switch (x) {
4871
case Enum1.A:
@@ -70,11 +93,22 @@ function foo(x: Enum1) {
7093
case Enum1.W:
7194
case Enum1.W1:
7295
case Enum1.W2:
96+
case Enum1.W3:
97+
case Enum1.W4:
7398
case Enum1.X:
7499
case Enum1.Y:
100+
case Enum1.Y1:
75101
case Enum1.Z:
76102
break;
77103
}
104+
}
105+
106+
function bar(e: A.B.C.E): number {
107+
switch (e) {
108+
case A.B.C.E.V1: return 1;
109+
case A.B.C.E.V2: return 1;
110+
case A.B.C.E.V3: return 1;
111+
}
78112
}
79113

80114
//// [constantsInEnumMembers.js]
@@ -111,13 +145,43 @@ var Enum1;
111145
// correct cases: reference to the enum member from different enum declaration
112146
Enum1[Enum1["W1"] = A0] = "W1";
113147
Enum1[Enum1["W2"] = Enum1.A0] = "W2";
148+
Enum1[Enum1["W3"] = Enum1["A0"]] = "W3";
149+
Enum1[Enum1["W4"] = Enum1["W"]] = "W4";
114150
// illegal case
115151
// forward reference to the element of the same enum
116152
Enum1[Enum1["X"] = Enum1.Y] = "X";
117153
// forward reference to the element of the same enum
118154
Enum1[Enum1["Y"] = 100 /* Z */] = "Y";
155+
Enum1[Enum1["Y1"] = Enum1["Z"]] = "Y1";
119156
Enum1[Enum1["Z"] = 100] = "Z";
120157
})(Enum1 || (Enum1 = {}));
158+
var A;
159+
(function (A) {
160+
var B;
161+
(function (B) {
162+
var C;
163+
(function (C) {
164+
(function (E) {
165+
E[E["V1"] = 1] = "V1";
166+
E[E["V2"] = A.B.C.E.V1 + 100] = "V2";
167+
})(C.E || (C.E = {}));
168+
var E = C.E;
169+
})(C = B.C || (B.C = {}));
170+
})(B = A.B || (A.B = {}));
171+
})(A || (A = {}));
172+
var A;
173+
(function (A) {
174+
var B;
175+
(function (B) {
176+
var C;
177+
(function (C) {
178+
(function (E) {
179+
E[E["V3"] = A.B.C.E["V2"] + 200] = "V3";
180+
})(C.E || (C.E = {}));
181+
var E = C.E;
182+
})(C = B.C || (B.C = {}));
183+
})(B = A.B || (A.B = {}));
184+
})(A || (A = {}));
121185
function foo(x) {
122186
switch (x) {
123187
case 0 /* A */:
@@ -145,9 +209,22 @@ function foo(x) {
145209
case 11 /* W */:
146210
case 100 /* W1 */:
147211
case 100 /* W2 */:
212+
case 100 /* W3 */:
213+
case 11 /* W4 */:
148214
case Enum1.X:
149215
case Enum1.Y:
216+
case Enum1.Y1:
150217
case 100 /* Z */:
151218
break;
152219
}
153220
}
221+
function bar(e) {
222+
switch (e) {
223+
case 1 /* V1 */:
224+
return 1;
225+
case 101 /* V2 */:
226+
return 1;
227+
case 301 /* V3 */:
228+
return 1;
229+
}
230+
}

0 commit comments

Comments
 (0)