added folding for references to enum members in enum member initializers, added tests

This commit is contained in:
Vladimir Matveev 2014-10-26 16:40:02 -07:00
parent 97460f5123
commit ce336bcec7
5 changed files with 565 additions and 14 deletions

View File

@ -7473,18 +7473,31 @@ module ts {
case SyntaxKind.PropertyAccess:
if (!program.getCompilerOptions().propagateEnumConstants) return undefined;
var refSymbol =
e.kind === SyntaxKind.Identifier
? resolveName(member, (<Identifier>e).text, SymbolFlags.EnumMember, /*nameNotFoundMessage*/ undefined, /*nameArg*/ undefined)
: resolveEntityName(member, e, SymbolFlags.EnumMember, /*suppressErrors*/ true);
var enumSymbol: Symbol;
var propertyName: string;
if (!refSymbol) return undefined;
var refDecl = <EnumMember>refSymbol.valueDeclaration;
// self references are not permitted
// non-qualified names are permitted only to members defined in the same enum
if (member === refDecl || (e.kind === SyntaxKind.Identifier && refDecl.parent !== member.parent)) return undefined;
// enumMemberValue might be undefined if corresponding enum value was not yet computed and it is ok to return undefined in this case
return <number>getNodeLinks(refDecl).enumMemberValue;
if (e.kind === SyntaxKind.Identifier) {
// unqualified names can refer to member that reside in different declaration of the enum so just doing name resolution won't work.
// instead pick symbol that correspond of enum declaration and later try to fetch member from the symbol
enumSymbol = getSymbolOfNode(member.parent);
propertyName = (<Identifier>e).text;
}
else {
// left part in PropertyAccess should be resolved to the symbol of enum that declared 'member'
enumSymbol = resolveEntityName(member, (<PropertyAccess>e).left, SymbolFlags.Enum, /*suppressErrors*/ true);
if (enumSymbol !== getSymbolOfNode(member.parent)) return undefined;
propertyName = (<Identifier>(<PropertyAccess>e).right).text;
}
var propertySymbol = enumSymbol.exports[propertyName];
if (!propertyName || !(propertySymbol.flags & SymbolFlags.EnumMember)) return undefined;
var propertyDecl = <EnumMember>propertySymbol.valueDeclaration;
// self references are illegal
if (member === propertyDecl) return undefined;
// enumMemberValue might be undefined if corresponding enum value was not yet computed
// and it is ok to return undefined in this case (use before defition)
return <number>getNodeLinks(propertyDecl).enumMemberValue;
}
}
}

View File

