diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 435787405a1..91edaf7c920 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -7473,18 +7473,31 @@ module ts { case SyntaxKind.PropertyAccess: if (!program.getCompilerOptions().propagateEnumConstants) return undefined; - var refSymbol = - e.kind === SyntaxKind.Identifier - ? resolveName(member, (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 = 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 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 = (e).text; + } + else { + // left part in PropertyAccess should be resolved to the symbol of enum that declared 'member' + enumSymbol = resolveEntityName(member, (e).left, SymbolFlags.Enum, /*suppressErrors*/ true); + + if (enumSymbol !== getSymbolOfNode(member.parent)) return undefined; + propertyName = ((e).right).text; + } + + var propertySymbol = enumSymbol.exports[propertyName]; + if (!propertyName || !(propertySymbol.flags & SymbolFlags.EnumMember)) return undefined; + var propertyDecl = 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 getNodeLinks(propertyDecl).enumMemberValue; } } } diff --git a/src/harness/harness.ts b/src/harness/harness.ts index 60c5da5a1a6..f537541b5b3 100644 --- a/src/harness/harness.ts +++ b/src/harness/harness.ts @@ -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[] { diff --git a/tests/baselines/reference/constantsInEnumMembers.js b/tests/baselines/reference/constantsInEnumMembers.js new file mode 100644 index 00000000000..a6fc5b2ca69 --- /dev/null +++ b/tests/baselines/reference/constantsInEnumMembers.js @@ -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; + } +} diff --git a/tests/baselines/reference/constantsInEnumMembers.types b/tests/baselines/reference/constantsInEnumMembers.types new file mode 100644 index 00000000000..c44de37649b --- /dev/null +++ b/tests/baselines/reference/constantsInEnumMembers.types @@ -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; + } +} diff --git a/tests/cases/compiler/constantsInEnumMembers.ts b/tests/cases/compiler/constantsInEnumMembers.ts new file mode 100644 index 00000000000..1a84cd2fb89 --- /dev/null +++ b/tests/cases/compiler/constantsInEnumMembers.ts @@ -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; + } +} \ No newline at end of file