@@ -20669,107 +20669,86 @@ namespace ts {
20669
20669
20670
20670
function computeEnumMemberValues(node: EnumDeclaration) {
20671
20671
const nodeLinks = getNodeLinks(node);
20672
-
20673
20672
if (!(nodeLinks.flags & NodeCheckFlags.EnumValuesComputed)) {
20674
- const enumSymbol = getSymbolOfNode(node);
20675
- const enumType = getDeclaredTypeOfSymbol(enumSymbol);
20676
- let autoValue = 0; // set to undefined when enum member is non-constant
20677
- const ambient = isInAmbientContext(node);
20678
- const enumIsConst = isConst(node);
20679
-
20673
+ nodeLinks.flags |= NodeCheckFlags.EnumValuesComputed;
20674
+ let autoValue = 0;
20680
20675
for (const member of node.members) {
20681
- if (isComputedNonLiteralName(<PropertyName>member.name)) {
20682
- error(member.name, Diagnostics.Computed_property_names_are_not_allowed_in_enums);
20683
- }
20684
- else {
20685
- const text = getTextOfPropertyName(<PropertyName>member.name);
20686
- if (isNumericLiteralName(text) && !isInfinityOrNaNString(text)) {
20687
- error(member.name, Diagnostics.An_enum_member_cannot_have_a_numeric_name);
20688
- }
20689
- }
20690
-
20691
- const previousEnumMemberIsNonConstant = autoValue === undefined;
20692
-
20693
- const initializer = member.initializer;
20694
- if (initializer) {
20695
- autoValue = computeConstantValueForEnumMemberInitializer(initializer, enumType, enumIsConst, ambient);
20696
- }
20697
- else if (ambient && !enumIsConst) {
20698
- // In ambient enum declarations that specify no const modifier, enum member declarations
20699
- // that omit a value are considered computed members (as opposed to having auto-incremented values assigned).
20700
- autoValue = undefined;
20701
- }
20702
- else if (previousEnumMemberIsNonConstant) {
20703
- // If the member declaration specifies no value, the member is considered a constant enum member.
20704
- // If the member is the first member in the enum declaration, it is assigned the value zero.
20705
- // Otherwise, it is assigned the value of the immediately preceding member plus one,
20706
- // and an error occurs if the immediately preceding member is not a constant enum member
20707
- error(member.name, Diagnostics.Enum_member_must_have_initializer);
20708
- }
20709
-
20710
- if (autoValue !== undefined) {
20711
- getNodeLinks(member).enumMemberValue = autoValue;
20712
- autoValue++;
20713
- }
20676
+ const value = computeMemberValue(member, autoValue);
20677
+ getNodeLinks(member).enumMemberValue = value;
20678
+ autoValue = typeof value === "number" ? value + 1 : undefined;
20714
20679
}
20715
-
20716
- nodeLinks.flags |= NodeCheckFlags.EnumValuesComputed;
20717
20680
}
20681
+ }
20718
20682
20719
- function computeConstantValueForEnumMemberInitializer(initializer: Expression, enumType: Type, enumIsConst: boolean, ambient: boolean): number {
20720
- // Controls if error should be reported after evaluation of constant value is completed
20721
- // Can be false if another more precise error was already reported during evaluation.
20722
- let reportError = true;
20723
- const value = evalConstant(initializer);
20724
-
20725
- if (reportError) {
20726
- if (value === undefined) {
20727
- if (enumIsConst) {
20728
- error(initializer, Diagnostics.In_const_enum_declarations_member_initializer_must_be_constant_expression);
20729
- }
20730
- else if (ambient) {
20731
- error(initializer, Diagnostics.In_ambient_enum_declarations_member_initializer_must_be_constant_expression);
20732
- }
20733
- else {
20734
- // Only here do we need to check that the initializer is assignable to the enum type.
20735
- checkTypeAssignableTo(checkExpression(initializer), enumType, initializer, /*headMessage*/ undefined);
20736
- }
20737
- }
20738
- else if (enumIsConst) {
20739
- if (isNaN(value)) {
20740
- error(initializer, Diagnostics.const_enum_member_initializer_was_evaluated_to_disallowed_value_NaN);
20741
- }
20742
- else if (!isFinite(value)) {
20743
- error(initializer, Diagnostics.const_enum_member_initializer_was_evaluated_to_a_non_finite_value);
20744
- }
20745
- }
20683
+ function computeMemberValue(member: EnumMember, autoValue: number) {
20684
+ if (isComputedNonLiteralName(<PropertyName>member.name)) {
20685
+ error(member.name, Diagnostics.Computed_property_names_are_not_allowed_in_enums);
20686
+ }
20687
+ else {
20688
+ const text = getTextOfPropertyName(<PropertyName>member.name);
20689
+ if (isNumericLiteralName(text) && !isInfinityOrNaNString(text)) {
20690
+ error(member.name, Diagnostics.An_enum_member_cannot_have_a_numeric_name);
20746
20691
}
20692
+ }
20693
+ if (member.initializer) {
20694
+ return computeConstantValue(member);
20695
+ }
20696
+ // In ambient enum declarations that specify no const modifier, enum member declarations that omit
20697
+ // a value are considered computed members (as opposed to having auto-incremented values).
20698
+ if (isInAmbientContext(member.parent) && !isConst(member.parent)) {
20699
+ return undefined;
20700
+ }
20701
+ // If the member declaration specifies no value, the member is considered a constant enum member.
20702
+ // If the member is the first member in the enum declaration, it is assigned the value zero.
20703
+ // Otherwise, it is assigned the value of the immediately preceding member plus one, and an error
20704
+ // occurs if the immediately preceding member is not a constant enum member.
20705
+ if (autoValue !== undefined) {
20706
+ return autoValue;
20707
+ }
20708
+ error(member.name, Diagnostics.Enum_member_must_have_initializer);
20709
+ return undefined;
20710
+ }
20747
20711
20748
- return value;
20712
+ function computeConstantValue(member: EnumMember): number {
20713
+ const isConstEnum = isConst(member.parent);
20714
+ const initializer = member.initializer;
20715
+ const value = evaluate(member.initializer);
20716
+ if (value !== undefined) {
20717
+ if (isConstEnum && !isFinite(value)) {
20718
+ error(initializer, isNaN(value) ?
20719
+ Diagnostics.const_enum_member_initializer_was_evaluated_to_disallowed_value_NaN :
20720
+ Diagnostics.const_enum_member_initializer_was_evaluated_to_a_non_finite_value);
20721
+ }
20722
+ }
20723
+ else if (isConstEnum) {
20724
+ error(initializer, Diagnostics.In_const_enum_declarations_member_initializer_must_be_constant_expression);
20725
+ }
20726
+ else if (isInAmbientContext(member.parent)) {
20727
+ error(initializer, Diagnostics.In_ambient_enum_declarations_member_initializer_must_be_constant_expression);
20728
+ }
20729
+ else {
20730
+ // Only here do we need to check that the initializer is assignable to the enum type.
20731
+ checkTypeAssignableTo(checkExpression(initializer), getDeclaredTypeOfSymbol(getSymbolOfNode(member.parent)), initializer, /*headMessage*/ undefined);
20732
+ }
20733
+ return value;
20749
20734
20750
- function evalConstant(e: Node): number {
20751
- switch (e.kind) {
20752
- case SyntaxKind.PrefixUnaryExpression:
20753
- const value = evalConstant((<PrefixUnaryExpression>e).operand);
20754
- if (value === undefined) {
20755
- return undefined;
20756
- }
20757
- switch ((<PrefixUnaryExpression>e).operator) {
20735
+ function evaluate(expr: Expression): number {
20736
+ switch (expr.kind) {
20737
+ case SyntaxKind.PrefixUnaryExpression:
20738
+ const value = evaluate((<PrefixUnaryExpression>expr).operand);
20739
+ if (typeof value === "number") {
20740
+ switch ((<PrefixUnaryExpression>expr).operator) {
20758
20741
case SyntaxKind.PlusToken: return value;
20759
20742
case SyntaxKind.MinusToken: return -value;
20760
20743
case SyntaxKind.TildeToken: return ~value;
20761
20744
}
20762
- return undefined;
20763
- case SyntaxKind.BinaryExpression:
20764
- const left = evalConstant((<BinaryExpression>e).left);
20765
- if (left === undefined) {
20766
- return undefined;
20767
- }
20768
- const right = evalConstant((<BinaryExpression>e).right);
20769
- if (right === undefined) {
20770
- return undefined;
20771
- }
20772
- switch ((<BinaryExpression>e).operatorToken.kind) {
20745
+ }
20746
+ break;
20747
+ case SyntaxKind.BinaryExpression:
20748
+ const left = evaluate((<BinaryExpression>expr).left);
20749
+ const right = evaluate((<BinaryExpression>expr).right);
20750
+ if (typeof left === "number" && typeof right === "number") {
20751
+ switch ((<BinaryExpression>expr).operatorToken.kind) {
20773
20752
case SyntaxKind.BarToken: return left | right;
20774
20753
case SyntaxKind.AmpersandToken: return left & right;
20775
20754
case SyntaxKind.GreaterThanGreaterThanToken: return left >> right;
@@ -20782,90 +20761,54 @@ namespace ts {
20782
20761
case SyntaxKind.MinusToken: return left - right;
20783
20762
case SyntaxKind.PercentToken: return left % right;
20784
20763
}
20785
- return undefined;
20786
- case SyntaxKind.NumericLiteral:
20787
- checkGrammarNumericLiteral(<NumericLiteral>e);
20788
- return +(<NumericLiteral>e).text;
20789
- case SyntaxKind.ParenthesizedExpression:
20790
- return evalConstant((<ParenthesizedExpression>e).expression);
20791
- case SyntaxKind.Identifier:
20792
- case SyntaxKind.ElementAccessExpression:
20793
- case SyntaxKind.PropertyAccessExpression:
20794
- const member = initializer.parent;
20795
- const currentType = getTypeOfSymbol(getSymbolOfNode(member.parent));
20796
- let enumType: Type;
20797
- let propertyName: string;
20798
-
20799
- if (e.kind === SyntaxKind.Identifier) {
20800
- // unqualified names can refer to member that reside in different declaration of the enum so just doing name resolution won't work.
20801
- // instead pick current enum type and later try to fetch member from the type
20802
- enumType = currentType;
20803
- propertyName = (<Identifier>e).text;
20804
- }
20805
- else {
20806
- let expression: Expression;
20807
- if (e.kind === SyntaxKind.ElementAccessExpression) {
20808
- if ((<ElementAccessExpression>e).argumentExpression === undefined ||
20809
- (<ElementAccessExpression>e).argumentExpression.kind !== SyntaxKind.StringLiteral) {
20810
- return undefined;
20811
- }
20812
- expression = (<ElementAccessExpression>e).expression;
20813
- propertyName = (<LiteralExpression>(<ElementAccessExpression>e).argumentExpression).text;
20814
- }
20815
- else {
20816
- expression = (<PropertyAccessExpression>e).expression;
20817
- propertyName = (<PropertyAccessExpression>e).name.text;
20818
- }
20819
-
20820
- // expression part in ElementAccess\PropertyAccess should be either identifier or dottedName
20821
- let current = expression;
20822
- while (current) {
20823
- if (current.kind === SyntaxKind.Identifier) {
20824
- break;
20825
- }
20826
- else if (current.kind === SyntaxKind.PropertyAccessExpression) {
20827
- current = (<ElementAccessExpression>current).expression;
20828
- }
20829
- else {
20830
- return undefined;
20831
- }
20832
- }
20833
-
20834
- enumType = getTypeOfExpression(expression);
20835
- // allow references to constant members of other enums
20836
- if (!(enumType.symbol && (enumType.symbol.flags & SymbolFlags.Enum))) {
20837
- return undefined;
20838
- }
20839
- }
20840
-
20841
- if (propertyName === undefined) {
20842
- return undefined;
20843
- }
20844
-
20845
- const property = getPropertyOfObjectType(enumType, propertyName);
20846
- if (!property || !(property.flags & SymbolFlags.EnumMember)) {
20847
- return undefined;
20848
- }
20849
-
20850
- const propertyDecl = property.valueDeclaration;
20851
- // self references are illegal
20852
- if (member === propertyDecl) {
20853
- return undefined;
20854
- }
20855
-
20856
- // illegal case: forward reference
20857
- if (!isBlockScopedNameDeclaredBeforeUse(propertyDecl, member)) {
20858
- reportError = false;
20859
- error(e, Diagnostics.A_member_initializer_in_a_enum_declaration_cannot_reference_members_declared_after_it_including_members_defined_in_other_enums);
20860
- return undefined;
20764
+ }
20765
+ break;
20766
+ case SyntaxKind.NumericLiteral:
20767
+ checkGrammarNumericLiteral(<NumericLiteral>expr);
20768
+ return +(<NumericLiteral>expr).text;
20769
+ case SyntaxKind.ParenthesizedExpression:
20770
+ return evaluate((<ParenthesizedExpression>expr).expression);
20771
+ case SyntaxKind.Identifier:
20772
+ return evaluateEnumMember(expr, getSymbolOfNode(member.parent), (<Identifier>expr).text);
20773
+ case SyntaxKind.ElementAccessExpression:
20774
+ case SyntaxKind.PropertyAccessExpression:
20775
+ if (isConstantMemberAccess(expr)) {
20776
+ const type = getTypeOfExpression((<PropertyAccessExpression | ElementAccessExpression>expr).expression);
20777
+ if (type.symbol && type.symbol.flags & SymbolFlags.Enum) {
20778
+ const name = expr.kind === SyntaxKind.PropertyAccessExpression ?
20779
+ (<PropertyAccessExpression>expr).name.text :
20780
+ (<LiteralExpression>(<ElementAccessExpression>expr).argumentExpression).text;
20781
+ return evaluateEnumMember(expr, type.symbol, name);
20861
20782
}
20783
+ }
20784
+ break;
20785
+ }
20786
+ return undefined;
20787
+ }
20862
20788
20863
- return <number>getNodeLinks(propertyDecl).enumMemberValue;
20789
+ function evaluateEnumMember(expr: Expression, enumSymbol: Symbol, name: string) {
20790
+ const memberSymbol = enumSymbol.exports.get(name);
20791
+ if (memberSymbol) {
20792
+ const declaration = memberSymbol.valueDeclaration;
20793
+ if (declaration !== member) {
20794
+ if (isBlockScopedNameDeclaredBeforeUse(declaration, member)) {
20795
+ return getNodeLinks(declaration).enumMemberValue;
20796
+ }
20797
+ error(expr, Diagnostics.A_member_initializer_in_a_enum_declaration_cannot_reference_members_declared_after_it_including_members_defined_in_other_enums);
20798
+ return 0;
20864
20799
}
20865
20800
}
20801
+ return undefined;
20866
20802
}
20867
20803
}
20868
20804
20805
+ function isConstantMemberAccess(node: Expression): boolean {
20806
+ return node.kind === SyntaxKind.Identifier ||
20807
+ node.kind === SyntaxKind.PropertyAccessExpression && isConstantMemberAccess((<PropertyAccessExpression>node).expression) ||
20808
+ node.kind === SyntaxKind.ElementAccessExpression && isConstantMemberAccess((<ElementAccessExpression>node).expression) &&
20809
+ (<ElementAccessExpression>node).argumentExpression.kind === SyntaxKind.StringLiteral;
20810
+ }
20811
+
20869
20812
function checkEnumDeclaration(node: EnumDeclaration) {
20870
20813
if (!produceDiagnostics) {
20871
20814
return;
0 commit comments