addressed CR feedback, added support for indexed access

This commit is contained in:
Vladimir Matveev 2014-10-26 23:42:20 -07:00
parent ce336bcec7
commit 365587f6bf
4 changed files with 327 additions and 43 deletions

View File

@ -306,6 +306,22 @@ module ts {
// return undefined if we can't find a symbol.
}
/** Returns true if node1 is defined before node 2**/
function isDefinedBefore(node1: Node, node2: Node): boolean {
var file1 = getSourceFileOfNode(node1);
var file2 = getSourceFileOfNode(node2);
if (file1 === file2) {
return node1.pos <= node2.pos;
}
if (!compilerOptions.out) {
return true;
}
var sourceFiles = program.getSourceFiles();
return sourceFiles.indexOf(file1) <= sourceFiles.indexOf(file2);
}
function resolveName(location: Node, name: string, meaning: SymbolFlags, nameNotFoundMessage: DiagnosticMessage, nameArg: string): Symbol {
var errorLocation = location;
var result: Symbol;
@ -330,18 +346,8 @@ module ts {
// Block-scoped variables can not be used before their definition
var declaration = forEach(s.declarations, d => d.flags & NodeFlags.BlockScoped ? d : undefined);
Debug.assert(declaration, "Block-scoped variable declaration is undefined");
var declarationSourceFile = getSourceFileOfNode(declaration);
var referenceSourceFile = getSourceFileOfNode(errorLocation);
if (declarationSourceFile === referenceSourceFile) {
if (declaration.pos > errorLocation.pos) {
error(errorLocation, Diagnostics.Block_scoped_variable_0_used_before_its_declaration, identifierToString(declaration.name));
}
}
else if (compilerOptions.out) {
var sourceFiles = program.getSourceFiles();
if (sourceFiles.indexOf(referenceSourceFile) < sourceFiles.indexOf(declarationSourceFile)) {
error(errorLocation, Diagnostics.Block_scoped_variable_0_used_before_its_declaration, identifierToString(declaration.name));
}
if (!isDefinedBefore(declaration, errorLocation)) {
error(errorLocation, Diagnostics.Block_scoped_variable_0_used_before_its_declaration, identifierToString(declaration.name));
}
}
return s;
@ -489,12 +495,10 @@ module ts {
}
// Resolves a qualified name and any involved import aliases
function resolveEntityName(location: Node, name: EntityName, meaning: SymbolFlags, suppressErrors?: boolean): Symbol {
function resolveEntityName(location: Node, name: EntityName, meaning: SymbolFlags): Symbol {
if (name.kind === SyntaxKind.Identifier) {
// TODO: Investigate error recovery for symbols not found
var nameNotFoundMessage = !suppressErrors && Diagnostics.Cannot_find_name_0;
var nameArg = !suppressErrors && identifierToString(<Identifier>name);
var symbol = resolveName(location, (<Identifier>name).text, meaning, nameNotFoundMessage, nameArg);
var symbol = resolveName(location, (<Identifier>name).text, meaning, Diagnostics.Cannot_find_name_0, identifierToString(<Identifier>name));
if (!symbol) {
return;
}
@ -504,10 +508,8 @@ module ts {
if (!namespace || namespace === unknownSymbol || (<QualifiedName>name).right.kind === SyntaxKind.Missing) return;
var symbol = getSymbol(namespace.exports, (<QualifiedName>name).right.text, meaning);
if (!symbol) {
if (!suppressErrors) {
error(location, Diagnostics.Module_0_has_no_exported_member_1, getFullyQualifiedName(namespace),
identifierToString((<QualifiedName>name).right));
}
error(location, Diagnostics.Module_0_has_no_exported_member_1, getFullyQualifiedName(namespace),
identifierToString((<QualifiedName>name).right));
return;
}
}
@ -7410,13 +7412,13 @@ module ts {
var autoValue = 0;
var ambient = isInAmbientContext(node);
forEach(node.members, member => {
forEach(node.members, member => {
if(isNumericName(member.name.text)) {
error(member.name, Diagnostics.An_enum_member_cannot_have_a_numeric_name);
}
var initializer = member.initializer;
if (initializer) {
autoValue = getConstantValueForEnumMemberInitializer(member, initializer);
autoValue = getConstantValueForEnumMemberInitializer(initializer);
if (autoValue === undefined && !ambient) {
// Only here do we need to check that the initializer is assignable to the enum type.
// If it is a constant value (not undefined), it is syntactically constrained to be a number.
@ -7437,66 +7439,101 @@ module ts {
nodeLinks.flags |= NodeCheckFlags.EnumValuesComputed;
}
function getConstantValueForEnumMemberInitializer(member: EnumMember, initializer: Expression): number {
function getConstantValueForEnumMemberInitializer(initializer: Expression): number {
return evalConstant(initializer);
function evalConstant(e: Node): number {
switch (e.kind) {
case SyntaxKind.PrefixOperator:
var value = evalConstant((<UnaryExpression>e).operand);
if (value === undefined) return undefined;
if (value === undefined) {
return undefined;
}
switch ((<UnaryExpression>e).operator) {
case SyntaxKind.PlusToken: return value;
case SyntaxKind.MinusToken: return -value;
case SyntaxKind.TildeToken: return ~value;
case SyntaxKind.TildeToken: return compilerOptions.propagateEnumConstants ? ~value : undefined;
}
return undefined;
case SyntaxKind.BinaryExpression:
if (!program.getCompilerOptions().propagateEnumConstants) return undefined;
if (!compilerOptions.propagateEnumConstants) {
return undefined;
}
var left = evalConstant((<BinaryExpression>e).left);
if (left === undefined) return undefined;
if (left === undefined) {
return undefined;
}
var right = evalConstant((<BinaryExpression>e).right);
if (right === undefined) return undefined;
if (right === undefined) {
return undefined;
}
switch ((<BinaryExpression>e).operator) {
case SyntaxKind.BarToken: return left | right;
case SyntaxKind.AmpersandToken: return left & right;
case SyntaxKind.PlusToken: return left + right;
case SyntaxKind.MinusToken: return left - right;
case SyntaxKind.GreaterThanGreaterThanToken: return left >> right;
case SyntaxKind.GreaterThanGreaterThanGreaterThanToken: return left >>> right;
case SyntaxKind.LessThanLessThanToken: return left << right;
}
return undefined;
case SyntaxKind.NumericLiteral:
return +(<LiteralExpression>e).text;
case SyntaxKind.Identifier:
case SyntaxKind.IndexedAccess:
case SyntaxKind.PropertyAccess:
if (!program.getCompilerOptions().propagateEnumConstants) return undefined;
if (!compilerOptions.propagateEnumConstants) {
return undefined;
}
var enumSymbol: Symbol;
var member = initializer.parent;
var currentType = getTypeOfSymbol(getSymbolOfNode(member.parent));
var enumType: Type;
var propertyName: string;
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);
enumType = currentType;
propertyName = (<Identifier>e).text;
}
else if (e.kind === SyntaxKind.IndexedAccess) {
if ((<IndexedAccess>e).index.kind !== SyntaxKind.StringLiteral) {
return undefined;
}
var enumType = getTypeOfNode((<IndexedAccess>e).object);
if (enumType !== currentType) {
return undefined;
}
propertyName = (<LiteralExpression>(<IndexedAccess>e).index).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 enumType = getTypeOfNode((<PropertyAccess>e).left);
if (enumType !== currentType) {
return undefined;
}
propertyName = (<PropertyAccess>e).right.text;
}
var propertySymbol = enumSymbol.exports[propertyName];
if (!propertyName || !(propertySymbol.flags & SymbolFlags.EnumMember)) return undefined;
var propertyDecl = <EnumMember>propertySymbol.valueDeclaration;
if (propertyName === undefined) {
return undefined;
}
var property = getPropertyOfObjectType(enumType, propertyName);
if (!(property.flags & SymbolFlags.EnumMember)) {
return undefined;
}
var propertyDecl = <EnumMember>property.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)
if (member === propertyDecl) {
return undefined;
}
// illegal case: forward reference
if (!isDefinedBefore(propertyDecl, member)) {
return undefined;
}
return <number>getNodeLinks(propertyDecl).enumMemberValue;
}
}

View File

@ -33,16 +33,39 @@ enum Enum1 {
// correct cases: reference to the enum member from different enum declaration
W1 = A0,
W2 = Enum1.A0,
W3 = Enum1["A0"],
W4 = Enum1["W"],
// 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,
Y1 = Enum1["Z"],
Z = 100,
}
module A {
export module B {
export module C {
export enum E {
V1 = 1,
V2 = A.B.C.E.V1 + 100
}
}
}
}
module A {
export module B {
export module C {
export enum E {
V3 = A.B.C.E["V2"] + 200,
}
}
}
}
function foo(x: Enum1) {
switch (x) {
case Enum1.A:
@ -70,11 +93,22 @@ function foo(x: Enum1) {
case Enum1.W:
case Enum1.W1:
case Enum1.W2:
case Enum1.W3:
case Enum1.W4:
case Enum1.X:
case Enum1.Y:
case Enum1.Y1:
case Enum1.Z:
break;
}
}
function bar(e: A.B.C.E): number {
switch (e) {
case A.B.C.E.V1: return 1;
case A.B.C.E.V2: return 1;
case A.B.C.E.V3: return 1;
}
}
//// [constantsInEnumMembers.js]
@ -111,13 +145,43 @@ var Enum1;
// correct cases: reference to the enum member from different enum declaration
Enum1[Enum1["W1"] = A0] = "W1";
Enum1[Enum1["W2"] = Enum1.A0] = "W2";
Enum1[Enum1["W3"] = Enum1["A0"]] = "W3";
Enum1[Enum1["W4"] = Enum1["W"]] = "W4";
// 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["Y1"] = Enum1["Z"]] = "Y1";
Enum1[Enum1["Z"] = 100] = "Z";
})(Enum1 || (Enum1 = {}));
var A;
(function (A) {
var B;
(function (B) {
var C;
(function (C) {
(function (E) {
E[E["V1"] = 1] = "V1";
E[E["V2"] = A.B.C.E.V1 + 100] = "V2";
})(C.E || (C.E = {}));
var E = C.E;
})(C = B.C || (B.C = {}));
})(B = A.B || (A.B = {}));
})(A || (A = {}));
var A;
(function (A) {
var B;
(function (B) {
var C;
(function (C) {
(function (E) {
E[E["V3"] = A.B.C.E["V2"] + 200] = "V3";
})(C.E || (C.E = {}));
var E = C.E;
})(C = B.C || (B.C = {}));
})(B = A.B || (A.B = {}));
})(A || (A = {}));
function foo(x) {
switch (x) {
case 0 /* A */:
@ -145,9 +209,22 @@ function foo(x) {
case 11 /* W */:
case 100 /* W1 */:
case 100 /* W2 */:
case 100 /* W3 */:
case 11 /* W4 */:
case Enum1.X:
case Enum1.Y:
case Enum1.Y1:
case 100 /* Z */:
break;
}
}
function bar(e) {
switch (e) {
case 1 /* V1 */:
return 1;
case 101 /* V2 */:
return 1;
case 301 /* V3 */:
return 1;
}
}

View File

@ -135,6 +135,16 @@ enum Enum1 {
>Enum1 : typeof Enum1
>A0 : Enum1
W3 = Enum1["A0"],
>W3 : Enum1
>Enum1["A0"] : Enum1
>Enum1 : typeof Enum1
W4 = Enum1["W"],
>W4 : Enum1
>Enum1["W"] : Enum1
>Enum1 : typeof Enum1
// illegal case
// forward reference to the element of the same enum
X = Y,
@ -148,11 +158,76 @@ enum Enum1 {
>Enum1 : typeof Enum1
>Z : Enum1
Y1 = Enum1["Z"],
>Y1 : Enum1
>Enum1["Z"] : Enum1
>Enum1 : typeof Enum1
Z = 100,
>Z : Enum1
}
module A {
>A : typeof A
export module B {
>B : typeof B
export module C {
>C : typeof C
export enum E {
>E : E
V1 = 1,
>V1 : E
V2 = A.B.C.E.V1 + 100
>V2 : E
>A.B.C.E.V1 + 100 : number
>A.B.C.E.V1 : E
>A.B.C.E : typeof E
>A.B.C : typeof C
>A.B : typeof B
>A : typeof A
>B : typeof B
>C : typeof C
>E : typeof E
>V1 : E
}
}
}
}
module A {
>A : typeof A
export module B {
>B : typeof B
export module C {
>C : typeof C
export enum E {
>E : E
V3 = A.B.C.E["V2"] + 200,
>V3 : E
>A.B.C.E["V2"] + 200 : number
>A.B.C.E["V2"] : E
>A.B.C.E : typeof E
>A.B.C : typeof C
>A.B : typeof B
>A : typeof A
>B : typeof B
>C : typeof C
>E : typeof E
}
}
}
}
function foo(x: Enum1) {
>foo : (x: Enum1) => void
>x : Enum1
@ -286,6 +361,16 @@ function foo(x: Enum1) {
>Enum1 : typeof Enum1
>W2 : Enum1
case Enum1.W3:
>Enum1.W3 : Enum1
>Enum1 : typeof Enum1
>W3 : Enum1
case Enum1.W4:
>Enum1.W4 : Enum1
>Enum1 : typeof Enum1
>W4 : Enum1
case Enum1.X:
>Enum1.X : Enum1
>Enum1 : typeof Enum1
@ -296,6 +381,11 @@ function foo(x: Enum1) {
>Enum1 : typeof Enum1
>Y : Enum1
case Enum1.Y1:
>Enum1.Y1 : Enum1
>Enum1 : typeof Enum1
>Y1 : Enum1
case Enum1.Z:
>Enum1.Z : Enum1
>Enum1 : typeof Enum1
@ -304,3 +394,49 @@ function foo(x: Enum1) {
break;
}
}
function bar(e: A.B.C.E): number {
>bar : (e: A.B.C.E) => number
>e : A.B.C.E
>A : unknown
>B : unknown
>C : unknown
>E : A.B.C.E
switch (e) {
>e : A.B.C.E
case A.B.C.E.V1: return 1;
>A.B.C.E.V1 : A.B.C.E
>A.B.C.E : typeof A.B.C.E
>A.B.C : typeof A.B.C
>A.B : typeof A.B
>A : typeof A
>B : typeof A.B
>C : typeof A.B.C
>E : typeof A.B.C.E
>V1 : A.B.C.E
case A.B.C.E.V2: return 1;
>A.B.C.E.V2 : A.B.C.E
>A.B.C.E : typeof A.B.C.E
>A.B.C : typeof A.B.C
>A.B : typeof A.B
>A : typeof A
>B : typeof A.B
>C : typeof A.B.C
>E : typeof A.B.C.E
>V2 : A.B.C.E
case A.B.C.E.V3: return 1;
>A.B.C.E.V3 : A.B.C.E
>A.B.C.E : typeof A.B.C.E
>A.B.C : typeof A.B.C
>A.B : typeof A.B
>A : typeof A
>B : typeof A.B
>C : typeof A.B.C
>E : typeof A.B.C.E
>V3 : A.B.C.E
}
}

View File

@ -33,16 +33,39 @@ enum Enum1 {
// correct cases: reference to the enum member from different enum declaration
W1 = A0,
W2 = Enum1.A0,
W3 = Enum1["A0"],
W4 = Enum1["W"],
// 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,
Y1 = Enum1["Z"],
Z = 100,
}
module A {
export module B {
export module C {
export enum E {
V1 = 1,
V2 = A.B.C.E.V1 + 100
}
}
}
}
module A {
export module B {
export module C {
export enum E {
V3 = A.B.C.E["V2"] + 200,
}
}
}
}
function foo(x: Enum1) {
switch (x) {
case Enum1.A:
@ -70,9 +93,20 @@ function foo(x: Enum1) {
case Enum1.W:
case Enum1.W1:
case Enum1.W2:
case Enum1.W3:
case Enum1.W4:
case Enum1.X:
case Enum1.Y:
case Enum1.Y1:
case Enum1.Z:
break;
}
}
function bar(e: A.B.C.E): number {
switch (e) {
case A.B.C.E.V1: return 1;
case A.B.C.E.V2: return 1;
case A.B.C.E.V3: return 1;
}
}