@ -749,6 +749,9 @@ module Harness {
case 'usecasesensitivefilenames':
useCaseSensitiveFileNames = setting.value === 'true';
break;
case 'propagateenumconstants':
options.propagateEnumConstants = setting.value === 'true';
break;
case 'mapsourcefiles':
case 'maproot':
@ -757,7 +760,6 @@ module Harness {
case 'codepage':
case 'createFileLog':
case 'filename':
case 'propagateenumconstants':
case 'removecomments':
case 'watch':
case 'allowautomaticsemicoloninsertion':
@ -772,7 +774,6 @@ module Harness {
case 'errortruncation':
options.noErrorTruncation = setting.value === 'false';
break;
default:
throw new Error('Unsupported compiler setting ' + setting.flag);
}
@ -1147,7 +1148,7 @@ module Harness {
var optionRegex = /^[\/]{2}\s*@(\w+)\s*:\s*(\S*)/gm; // multiple matches on multiple lines
// List of allowed metadata names
var fileMetadataNames = ["filename", "comments", "declaration", "module", "nolib", "sourcemap", "target", "out", "outdir", "noimplicitany", "noresolve", "newline", "newlines", "emitbom", "errortruncation", "usecasesensitivefilenames"];
var fileMetadataNames = ["filename", "comments", "declaration", "module", "nolib", "sourcemap", "target", "out", "outdir", "noimplicitany", "noresolve", "newline", "newlines", "emitbom", "errortruncation", "usecasesensitivefilenames", "propagateenumconstants"];
function extractCompilerSettings(content: string): CompilerSetting[] {

View File

@ -0,0 +1,153 @@
//// [constantsInEnumMembers.ts]
enum Enum1 {
A0 = 100,
}
enum Enum1 {
// correct cases
A,
B,
C = 10,
D = A + B,
E = A + 1,
F = 1 + A,
G = 1 + 1,
H = A - B,
I = A - 1,
J = 1 - A,
K = 1 - 1,
L = ~D,
M = E << B,
N = E << 1,
O = E >> B,
P = E >> 1,
Q = -D,
R = C & 5,
S = 5 & C,
T = C | D,
U = C | 1,
V = 10 | D,
W = Enum1.V,
// correct cases: reference to the enum member from different enum declaration
W1 = A0,
W2 = Enum1.A0,
// illegal case
// forward reference to the element of the same enum
X = Y,
// forward reference to the element of the same enum
Y = Enum1.Z,
Z = 100,
}
function foo(x: Enum1) {
switch (x) {
case Enum1.A:
case Enum1.B:
case Enum1.C:
case Enum1.D:
case Enum1.E:
case Enum1.F:
case Enum1.G:
case Enum1.H:
case Enum1.I:
case Enum1.J:
case Enum1.K:
case Enum1.L:
case Enum1.M:
case Enum1.N:
case Enum1.O:
case Enum1.P:
case Enum1.Q:
case Enum1.R:
case Enum1.S:
case Enum1.T:
case Enum1.U:
case Enum1.V:
case Enum1.W:
case Enum1.W1:
case Enum1.W2:
case Enum1.X:
case Enum1.Y:
case Enum1.Z:
break;
}
}
//// [constantsInEnumMembers.js]
var Enum1;
(function (Enum1) {
Enum1[Enum1["A0"] = 100] = "A0";
})(Enum1 || (Enum1 = {}));
var Enum1;
(function (Enum1) {
// correct cases
Enum1[Enum1["A"] = 0] = "A";
Enum1[Enum1["B"] = 1] = "B";
Enum1[Enum1["C"] = 10] = "C";
Enum1[Enum1["D"] = A + B] = "D";
Enum1[Enum1["E"] = A + 1] = "E";
Enum1[Enum1["F"] = 1 + A] = "F";
Enum1[Enum1["G"] = 1 + 1] = "G";
Enum1[Enum1["H"] = A - B] = "H";
Enum1[Enum1["I"] = A - 1] = "I";
Enum1[Enum1["J"] = 1 - A] = "J";
Enum1[Enum1["K"] = 1 - 1] = "K";
Enum1[Enum1["L"] = ~D] = "L";
Enum1[Enum1["M"] = E << B] = "M";
Enum1[Enum1["N"] = E << 1] = "N";
Enum1[Enum1["O"] = E >> B] = "O";
Enum1[Enum1["P"] = E >> 1] = "P";
Enum1[Enum1["Q"] = -D] = "Q";
Enum1[Enum1["R"] = C & 5] = "R";
Enum1[Enum1["S"] = 5 & C] = "S";
Enum1[Enum1["T"] = C | D] = "T";
Enum1[Enum1["U"] = C | 1] = "U";
Enum1[Enum1["V"] = 10 | D] = "V";
Enum1[Enum1["W"] = Enum1.V] = "W";
// correct cases: reference to the enum member from different enum declaration
Enum1[Enum1["W1"] = A0] = "W1";
Enum1[Enum1["W2"] = Enum1.A0] = "W2";
// illegal case
// forward reference to the element of the same enum
Enum1[Enum1["X"] = Enum1.Y] = "X";
// forward reference to the element of the same enum
Enum1[Enum1["Y"] = 100 /* Z */] = "Y";
Enum1[Enum1["Z"] = 100] = "Z";
})(Enum1 || (Enum1 = {}));
function foo(x) {
switch (x) {
case 0 /* A */:
case 1 /* B */:
case 10 /* C */:
case 1 /* D */:
case 1 /* E */:
case 1 /* F */:
case 2 /* G */:
case -1 /* H */:
case -1 /* I */:
case 1 /* J */:
case 0 /* K */:
case -2 /* L */:
case 2 /* M */:
case 2 /* N */:
case 0 /* O */:
case 0 /* P */:
case -1 /* Q */:
case 0 /* R */:
case 0 /* S */:
case 11 /* T */:
case 11 /* U */:
case 11 /* V */:
case 11 /* W */:
case 100 /* W1 */:
case 100 /* W2 */:
case Enum1.X:
case Enum1.Y:
case 100 /* Z */:
break;
}
}

View File

@ -0,0 +1,306 @@
=== tests/cases/compiler/constantsInEnumMembers.ts ===
enum Enum1 {
>Enum1 : Enum1
A0 = 100,
>A0 : Enum1
}
enum Enum1 {
>Enum1 : Enum1
// correct cases
A,
>A : Enum1
B,
>B : Enum1
C = 10,
>C : Enum1
D = A + B,
>D : Enum1
>A + B : number
>A : Enum1
>B : Enum1
E = A + 1,
>E : Enum1
>A + 1 : number
>A : Enum1
F = 1 + A,
>F : Enum1
>1 + A : number
>A : Enum1
G = 1 + 1,
>G : Enum1
>1 + 1 : number
H = A - B,
>H : Enum1
>A - B : number
>A : Enum1
>B : Enum1
I = A - 1,
>I : Enum1
>A - 1 : number
>A : Enum1
J = 1 - A,
>J : Enum1
>1 - A : number
>A : Enum1
K = 1 - 1,
>K : Enum1
>1 - 1 : number
L = ~D,
>L : Enum1
>~D : number
>D : Enum1
M = E << B,
>M : Enum1
>E << B : number
>E : Enum1
>B : Enum1
N = E << 1,
>N : Enum1
>E << 1 : number
>E : Enum1
O = E >> B,
>O : Enum1
>E >> B : number
>E : Enum1
>B : Enum1
P = E >> 1,
>P : Enum1
>E >> 1 : number
>E : Enum1
Q = -D,
>Q : Enum1
>-D : number
>D : Enum1
R = C & 5,
>R : Enum1
>C & 5 : number
>C : Enum1
S = 5 & C,
>S : Enum1
>5 & C : number
>C : Enum1
T = C | D,
>T : Enum1
>C | D : number
>C : Enum1
>D : Enum1
U = C | 1,
>U : Enum1
>C | 1 : number
>C : Enum1
V = 10 | D,
>V : Enum1
>10 | D : number
>D : Enum1
W = Enum1.V,
>W : Enum1
>Enum1.V : Enum1
>Enum1 : typeof Enum1
>V : Enum1
// correct cases: reference to the enum member from different enum declaration
W1 = A0,
>W1 : Enum1
>A0 : Enum1
W2 = Enum1.A0,
>W2 : Enum1
>Enum1.A0 : Enum1
>Enum1 : typeof Enum1
>A0 : Enum1
// illegal case
// forward reference to the element of the same enum
X = Y,
>X : Enum1
>Y : Enum1
// forward reference to the element of the same enum
Y = Enum1.Z,
>Y : Enum1
>Enum1.Z : Enum1
>Enum1 : typeof Enum1
>Z : Enum1
Z = 100,
>Z : Enum1
}
function foo(x: Enum1) {
>foo : (x: Enum1) => void
>x : Enum1
>Enum1 : Enum1
switch (x) {
>x : Enum1
case Enum1.A:
>Enum1.A : Enum1
>Enum1 : typeof Enum1
>A : Enum1
case Enum1.B:
>Enum1.B : Enum1
>Enum1 : typeof Enum1
>B : Enum1
case Enum1.C:
>Enum1.C : Enum1
>Enum1 : typeof Enum1
>C : Enum1
case Enum1.D:
>Enum1.D : Enum1
>Enum1 : typeof Enum1
>D : Enum1
case Enum1.E:
>Enum1.E : Enum1
>Enum1 : typeof Enum1
>E : Enum1
case Enum1.F:
>Enum1.F : Enum1
>Enum1 : typeof Enum1
>F : Enum1
case Enum1.G:
>Enum1.G : Enum1
>Enum1 : typeof Enum1
>G : Enum1
case Enum1.H:
>Enum1.H : Enum1
>Enum1 : typeof Enum1
>H : Enum1
case Enum1.I:
>Enum1.I : Enum1
>Enum1 : typeof Enum1
>I : Enum1
case Enum1.J:
>Enum1.J : Enum1
>Enum1 : typeof Enum1
>J : Enum1
case Enum1.K:
>Enum1.K : Enum1
>Enum1 : typeof Enum1
>K : Enum1
case Enum1.L:
>Enum1.L : Enum1
>Enum1 : typeof Enum1
>L : Enum1
case Enum1.M:
>Enum1.M : Enum1
>Enum1 : typeof Enum1
>M : Enum1
case Enum1.N:
>Enum1.N : Enum1
>Enum1 : typeof Enum1
>N : Enum1
case Enum1.O:
>Enum1.O : Enum1
>Enum1 : typeof Enum1
>O : Enum1
case Enum1.P:
>Enum1.P : Enum1
>Enum1 : typeof Enum1
>P : Enum1
case Enum1.Q:
>Enum1.Q : Enum1
>Enum1 : typeof Enum1
>Q : Enum1
case Enum1.R:
>Enum1.R : Enum1
>Enum1 : typeof Enum1
>R : Enum1
case Enum1.S:
>Enum1.S : Enum1
>Enum1 : typeof Enum1
>S : Enum1
case Enum1.T:
>Enum1.T : Enum1
>Enum1 : typeof Enum1
>T : Enum1
case Enum1.U:
>Enum1.U : Enum1
>Enum1 : typeof Enum1
>U : Enum1
case Enum1.V:
>Enum1.V : Enum1
>Enum1 : typeof Enum1
>V : Enum1
case Enum1.W:
>Enum1.W : Enum1
>Enum1 : typeof Enum1
>W : Enum1
case Enum1.W1:
>Enum1.W1 : Enum1
>Enum1 : typeof Enum1
>W1 : Enum1
case Enum1.W2:
>Enum1.W2 : Enum1
>Enum1 : typeof Enum1
>W2 : Enum1
case Enum1.X:
>Enum1.X : Enum1
>Enum1 : typeof Enum1
>X : Enum1
case Enum1.Y:
>Enum1.Y : Enum1
>Enum1 : typeof Enum1
>Y : Enum1
case Enum1.Z:
>Enum1.Z : Enum1
>Enum1 : typeof Enum1
>Z : Enum1
break;
}
}

View File

@ -0,0 +1,78 @@
// @propagateEnumConstants: true
enum Enum1 {
A0 = 100,
}
enum Enum1 {
// correct cases
A,
B,
C = 10,
D = A + B,
E = A + 1,
F = 1 + A,
G = 1 + 1,
H = A - B,
I = A - 1,
J = 1 - A,
K = 1 - 1,
L = ~D,
M = E << B,
N = E << 1,
O = E >> B,
P = E >> 1,
Q = -D,
R = C & 5,
S = 5 & C,
T = C | D,
U = C | 1,
V = 10 | D,
W = Enum1.V,
// correct cases: reference to the enum member from different enum declaration
W1 = A0,
W2 = Enum1.A0,
// illegal case
// forward reference to the element of the same enum
X = Y,
// forward reference to the element of the same enum
Y = Enum1.Z,
Z = 100,
}
function foo(x: Enum1) {
switch (x) {
case Enum1.A:
case Enum1.B:
case Enum1.C:
case Enum1.D:
case Enum1.E:
case Enum1.F:
case Enum1.G:
case Enum1.H:
case Enum1.I:
case Enum1.J:
case Enum1.K:
case Enum1.L:
case Enum1.M:
case Enum1.N:
case Enum1.O:
case Enum1.P:
case Enum1.Q:
case Enum1.R:
case Enum1.S:
case Enum1.T:
case Enum1.U:
case Enum1.V:
case Enum1.W:
case Enum1.W1:
case Enum1.W2:
case Enum1.X:
case Enum1.Y:
case Enum1.Z:
break;
}
}