mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-11 10:00:13 -06:00
Provide capability to ask the checker what the constant value of an enum member is.
This commit is contained in:
parent
89eff5608d
commit
fae7a3560e
@ -90,7 +90,8 @@ module ts {
|
||||
getAugmentedPropertiesOfApparentType: getAugmentedPropertiesOfApparentType,
|
||||
getRootSymbol: getRootSymbol,
|
||||
getContextualType: getContextualType,
|
||||
getFullyQualifiedName: getFullyQualifiedName
|
||||
getFullyQualifiedName: getFullyQualifiedName,
|
||||
getEnumMemberValue: getEnumMemberValue
|
||||
};
|
||||
|
||||
var undefinedSymbol = createSymbol(SymbolFlags.Property | SymbolFlags.Transient, "undefined");
|
||||
@ -6495,7 +6496,7 @@ module ts {
|
||||
}
|
||||
}
|
||||
|
||||
function getConstantValue(node: Expression): number {
|
||||
function getConstantValueForExpression(node: Expression): number {
|
||||
var isNegative = false;
|
||||
if (node.kind === SyntaxKind.PrefixOperator) {
|
||||
var unaryExpression = <UnaryExpression>node;
|
||||
@ -6512,38 +6513,55 @@ module ts {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function computeEnumMemberValues(node: EnumDeclaration, reportErrors: boolean) {
|
||||
var nodeLinks = getNodeLinks(node);
|
||||
|
||||
if (!(nodeLinks.flags & NodeCheckFlags.EnumValuesComputedAndChecked)) {
|
||||
var enumSymbol = getSymbolOfNode(node);
|
||||
var enumType = getDeclaredTypeOfSymbol(enumSymbol);
|
||||
var autoValue = 0;
|
||||
var ambient = isInAmbientContext(node);
|
||||
|
||||
forEach(node.members, member => {
|
||||
var initializer = member.initializer;
|
||||
if (initializer) {
|
||||
autoValue = getConstantValueForExpression(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.
|
||||
// Also, we do not need to check this for ambients because there is already
|
||||
// a syntax error if it is not a constant.
|
||||
if (reportErrors) {
|
||||
checkTypeAssignableTo(checkExpression(initializer), enumType, initializer, /*chainedMessage*/ undefined, /*terminalMessage*/ undefined);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (ambient) {
|
||||
autoValue = undefined;
|
||||
}
|
||||
|
||||
if (autoValue !== undefined) {
|
||||
getNodeLinks(member).enumMemberValue = autoValue++;
|
||||
}
|
||||
});
|
||||
|
||||
if (reportErrors) {
|
||||
nodeLinks.flags |= NodeCheckFlags.EnumValuesComputedAndChecked;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function checkEnumDeclaration(node: EnumDeclaration) {
|
||||
if (!fullTypeCheck) {
|
||||
return;
|
||||
}
|
||||
|
||||
checkTypeNameIsReserved(node.name, Diagnostics.Enum_name_cannot_be_0);
|
||||
checkCollisionWithCapturedThisVariable(node, node.name);
|
||||
checkCollistionWithRequireExportsInGeneratedCode(node, node.name);
|
||||
checkExportsOnMergedDeclarations(node);
|
||||
var enumSymbol = getSymbolOfNode(node);
|
||||
var enumType = getDeclaredTypeOfSymbol(enumSymbol);
|
||||
var autoValue = 0;
|
||||
var ambient = isInAmbientContext(node);
|
||||
forEach(node.members, member => {
|
||||
var initializer = member.initializer;
|
||||
if (initializer) {
|
||||
autoValue = getConstantValue(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.
|
||||
// Also, we do not need to check this for ambients because there is already
|
||||
// a syntax error if it is not a constant.
|
||||
checkTypeAssignableTo(checkExpression(initializer), enumType, initializer, /*chainedMessage*/ undefined, /*terminalMessage*/ undefined);
|
||||
}
|
||||
}
|
||||
else if (ambient) {
|
||||
autoValue = undefined;
|
||||
}
|
||||
|
||||
if (autoValue !== undefined) {
|
||||
getNodeLinks(member).enumMemberValue = autoValue++;
|
||||
}
|
||||
});
|
||||
computeEnumMemberValues(node, /*reportErrors:*/ true);
|
||||
|
||||
// Spec 2014 - Section 9.3:
|
||||
// It isn't possible for one enum declaration to continue the automatic numbering sequence of another,
|
||||
@ -6551,6 +6569,7 @@ module ts {
|
||||
// for the first member.
|
||||
//
|
||||
// Only perform this check once per symbol
|
||||
var enumSymbol = getSymbolOfNode(node);
|
||||
var firstDeclaration = getDeclarationOfKind(enumSymbol, node.kind);
|
||||
if (node === firstDeclaration) {
|
||||
var seenEnumMissingInitialInitializer = false;
|
||||
@ -7450,17 +7469,6 @@ module ts {
|
||||
}
|
||||
}
|
||||
|
||||
function getPropertyAccessSubstitution(node: PropertyAccess): string {
|
||||
var symbol = getNodeLinks(node).resolvedSymbol;
|
||||
if (symbol && (symbol.flags & SymbolFlags.EnumMember)) {
|
||||
var declaration = symbol.valueDeclaration;
|
||||
var constantValue: number;
|
||||
if (declaration.kind === SyntaxKind.EnumMember && (constantValue = getNodeLinks(declaration).enumMemberValue) !== undefined) {
|
||||
return constantValue.toString() + " /* " + identifierToString(declaration.name) + " */";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getExportAssignmentName(node: SourceFile): string {
|
||||
var symbol = getExportAssignmentSymbol(getSymbolOfNode(node));
|
||||
return symbol && symbolIsValue(symbol) ? symbolToString(symbol): undefined;
|
||||
@ -7523,9 +7531,23 @@ module ts {
|
||||
}
|
||||
|
||||
function getEnumMemberValue(node: EnumMember): number {
|
||||
computeEnumMemberValues(<EnumDeclaration>node.parent, /*reportErrors:*/ false);
|
||||
return getNodeLinks(node).enumMemberValue;
|
||||
}
|
||||
|
||||
function getConstantValue(node: PropertyAccess): number {
|
||||
var symbol = getNodeLinks(node).resolvedSymbol;
|
||||
if (symbol && (symbol.flags & SymbolFlags.EnumMember)) {
|
||||
var declaration = symbol.valueDeclaration;
|
||||
var constantValue: number;
|
||||
if (declaration.kind === SyntaxKind.EnumMember && (constantValue = getNodeLinks(declaration).enumMemberValue) !== undefined) {
|
||||
return constantValue;
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// Create a single instance that we can wrap the underlying emitter TextWriter with. That
|
||||
// way we don't have to allocate a new wrapper every time writeTypeAtLocation and
|
||||
// writeReturnTypeOfSignatureDeclaration are called.
|
||||
@ -7561,7 +7583,6 @@ module ts {
|
||||
getProgram: () => program,
|
||||
getLocalNameOfContainer: getLocalNameOfContainer,
|
||||
getExpressionNamePrefix: getExpressionNamePrefix,
|
||||
getPropertyAccessSubstitution: getPropertyAccessSubstitution,
|
||||
getExportAssignmentName: getExportAssignmentName,
|
||||
isReferencedImportDeclaration: isReferencedImportDeclaration,
|
||||
getNodeCheckFlags: getNodeCheckFlags,
|
||||
@ -7573,7 +7594,8 @@ module ts {
|
||||
writeTypeAtLocation: writeTypeAtLocation,
|
||||
writeReturnTypeOfSignatureDeclaration: writeReturnTypeOfSignatureDeclaration,
|
||||
isSymbolAccessible: isSymbolAccessible,
|
||||
isImportDeclarationEntityNameReferenceDeclarationVisibile: isImportDeclarationEntityNameReferenceDeclarationVisibile
|
||||
isImportDeclarationEntityNameReferenceDeclarationVisibile: isImportDeclarationEntityNameReferenceDeclarationVisibile,
|
||||
getConstantValue: getConstantValue,
|
||||
};
|
||||
checkProgram();
|
||||
return emitFiles(resolver, targetSourceFile);
|
||||
|
||||
@ -868,14 +868,15 @@ module ts {
|
||||
}
|
||||
|
||||
function emitPropertyAccess(node: PropertyAccess) {
|
||||
var text = resolver.getPropertyAccessSubstitution(node);
|
||||
if (text) {
|
||||
write(text);
|
||||
return;
|
||||
var constantValue = resolver.getConstantValue(node);
|
||||
if (constantValue !== undefined) {
|
||||
write(constantValue.toString() + " /* " + identifierToString(node.right) + " */");
|
||||
}
|
||||
else {
|
||||
emit(node.left);
|
||||
write(".");
|
||||
emit(node.right);
|
||||
}
|
||||
emit(node.left);
|
||||
write(".");
|
||||
emit(node.right);
|
||||
}
|
||||
|
||||
function emitIndexedAccess(node: IndexedAccess) {
|
||||
|
||||
@ -645,6 +645,10 @@ module ts {
|
||||
getAugmentedPropertiesOfApparentType(type: Type): Symbol[];
|
||||
getRootSymbol(symbol: Symbol): Symbol;
|
||||
getContextualType(node: Node): Type;
|
||||
|
||||
// Returns the constant value of this enum member, or 'undefined' if the enum member has a
|
||||
// computed value.
|
||||
getEnumMemberValue(node: EnumMember): number;
|
||||
}
|
||||
|
||||
export interface TextWriter {
|
||||
@ -680,7 +684,6 @@ module ts {
|
||||
getProgram(): Program;
|
||||
getLocalNameOfContainer(container: Declaration): string;
|
||||
getExpressionNamePrefix(node: Identifier): string;
|
||||
getPropertyAccessSubstitution(node: PropertyAccess): string;
|
||||
getExportAssignmentName(node: SourceFile): string;
|
||||
isReferencedImportDeclaration(node: ImportDeclaration): boolean;
|
||||
isTopLevelValueImportedViaEntityName(node: ImportDeclaration): boolean;
|
||||
@ -693,6 +696,10 @@ module ts {
|
||||
writeReturnTypeOfSignatureDeclaration(signatureDeclaration: SignatureDeclaration, enclosingDeclaration: Node, flags: TypeFormatFlags, writer: TextWriter): void;
|
||||
isSymbolAccessible(symbol: Symbol, enclosingDeclaration: Node, meaning: SymbolFlags): SymbolAccessiblityResult;
|
||||
isImportDeclarationEntityNameReferenceDeclarationVisibile(entityName: EntityName): SymbolAccessiblityResult;
|
||||
|
||||
// Returns the constant value this property access resolves to, or 'undefined' if it does
|
||||
// resolve to a constant.
|
||||
getConstantValue(node: PropertyAccess): number;
|
||||
}
|
||||
|
||||
export enum SymbolFlags {
|
||||
@ -794,13 +801,16 @@ module ts {
|
||||
}
|
||||
|
||||
export enum NodeCheckFlags {
|
||||
TypeChecked = 0x00000001, // Node has been type checked
|
||||
LexicalThis = 0x00000002, // Lexical 'this' reference
|
||||
CaptureThis = 0x00000004, // Lexical 'this' used in body
|
||||
EmitExtends = 0x00000008, // Emit __extends
|
||||
SuperInstance = 0x00000010, // Instance 'super' reference
|
||||
SuperStatic = 0x00000020, // Static 'super' reference
|
||||
ContextChecked = 0x00000040, // Contextual types have been assigned
|
||||
TypeChecked = 0x00000001, // Node has been type checked
|
||||
LexicalThis = 0x00000002, // Lexical 'this' reference
|
||||
CaptureThis = 0x00000004, // Lexical 'this' used in body
|
||||
EmitExtends = 0x00000008, // Emit __extends
|
||||
SuperInstance = 0x00000010, // Instance 'super' reference
|
||||
SuperStatic = 0x00000020, // Static 'super' reference
|
||||
ContextChecked = 0x00000040, // Contextual types have been assigned
|
||||
|
||||
// Values for enum members have been computed, and any errors have been reported for them.
|
||||
EnumValuesComputedAndChecked = 0x00000080,
|
||||
}
|
||||
|
||||
export interface NodeLinks {
|
||||
|
||||
@ -2304,12 +2304,14 @@ module ts {
|
||||
totalParts.push.apply(totalParts, typeInfoResolver.symbolToDisplayParts(symbol, getContainerNode(node)));
|
||||
|
||||
if (symbol.flags & SymbolFlags.Property ||
|
||||
symbol.flags & SymbolFlags.EnumMember ||
|
||||
symbol.flags & SymbolFlags.Variable) {
|
||||
|
||||
totalParts.push({ text: ":", kind: SymbolDisplayPartKind.punctuation, symbol: undefined });
|
||||
totalParts.push({ text: " ", kind: SymbolDisplayPartKind.space, symbol: undefined });
|
||||
}
|
||||
else if (symbol.flags & SymbolFlags.EnumMember) {
|
||||
|
||||
}
|
||||
|
||||
if (addType) {
|
||||
var type = typeInfoResolver.getTypeOfSymbol(symbol);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user