diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index f0b2d71ccadfc..82fdaf5cdcd5d 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -5,25 +5,51 @@ module ts { - export function isInstantiated(node: Node): boolean { + export enum ModuleInstanceState { + NonInstantiated = 0, + Instantiated = 1, + ConstEnumOnly = 2 + } + + export function getModuleInstanceState(node: Node): ModuleInstanceState { // A module is uninstantiated if it contains only // 1. interface declarations if (node.kind === SyntaxKind.InterfaceDeclaration) { - return false; + return ModuleInstanceState.NonInstantiated; } - // 2. non - exported import declarations + // 2. const enum declarations don't make module instantiated + else if (node.kind === SyntaxKind.EnumDeclaration && isConstEnumDeclaration(node)) { + return ModuleInstanceState.ConstEnumOnly; + } + // 3. non - exported import declarations else if (node.kind === SyntaxKind.ImportDeclaration && !(node.flags & NodeFlags.Export)) { - return false; + return ModuleInstanceState.NonInstantiated; } - // 3. other uninstantiated module declarations. - else if (node.kind === SyntaxKind.ModuleBlock && !forEachChild(node, isInstantiated)) { - return false; + // 4. other uninstantiated module declarations. + else if (node.kind === SyntaxKind.ModuleBlock) { + var state = ModuleInstanceState.NonInstantiated; + forEachChild(node, n => { + switch (getModuleInstanceState(n)) { + case ModuleInstanceState.NonInstantiated: + // child is non-instantiated - continue searching + return false; + case ModuleInstanceState.ConstEnumOnly: + // child is const enum only - record state and continue searching + state = ModuleInstanceState.ConstEnumOnly; + return false; + case ModuleInstanceState.Instantiated: + // child is instantiated - record state and stop + state = ModuleInstanceState.Instantiated; + return true; + } + }); + return state; } - else if (node.kind === SyntaxKind.ModuleDeclaration && !isInstantiated((node).body)) { - return false; + else if (node.kind === SyntaxKind.ModuleDeclaration) { + return getModuleInstanceState((node).body); } else { - return true; + return ModuleInstanceState.Instantiated; } } @@ -248,11 +274,22 @@ module ts { if (node.name.kind === SyntaxKind.StringLiteral) { bindDeclaration(node, SymbolFlags.ValueModule, SymbolFlags.ValueModuleExcludes, /*isBlockScopeContainer*/ true); } - else if (isInstantiated(node)) { - bindDeclaration(node, SymbolFlags.ValueModule, SymbolFlags.ValueModuleExcludes, /*isBlockScopeContainer*/ true); - } else { - bindDeclaration(node, SymbolFlags.NamespaceModule, SymbolFlags.NamespaceModuleExcludes, /*isBlockScopeContainer*/ true); + var state = getModuleInstanceState(node); + if (state === ModuleInstanceState.NonInstantiated) { + bindDeclaration(node, SymbolFlags.NamespaceModule, SymbolFlags.NamespaceModuleExcludes, /*isBlockScopeContainer*/ true); + } + else { + bindDeclaration(node, SymbolFlags.ValueModule, SymbolFlags.ValueModuleExcludes, /*isBlockScopeContainer*/ true); + if (state === ModuleInstanceState.ConstEnumOnly) { + // mark value module as module that contains only enums + node.symbol.constEnumOnlyModule = true; + } + else if (node.symbol.constEnumOnlyModule) { + // const only value module was merged with instantiated module - reset flag + node.symbol.constEnumOnlyModule = false; + } + } } } @@ -364,7 +401,12 @@ module ts { bindDeclaration(node, SymbolFlags.TypeAlias, SymbolFlags.TypeAliasExcludes, /*isBlockScopeContainer*/ false); break; case SyntaxKind.EnumDeclaration: - bindDeclaration(node, SymbolFlags.Enum, SymbolFlags.EnumExcludes, /*isBlockScopeContainer*/ false); + if (isConstEnumDeclaration(node)) { + bindDeclaration(node, SymbolFlags.ConstEnum, SymbolFlags.ConstEnumExcludes, /*isBlockScopeContainer*/ false); + } + else { + bindDeclaration(node, SymbolFlags.RegularEnum, SymbolFlags.RegularEnumExcludes, /*isBlockScopeContainer*/ false); + } break; case SyntaxKind.ModuleDeclaration: bindModuleDeclaration(node); diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 9f612e11673b6..cac2d60d50034 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -186,7 +186,8 @@ module ts { if (flags & SymbolFlags.Function) result |= SymbolFlags.FunctionExcludes; if (flags & SymbolFlags.Class) result |= SymbolFlags.ClassExcludes; if (flags & SymbolFlags.Interface) result |= SymbolFlags.InterfaceExcludes; - if (flags & SymbolFlags.Enum) result |= SymbolFlags.EnumExcludes; + if (flags & SymbolFlags.RegularEnum) result |= SymbolFlags.RegularEnumExcludes; + if (flags & SymbolFlags.ConstEnum) result |= SymbolFlags.ConstEnumExcludes; if (flags & SymbolFlags.ValueModule) result |= SymbolFlags.ValueModuleExcludes; if (flags & SymbolFlags.Method) result |= SymbolFlags.MethodExcludes; if (flags & SymbolFlags.GetAccessor) result |= SymbolFlags.GetAccessorExcludes; @@ -207,6 +208,7 @@ module ts { result.declarations = symbol.declarations.slice(0); result.parent = symbol.parent; if (symbol.valueDeclaration) result.valueDeclaration = symbol.valueDeclaration; + if (symbol.constEnumOnlyModule) result.constEnumOnlyModule = true; if (symbol.members) result.members = cloneSymbolTable(symbol.members); if (symbol.exports) result.exports = cloneSymbolTable(symbol.exports); recordMergedSymbol(result, symbol); @@ -215,6 +217,10 @@ module ts { function extendSymbol(target: Symbol, source: Symbol) { if (!(target.flags & getExcludedSymbolFlags(source.flags))) { + if (source.flags & SymbolFlags.ValueModule && target.flags & SymbolFlags.ValueModule && target.constEnumOnlyModule && !source.constEnumOnlyModule) { + // reset flag when merging instantiated module into value module that has only const enums + target.constEnumOnlyModule = false; + } target.flags |= source.flags; if (!target.valueDeclaration && source.valueDeclaration) target.valueDeclaration = source.valueDeclaration; forEach(source.declarations, node => { @@ -309,6 +315,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); + } + // Resolve a given name for a given meaning at a given location. An error is reported if the name was not found and // the nameNotFoundMessage argument is not undefined. Returns the resolved symbol, or undefined if no symbol with // the given name can be found. @@ -428,18 +450,8 @@ module ts { // Block-scoped variables cannot be used before their definition var declaration = forEach(result.declarations, d => d.flags & NodeFlags.BlockScoped ? d : undefined); Debug.assert(declaration !== undefined, "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)); } } } @@ -4451,8 +4463,8 @@ module ts { if (symbol.flags & SymbolFlags.Import) { // Mark the import as referenced so that we emit it in the final .js file. - // exception: identifiers that appear in type queries - getSymbolLinks(symbol).referenced = !isInTypeQuery(node); + // exception: identifiers that appear in type queries, const enums, modules that contain only const enums + getSymbolLinks(symbol).referenced = !isInTypeQuery(node) && !isConstEnumOrConstEnumOnlyModule(resolveImport(symbol)); } checkCollisionWithCapturedSuperVariable(node, node); @@ -5106,6 +5118,10 @@ module ts { if (objectType === unknownType) return unknownType; + if (isConstEnumObjectType(objectType) && node.index.kind !== SyntaxKind.StringLiteral) { + error(node.index, Diagnostics.Index_expression_arguments_in_const_enums_must_be_of_type_string); + } + // TypeScript 1.0 spec (April 2014): 4.10 Property Access // - If IndexExpr is a string literal or a numeric literal and ObjExpr's apparent type has a property with the name // given by that literal(converted to its string representation in the case of a numeric literal), the property access is of the type of that property. @@ -5120,6 +5136,7 @@ module ts { var name = (node.index).text; var prop = getPropertyOfType(objectType, name); if (prop) { + getNodeLinks(node).resolvedSymbol = prop; return getTypeOfSymbol(prop); } } @@ -5987,6 +6004,14 @@ module ts { return (type.flags & TypeFlags.Structured) !== 0; } + function isConstEnumObjectType(type: Type) : boolean { + return type.flags & (TypeFlags.ObjectType | TypeFlags.Anonymous) && type.symbol && isConstEnumSymbol(type.symbol); + } + + function isConstEnumSymbol(symbol: Symbol): boolean { + return (symbol.flags & SymbolFlags.ConstEnum) !== 0; + } + function checkInstanceOfExpression(node: BinaryExpression, leftType: Type, rightType: Type): Type { // TypeScript 1.0 spec (April 2014): 4.15.4 // The instanceof operator requires the left operand to be of type Any, an object type, or a type parameter type, @@ -6224,6 +6249,21 @@ module ts { } } } + + if (isConstEnumObjectType(type)) { + // enum object type for const enums are only permitted in: + // - 'left' in property access + // - 'object' in indexed access + // - target in rhs of import statement + var ok = + (node.parent.kind === SyntaxKind.PropertyAccess && (node.parent).left === node) || + (node.parent.kind === SyntaxKind.IndexedAccess && (node.parent).object === node) || + ((node.kind === SyntaxKind.Identifier || node.kind === SyntaxKind.QualifiedName) && isInRightSideOfImportOrExportAssignment(node)); + + if (!ok) { + error(node, Diagnostics.const_enums_can_only_be_used_in_property_or_index_access_expressions_or_the_right_hand_side_of_an_import_declaration_or_export_assignment); + } + } return type; } @@ -6898,7 +6938,7 @@ module ts { case SyntaxKind.InterfaceDeclaration: return SymbolFlags.ExportType; case SyntaxKind.ModuleDeclaration: - return (d).name.kind === SyntaxKind.StringLiteral || isInstantiated(d) + return (d).name.kind === SyntaxKind.StringLiteral || getModuleInstanceState(d) !== ModuleInstanceState.NonInstantiated ? SymbolFlags.ExportNamespace | SymbolFlags.ExportValue : SymbolFlags.ExportNamespace; case SyntaxKind.ClassDeclaration: @@ -7135,7 +7175,7 @@ module ts { } // Uninstantiated modules shouldnt do this check - if (node.kind === SyntaxKind.ModuleDeclaration && !isInstantiated(node)) { + if (node.kind === SyntaxKind.ModuleDeclaration && getModuleInstanceState(node) !== ModuleInstanceState.Instantiated) { return; } @@ -7700,23 +7740,6 @@ module ts { checkSourceElement(node.type); } - function getConstantValueForExpression(node: Expression): number { - var isNegative = false; - if (node.kind === SyntaxKind.PrefixOperator) { - var unaryExpression = node; - if (unaryExpression.operator === SyntaxKind.MinusToken || unaryExpression.operator === SyntaxKind.PlusToken) { - node = unaryExpression.operand; - isNegative = unaryExpression.operator === SyntaxKind.MinusToken; - } - } - if (node.kind === SyntaxKind.NumericLiteral) { - var literalText = (node).text; - return isNegative ? -literalText : +literalText; - } - - return undefined; - } - function computeEnumMemberValues(node: EnumDeclaration) { var nodeLinks = getNodeLinks(node); @@ -7725,6 +7748,7 @@ module ts { var enumType = getDeclaredTypeOfSymbol(enumSymbol); var autoValue = 0; var ambient = isInAmbientContext(node); + var enumIsConst = isConstEnumDeclaration(node); forEach(node.members, member => { if(isNumericName(member.name.text)) { @@ -7732,16 +7756,30 @@ module ts { } 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. + autoValue = getConstantValueForEnumMemberInitializer(initializer, enumIsConst); + if (autoValue === undefined) { + if (enumIsConst) { + error(initializer, Diagnostics.In_const_enum_declarations_member_initializer_must_be_constant_expression); + } + else if (!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, /*headMessage*/ undefined); + } + } + else if (enumIsConst) { + if (isNaN(autoValue)) { + error(initializer, Diagnostics.const_enum_member_initializer_was_evaluated_to_disallowed_value_NaN); + } + else if (!isFinite(autoValue)) { + error(initializer, Diagnostics.const_enum_member_initializer_was_evaluated_to_a_non_finite_value); + } } + } - else if (ambient) { + else if (ambient && !enumIsConst) { autoValue = undefined; } @@ -7752,6 +7790,110 @@ module ts { nodeLinks.flags |= NodeCheckFlags.EnumValuesComputed; } + + function getConstantValueForEnumMemberInitializer(initializer: Expression, enumIsConst: boolean): number { + return evalConstant(initializer); + + function evalConstant(e: Node): number { + switch (e.kind) { + case SyntaxKind.PrefixOperator: + var value = evalConstant((e).operand); + if (value === undefined) { + return undefined; + } + switch ((e).operator) { + case SyntaxKind.PlusToken: return value; + case SyntaxKind.MinusToken: return -value; + case SyntaxKind.TildeToken: return enumIsConst ? ~value : undefined; + } + return undefined; + case SyntaxKind.BinaryExpression: + if (!enumIsConst) { + return undefined; + } + + var left = evalConstant((e).left); + if (left === undefined) { + return undefined; + } + var right = evalConstant((e).right); + if (right === undefined) { + return undefined; + } + switch ((e).operator) { + case SyntaxKind.BarToken: return left | right; + case SyntaxKind.AmpersandToken: return left & right; + case SyntaxKind.GreaterThanGreaterThanToken: return left >> right; + case SyntaxKind.GreaterThanGreaterThanGreaterThanToken: return left >>> right; + case SyntaxKind.LessThanLessThanToken: return left << right; + case SyntaxKind.CaretToken: return left ^ right; + case SyntaxKind.AsteriskToken: return left * right; + case SyntaxKind.SlashToken: return left / right; + case SyntaxKind.PlusToken: return left + right; + case SyntaxKind.MinusToken: return left - right; + case SyntaxKind.PercentToken: return left % right; + } + return undefined; + case SyntaxKind.NumericLiteral: + return +(e).text; + case SyntaxKind.ParenExpression: + return enumIsConst ? evalConstant((e).expression) : undefined; + case SyntaxKind.Identifier: + case SyntaxKind.IndexedAccess: + case SyntaxKind.PropertyAccess: + if (!enumIsConst) { + return undefined; + } + + 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 current enum type and later try to fetch member from the type + enumType = currentType; + propertyName = (e).text; + } + else { + if (e.kind === SyntaxKind.IndexedAccess) { + if ((e).index.kind !== SyntaxKind.StringLiteral) { + return undefined; + } + var enumType = getTypeOfNode((e).object); + propertyName = ((e).index).text; + } + else { + var enumType = getTypeOfNode((e).left); + propertyName = (e).right.text; + } + if (enumType !== currentType) { + return undefined; + } + } + + if (propertyName === undefined) { + return undefined; + } + var property = getPropertyOfObjectType(enumType, propertyName); + if (!property || !(property.flags & SymbolFlags.EnumMember)) { + return undefined; + } + var propertyDecl = property.valueDeclaration; + // self references are illegal + if (member === propertyDecl) { + return undefined; + } + + // illegal case: forward reference + if (!isDefinedBefore(propertyDecl, member)) { + return undefined; + } + return getNodeLinks(propertyDecl).enumMemberValue; + } + } + } } function checkEnumDeclaration(node: EnumDeclaration) { @@ -7775,6 +7917,16 @@ module ts { var enumSymbol = getSymbolOfNode(node); var firstDeclaration = getDeclarationOfKind(enumSymbol, node.kind); if (node === firstDeclaration) { + if (enumSymbol.declarations.length > 1) { + var enumIsConst = isConstEnumDeclaration(node); + // check that const is placed\omitted on all enum declarations + forEach(enumSymbol.declarations, decl => { + if (isConstEnumDeclaration(decl) !== enumIsConst) { + error(decl.name, Diagnostics.Enum_declarations_must_all_be_const_or_non_const); + } + }); + } + var seenEnumMissingInitialInitializer = false; forEach(enumSymbol.declarations, declaration => { // return true if we hit a violation of the rule, false otherwise @@ -8608,17 +8760,16 @@ module ts { function getExportAssignmentName(node: SourceFile): string { var symbol = getExportAssignmentSymbol(getSymbolOfNode(node)); - return symbol && symbolIsValue(symbol) ? symbolToString(symbol): undefined; + return symbol && symbolIsValue(symbol) && !isConstEnumSymbol(symbol) ? symbolToString(symbol): undefined; } - function isTopLevelValueImportedViaEntityName(node: ImportDeclaration): boolean { + function isTopLevelValueImportWithEntityName(node: ImportDeclaration): boolean { if (node.parent.kind !== SyntaxKind.SourceFile || !node.entityName) { // parent is not source file or it is not reference to internal module return false; } var symbol = getSymbolOfNode(node); - var target = resolveImport(symbol); - return target !== unknownSymbol && ((target.flags & SymbolFlags.Value) !== 0); + return isImportResolvedToValue(getSymbolOfNode(node)); } function hasSemanticErrors() { @@ -8630,6 +8781,16 @@ module ts { return forEach(getDiagnostics(sourceFile), d => d.isEarly); } + function isImportResolvedToValue(symbol: Symbol): boolean { + var target = resolveImport(symbol); + // const enums and modules that contain only const enums are not considered values from the emit perespective + return target !== unknownSymbol && target.flags & SymbolFlags.Value && !isConstEnumOrConstEnumOnlyModule(target); + } + + function isConstEnumOrConstEnumOnlyModule(s: Symbol): boolean { + return isConstEnumSymbol(s) || s.constEnumOnlyModule; + } + function isReferencedImportDeclaration(node: ImportDeclaration): boolean { var symbol = getSymbolOfNode(node); if (getSymbolLinks(symbol).referenced) { @@ -8638,10 +8799,7 @@ module ts { // logic below will answer 'true' for exported import declaration in a nested module that itself is not exported. // As a consequence this might cause emitting extra. if (node.flags & NodeFlags.Export) { - var target = resolveImport(symbol); - if (target !== unknownSymbol && target.flags & SymbolFlags.Value) { - return true; - } + return isImportResolvedToValue(symbol); } return false; } @@ -8676,7 +8834,7 @@ module ts { return getNodeLinks(node).enumMemberValue; } - function getConstantValue(node: PropertyAccess): number { + function getConstantValue(node: PropertyAccess | IndexedAccess): number { var symbol = getNodeLinks(node).resolvedSymbol; if (symbol && (symbol.flags & SymbolFlags.EnumMember)) { var declaration = symbol.valueDeclaration; @@ -8711,7 +8869,7 @@ module ts { isReferencedImportDeclaration: isReferencedImportDeclaration, getNodeCheckFlags: getNodeCheckFlags, getEnumMemberValue: getEnumMemberValue, - isTopLevelValueImportedViaEntityName: isTopLevelValueImportedViaEntityName, + isTopLevelValueImportWithEntityName: isTopLevelValueImportWithEntityName, hasSemanticErrors: hasSemanticErrors, hasEarlyErrors: hasEarlyErrors, isDeclarationVisible: isDeclarationVisible, diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts index 63c555f1951b8..fed7c991b2e3d 100644 --- a/src/compiler/commandLineParser.ts +++ b/src/compiler/commandLineParser.ts @@ -24,7 +24,7 @@ module ts { type: "boolean", }, { - name: "emitBOM", + name: "emitBOM", type: "boolean" }, { @@ -102,7 +102,7 @@ module ts { { name: "target", shortName: "t", - type: { "es3": ScriptTarget.ES3, "es5": ScriptTarget.ES5 , "es6": ScriptTarget.ES6 }, + type: { "es3": ScriptTarget.ES3, "es5": ScriptTarget.ES5, "es6": ScriptTarget.ES6 }, description: Diagnostics.Specify_ECMAScript_target_version_Colon_ES3_default_ES5_or_ES6_experimental, paramType: Diagnostics.VERSION, error: Diagnostics.Argument_for_target_option_must_be_es3_es5_or_es6 @@ -118,6 +118,11 @@ module ts { shortName: "w", type: "boolean", description: Diagnostics.Watch_input_files, + }, + { + name: "preserveConstEnums", + type: "boolean", + description: Diagnostics.Do_not_erase_const_enum_declarations_in_generated_code } ]; diff --git a/src/compiler/diagnosticInformationMap.generated.ts b/src/compiler/diagnosticInformationMap.generated.ts index 7ab19bd5074e7..9138baeda4117 100644 --- a/src/compiler/diagnosticInformationMap.generated.ts +++ b/src/compiler/diagnosticInformationMap.generated.ts @@ -352,6 +352,12 @@ module ts { Exported_type_alias_0_has_or_is_using_name_1_from_external_module_2_but_cannot_be_named: { code: 4079, category: DiagnosticCategory.Error, key: "Exported type alias '{0}' has or is using name '{1}' from external module {2} but cannot be named." }, Exported_type_alias_0_has_or_is_using_name_1_from_private_module_2: { code: 4080, category: DiagnosticCategory.Error, key: "Exported type alias '{0}' has or is using name '{1}' from private module '{2}'." }, Exported_type_alias_0_has_or_is_using_private_name_1: { code: 4081, category: DiagnosticCategory.Error, key: "Exported type alias '{0}' has or is using private name '{1}'." }, + Enum_declarations_must_all_be_const_or_non_const: { code: 4082, category: DiagnosticCategory.Error, key: "Enum declarations must all be const or non-const." }, + In_const_enum_declarations_member_initializer_must_be_constant_expression: { code: 4083, category: DiagnosticCategory.Error, key: "In 'const' enum declarations member initializer must be constant expression.", isEarly: true }, + const_enums_can_only_be_used_in_property_or_index_access_expressions_or_the_right_hand_side_of_an_import_declaration_or_export_assignment: { code: 4084, category: DiagnosticCategory.Error, key: "'const' enums can only be used in property or index access expressions or the right hand side of an import declaration or export assignment." }, + Index_expression_arguments_in_const_enums_must_be_of_type_string: { code: 4085, category: DiagnosticCategory.Error, key: "Index expression arguments in 'const' enums must be of type 'string'." }, + const_enum_member_initializer_was_evaluated_to_a_non_finite_value: { code: 4086, category: DiagnosticCategory.Error, key: "'const' enum member initializer was evaluated to a non-finite value." }, + const_enum_member_initializer_was_evaluated_to_disallowed_value_NaN: { code: 4087, category: DiagnosticCategory.Error, key: "'const' enum member initializer was evaluated to disallowed value 'NaN'." }, The_current_host_does_not_support_the_0_option: { code: 5001, category: DiagnosticCategory.Error, key: "The current host does not support the '{0}' option." }, Cannot_find_the_common_subdirectory_path_for_the_input_files: { code: 5009, category: DiagnosticCategory.Error, key: "Cannot find the common subdirectory path for the input files." }, Cannot_read_file_0_Colon_1: { code: 5012, category: DiagnosticCategory.Error, key: "Cannot read file '{0}': {1}" }, @@ -366,6 +372,7 @@ module ts { Specifies_the_location_where_debugger_should_locate_TypeScript_files_instead_of_source_locations: { code: 6004, category: DiagnosticCategory.Message, key: "Specifies the location where debugger should locate TypeScript files instead of source locations." }, Watch_input_files: { code: 6005, category: DiagnosticCategory.Message, key: "Watch input files." }, Redirect_output_structure_to_the_directory: { code: 6006, category: DiagnosticCategory.Message, key: "Redirect output structure to the directory." }, + Do_not_erase_const_enum_declarations_in_generated_code: { code: 6007, category: DiagnosticCategory.Message, key: "Do not erase const enum declarations in generated code." }, Do_not_emit_comments_to_output: { code: 6009, category: DiagnosticCategory.Message, key: "Do not emit comments to output." }, Specify_ECMAScript_target_version_Colon_ES3_default_ES5_or_ES6_experimental: { code: 6015, category: DiagnosticCategory.Message, key: "Specify ECMAScript target version: 'ES3' (default), 'ES5', or 'ES6' (experimental)" }, Specify_module_code_generation_Colon_commonjs_or_amd: { code: 6016, category: DiagnosticCategory.Message, key: "Specify module code generation: 'commonjs' or 'amd'" }, diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 3e380ea5a67ae..b6c9df1cf3666 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -1409,13 +1409,35 @@ "category": "Error", "code": 4081 }, - - + "Enum declarations must all be const or non-const.": { + "category": "Error", + "code": 4082 + }, + "In 'const' enum declarations member initializer must be constant expression.": { + "category": "Error", + "code": 4083, + "isEarly": true + }, + "'const' enums can only be used in property or index access expressions or the right hand side of an import declaration or export assignment.": { + "category": "Error", + "code": 4084 + }, + "Index expression arguments in 'const' enums must be of type 'string'.": { + "category": "Error", + "code": 4085 + }, + "'const' enum member initializer was evaluated to a non-finite value.": { + "category": "Error", + "code": 4086 + }, + "'const' enum member initializer was evaluated to disallowed value 'NaN'.": { + "category": "Error", + "code": 4087 + }, "The current host does not support the '{0}' option.": { "category": "Error", "code": 5001 }, - "Cannot find the common subdirectory path for the input files.": { "category": "Error", "code": 5009 @@ -1468,6 +1490,10 @@ "category": "Message", "code": 6006 }, + "Do not erase const enum declarations in generated code.": { + "category": "Message", + "code": 6007 + }, "Do not emit comments to output.": { "category": "Message", "code": 6009 diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 782d22a0bf462..e39f35011fa8f 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -1031,19 +1031,29 @@ module ts { emitTrailingComments(node); } - function emitPropertyAccess(node: PropertyAccess) { + function tryEmitConstantValue(node: PropertyAccess | IndexedAccess): boolean { var constantValue = resolver.getConstantValue(node); if (constantValue !== undefined) { - write(constantValue.toString() + " /* " + identifierToString(node.right) + " */"); + var propertyName = node.kind === SyntaxKind.PropertyAccess ? identifierToString((node).right) : getTextOfNode((node).index); + write(constantValue.toString() + " /* " + propertyName + " */"); + return true; } - else { - emit(node.left); - write("."); - emit(node.right); + return false; + } + + function emitPropertyAccess(node: PropertyAccess) { + if (tryEmitConstantValue(node)) { + return; } + emit(node.left); + write("."); + emit(node.right); } function emitIndexedAccess(node: IndexedAccess) { + if (tryEmitConstantValue(node)) { + return; + } emit(node.object); write("["); emit(node.index); @@ -1876,6 +1886,10 @@ module ts { } function emitEnumDeclaration(node: EnumDeclaration) { + // const enums are completely erased during compilation. + if (isConstEnumDeclaration(node) && !compilerOptions.preserveConstEnums) { + return; + } emitLeadingComments(node); if (!(node.flags & NodeFlags.Export)) { emitStart(node); @@ -1950,7 +1964,7 @@ module ts { } function emitModuleDeclaration(node: ModuleDeclaration) { - if (!isInstantiated(node)) { + if (getModuleInstanceState(node) !== ModuleInstanceState.Instantiated) { return emitPinnedOrTripleSlashComments(node); } emitLeadingComments(node); @@ -2002,7 +2016,7 @@ module ts { // preserve old compiler's behavior: emit 'var' for import declaration (even if we do not consider them referenced) when // - current file is not external module // - import declaration is top level and target is value imported by entity name - emitImportDeclaration = !isExternalModule(currentSourceFile) && resolver.isTopLevelValueImportedViaEntityName(node); + emitImportDeclaration = !isExternalModule(currentSourceFile) && resolver.isTopLevelValueImportWithEntityName(node); } if (emitImportDeclaration) { @@ -2722,6 +2736,9 @@ module ts { if (resolver.isDeclarationVisible(node)) { emitJsDocComments(node); emitDeclarationFlags(node); + if (isConstEnumDeclaration(node)) { + write("const ") + } write("enum "); emitSourceTextOfNode(node.name); write(" {"); diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 0c5af3660a2ae..77177bc73bc04 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -115,6 +115,10 @@ module ts { return (file.flags & NodeFlags.DeclarationFile) !== 0; } + export function isConstEnumDeclaration(node: EnumDeclaration): boolean { + return (node.flags & NodeFlags.Const) !== 0; + } + export function isPrologueDirective(node: Node): boolean { return node.kind === SyntaxKind.ExpressionStatement && (node).expression.kind === SyntaxKind.StringLiteral; } @@ -3246,7 +3250,6 @@ module ts { case SyntaxKind.OpenBraceToken: case SyntaxKind.VarKeyword: case SyntaxKind.LetKeyword: - case SyntaxKind.ConstKeyword: case SyntaxKind.FunctionKeyword: case SyntaxKind.IfKeyword: case SyntaxKind.DoKeyword: @@ -3265,6 +3268,12 @@ module ts { case SyntaxKind.CatchKeyword: case SyntaxKind.FinallyKeyword: return true; + case SyntaxKind.ConstKeyword: + // const keyword can precede enum keyword when defining constant enums + // 'const enum' do not start statement. + // In ES 6 'enum' is a future reserved keyword, so it should not be used as identifier + var isConstEnum = lookAhead(() => nextToken() === SyntaxKind.EnumKeyword); + return !isConstEnum; case SyntaxKind.InterfaceKeyword: case SyntaxKind.ClassKeyword: case SyntaxKind.ModuleKeyword: @@ -3275,6 +3284,7 @@ module ts { if (isDeclarationStart()) { return false; } + case SyntaxKind.PublicKeyword: case SyntaxKind.PrivateKeyword: case SyntaxKind.ProtectedKeyword: @@ -3296,6 +3306,7 @@ module ts { case SyntaxKind.VarKeyword: case SyntaxKind.LetKeyword: case SyntaxKind.ConstKeyword: + // const here should always be parsed as const declaration because of check in 'isStatement' return parseVariableStatement(allowLetAndConstDeclarations); case SyntaxKind.FunctionKeyword: return parseFunctionDeclaration(); @@ -3867,6 +3878,7 @@ module ts { } function parseAndCheckEnumDeclaration(pos: number, flags: NodeFlags): EnumDeclaration { + var enumIsConst = flags & NodeFlags.Const; function isIntegerLiteral(expression: Expression): boolean { function isInteger(literalExpression: LiteralExpression): boolean { // Allows for scientific notation since literalExpression.text was formed by @@ -3901,22 +3913,29 @@ module ts { node.name = parsePropertyName(); node.initializer = parseInitializer(/*inParameter*/ false); - if (inAmbientContext) { - if (node.initializer && !isIntegerLiteral(node.initializer) && errorCountBeforeEnumMember === file.syntacticErrors.length) { - grammarErrorOnNode(node.name, Diagnostics.Ambient_enum_elements_can_only_have_integer_literal_initializers); + // skip checks below for const enums - they allow arbitrary initializers as long as they can be evaluated to constant expressions. + // since all values are known in compile time - it is not necessary to check that constant enum section precedes computed enum members. + if (!enumIsConst) { + if (inAmbientContext) { + if (node.initializer && !isIntegerLiteral(node.initializer) && errorCountBeforeEnumMember === file.syntacticErrors.length) { + grammarErrorOnNode(node.name, Diagnostics.Ambient_enum_elements_can_only_have_integer_literal_initializers); + } + } + else if (node.initializer) { + inConstantEnumMemberSection = isIntegerLiteral(node.initializer); + } + else if (!inConstantEnumMemberSection && errorCountBeforeEnumMember === file.syntacticErrors.length) { + grammarErrorOnNode(node.name, Diagnostics.Enum_member_must_have_initializer); } - } - else if (node.initializer) { - inConstantEnumMemberSection = isIntegerLiteral(node.initializer); - } - else if (!inConstantEnumMemberSection && errorCountBeforeEnumMember === file.syntacticErrors.length) { - grammarErrorOnNode(node.name, Diagnostics.Enum_member_must_have_initializer); } return finishNode(node); } var node = createNode(SyntaxKind.EnumDeclaration, pos); node.flags = flags; + if (enumIsConst) { + parseExpected(SyntaxKind.ConstKeyword); + } parseExpected(SyntaxKind.EnumKeyword); node.name = parseIdentifier(); if (parseExpected(SyntaxKind.OpenBraceToken)) { @@ -4073,9 +4092,17 @@ module ts { switch (token) { case SyntaxKind.VarKeyword: case SyntaxKind.LetKeyword: - case SyntaxKind.ConstKeyword: result = parseVariableStatement(/*allowLetAndConstDeclarations*/ true, pos, flags); break; + case SyntaxKind.ConstKeyword: + var isConstEnum = lookAhead(() => nextToken() === SyntaxKind.EnumKeyword); + if (isConstEnum) { + result = parseAndCheckEnumDeclaration(pos, flags | NodeFlags.Const); + } + else { + result = parseVariableStatement(/*allowLetAndConstDeclarations*/ true, pos, flags); + } + break; case SyntaxKind.FunctionKeyword: result = parseFunctionDeclaration(pos, flags); break; diff --git a/src/compiler/types.ts b/src/compiler/types.ts index ead8495773190..bd43fe94ec850 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -763,7 +763,7 @@ module ts { getExpressionNamePrefix(node: Identifier): string; getExportAssignmentName(node: SourceFile): string; isReferencedImportDeclaration(node: ImportDeclaration): boolean; - isTopLevelValueImportedViaEntityName(node: ImportDeclaration): boolean; + isTopLevelValueImportWithEntityName(node: ImportDeclaration): boolean; getNodeCheckFlags(node: Node): NodeCheckFlags; getEnumMemberValue(node: EnumMember): number; hasSemanticErrors(): boolean; @@ -774,7 +774,7 @@ module ts { isSymbolAccessible(symbol: Symbol, enclosingDeclaration: Node, meaning: SymbolFlags): SymbolAccessiblityResult; isImportDeclarationEntityNameReferenceDeclarationVisibile(entityName: EntityName): SymbolAccessiblityResult; // Returns the constant value this property access resolves to, or 'undefined' for a non-constant - getConstantValue(node: PropertyAccess): number; + getConstantValue(node: PropertyAccess | IndexedAccess): number; hasEarlyErrors(sourceFile?: SourceFile): boolean; } @@ -786,32 +786,34 @@ module ts { Function = 0x00000010, // Function Class = 0x00000020, // Class Interface = 0x00000040, // Interface - Enum = 0x00000080, // Enum - ValueModule = 0x00000100, // Instantiated module - NamespaceModule = 0x00000200, // Uninstantiated module - TypeLiteral = 0x00000400, // Type Literal - ObjectLiteral = 0x00000800, // Object Literal - Method = 0x00001000, // Method - Constructor = 0x00002000, // Constructor - GetAccessor = 0x00004000, // Get accessor - SetAccessor = 0x00008000, // Set accessor - CallSignature = 0x00010000, // Call signature - ConstructSignature = 0x00020000, // Construct signature - IndexSignature = 0x00040000, // Index signature - TypeParameter = 0x00080000, // Type parameter - TypeAlias = 0x00100000, // Type alias + ConstEnum = 0x00000080, // Const enum + RegularEnum = 0x00000100, // Enum + ValueModule = 0x00000200, // Instantiated module + NamespaceModule = 0x00000400, // Uninstantiated module + TypeLiteral = 0x00000800, // Type Literal + ObjectLiteral = 0x00001000, // Object Literal + Method = 0x00002000, // Method + Constructor = 0x00004000, // Constructor + GetAccessor = 0x00008000, // Get accessor + SetAccessor = 0x00010000, // Set accessor + CallSignature = 0x00020000, // Call signature + ConstructSignature = 0x00040000, // Construct signature + IndexSignature = 0x00080000, // Index signature + TypeParameter = 0x00100000, // Type parameter + TypeAlias = 0x00200000, // Type alias // Export markers (see comment in declareModuleMember in binder) - ExportValue = 0x00200000, // Exported value marker - ExportType = 0x00400000, // Exported type marker - ExportNamespace = 0x00800000, // Exported namespace marker - Import = 0x01000000, // Import - Instantiated = 0x02000000, // Instantiated symbol - Merged = 0x04000000, // Merged symbol (created during program binding) - Transient = 0x08000000, // Transient symbol (created during type check) - Prototype = 0x10000000, // Prototype property (no source representation) - UnionProperty = 0x20000000, // Property in union type - + ExportValue = 0x00400000, // Exported value marker + ExportType = 0x00800000, // Exported type marker + ExportNamespace = 0x01000000, // Exported namespace marker + Import = 0x02000000, // Import + Instantiated = 0x04000000, // Instantiated symbol + Merged = 0x08000000, // Merged symbol (created during program binding) + Transient = 0x10000000, // Transient symbol (created during type check) + Prototype = 0x20000000, // Prototype property (no source representation) + UnionProperty = 0x40000000, // Property in union type + + Enum = RegularEnum | ConstEnum, Variable = FunctionScopedVariable | BlockScopedVariable, Value = Variable | Property | EnumMember | Function | Class | Enum | ValueModule | Method | GetAccessor | SetAccessor, Type = Class | Interface | Enum | TypeLiteral | ObjectLiteral | TypeParameter | TypeAlias, @@ -834,8 +836,9 @@ module ts { FunctionExcludes = Value & ~(Function | ValueModule), ClassExcludes = (Value | Type) & ~ValueModule, InterfaceExcludes = Type & ~Interface, - EnumExcludes = (Value | Type) & ~(Enum | ValueModule), - ValueModuleExcludes = Value & ~(Function | Class | Enum | ValueModule), + RegularEnumExcludes = (Value | Type) & ~(RegularEnum | ValueModule), // regular enums merge only with regular enums and modules + ConstEnumExcludes = (Value | Type) & ~ConstEnum, // const enums merge only with const enums + ValueModuleExcludes = Value & ~(Function | Class | RegularEnum | ValueModule), NamespaceModuleExcludes = 0, MethodExcludes = Value & ~Method, GetAccessorExcludes = Value & ~SetAccessor, @@ -867,7 +870,8 @@ module ts { members?: SymbolTable; // Class, interface or literal instance members exports?: SymbolTable; // Module exports exportSymbol?: Symbol; // Exported symbol associated with this symbol - valueDeclaration?: Declaration // First value declaration of the symbol + valueDeclaration?: Declaration // First value declaration of the symbol, + constEnumOnlyModule?: boolean // For modules - if true - module contains only const enums or other modules with only const enums. } export interface SymbolLinks { @@ -1104,6 +1108,7 @@ module ts { target?: ScriptTarget; version?: boolean; watch?: boolean; + preserveConstEnums?: boolean; [option: string]: string | number | boolean; } diff --git a/src/harness/harness.ts b/src/harness/harness.ts index 60c5da5a1a68a..3b64868504166 100644 --- a/src/harness/harness.ts +++ b/src/harness/harness.ts @@ -757,7 +757,6 @@ module Harness { case 'codepage': case 'createFileLog': case 'filename': - case 'propagateenumconstants': case 'removecomments': case 'watch': case 'allowautomaticsemicoloninsertion': @@ -772,7 +771,9 @@ module Harness { case 'errortruncation': options.noErrorTruncation = setting.value === 'false'; break; - + case 'preserveconstenums': + options.preserveConstEnums = setting.value === 'true'; + 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", "preserveconstenums"]; function extractCompilerSettings(content: string): CompilerSetting[] { diff --git a/src/services/breakpoints.ts b/src/services/breakpoints.ts index 3a72ee9bfa63a..3a82e71c677a3 100644 --- a/src/services/breakpoints.ts +++ b/src/services/breakpoints.ts @@ -178,7 +178,7 @@ module ts.BreakpointResolver { case SyntaxKind.ModuleDeclaration: // span on complete module if it is instantiated - if (!isInstantiated(node)) { + if (getModuleInstanceState(node) !== ModuleInstanceState.Instantiated) { return undefined; } @@ -350,7 +350,7 @@ module ts.BreakpointResolver { function spanInBlock(block: Block): TypeScript.TextSpan { switch (block.parent.kind) { case SyntaxKind.ModuleDeclaration: - if (!isInstantiated(block.parent)) { + if (getModuleInstanceState(block.parent) !== ModuleInstanceState.Instantiated) { return undefined; } @@ -407,7 +407,7 @@ module ts.BreakpointResolver { switch (node.parent.kind) { case SyntaxKind.ModuleBlock: // If this is not instantiated module block no bp span - if (!isInstantiated(node.parent.parent)) { + if (getModuleInstanceState(node.parent.parent) !== ModuleInstanceState.Instantiated) { return undefined; } diff --git a/src/services/services.ts b/src/services/services.ts index d208eb87e96ea..d9d6b32b581fa 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -4572,7 +4572,7 @@ module ts { if ((node).name.kind === SyntaxKind.StringLiteral) { return SemanticMeaning.Namespace | SemanticMeaning.Value; } - else if (isInstantiated(node)) { + else if (getModuleInstanceState(node) === ModuleInstanceState.Instantiated) { return SemanticMeaning.Namespace | SemanticMeaning.Value; } else { @@ -4849,7 +4849,7 @@ module ts { */ function hasValueSideModule(symbol: Symbol): boolean { return forEach(symbol.declarations, declaration => { - return declaration.kind === SyntaxKind.ModuleDeclaration && isInstantiated(declaration); + return declaration.kind === SyntaxKind.ModuleDeclaration && getModuleInstanceState(declaration) == ModuleInstanceState.Instantiated; }); } } diff --git a/tests/baselines/reference/additionOperatorWithNullValueAndValidOperator.js b/tests/baselines/reference/additionOperatorWithNullValueAndValidOperator.js index f34ac61d86d55..1ba54d47f1cb3 100644 --- a/tests/baselines/reference/additionOperatorWithNullValueAndValidOperator.js +++ b/tests/baselines/reference/additionOperatorWithNullValueAndValidOperator.js @@ -50,12 +50,12 @@ var r3 = null + b; var r4 = null + 1; var r5 = null + c; var r6 = null + 0 /* a */; -var r7 = null + E['a']; +var r7 = null + 0 /* 'a' */; var r8 = b + null; var r9 = 1 + null; var r10 = c + null; var r11 = 0 /* a */ + null; -var r12 = E['a'] + null; +var r12 = 0 /* 'a' */ + null; // null + string var r13 = null + d; var r14 = null + ''; diff --git a/tests/baselines/reference/additionOperatorWithNumberAndEnum.js b/tests/baselines/reference/additionOperatorWithNumberAndEnum.js index 63e038265d64c..05bca7a4c0cd5 100644 --- a/tests/baselines/reference/additionOperatorWithNumberAndEnum.js +++ b/tests/baselines/reference/additionOperatorWithNumberAndEnum.js @@ -29,4 +29,4 @@ var r4 = b + b; var r5 = 0 + a; var r6 = 0 /* a */ + 0; var r7 = 0 /* a */ + 1 /* b */; -var r8 = E['a'] + E['b']; +var r8 = 0 /* 'a' */ + 1 /* 'b' */; diff --git a/tests/baselines/reference/additionOperatorWithUndefinedValueAndValidOperator.js b/tests/baselines/reference/additionOperatorWithUndefinedValueAndValidOperator.js index a6a1b2ef555c3..cb89d242a18a6 100644 --- a/tests/baselines/reference/additionOperatorWithUndefinedValueAndValidOperator.js +++ b/tests/baselines/reference/additionOperatorWithUndefinedValueAndValidOperator.js @@ -50,12 +50,12 @@ var r3 = undefined + b; var r4 = undefined + 1; var r5 = undefined + c; var r6 = undefined + 0 /* a */; -var r7 = undefined + E['a']; +var r7 = undefined + 0 /* 'a' */; var r8 = b + undefined; var r9 = 1 + undefined; var r10 = c + undefined; var r11 = 0 /* a */ + undefined; -var r12 = E['a'] + undefined; +var r12 = 0 /* 'a' */ + undefined; // undefined + string var r13 = undefined + d; var r14 = undefined + ''; diff --git a/tests/baselines/reference/bitwiseNotOperatorWithEnumType.js b/tests/baselines/reference/bitwiseNotOperatorWithEnumType.js index 9cd5a63912e09..710af4edfa779 100644 --- a/tests/baselines/reference/bitwiseNotOperatorWithEnumType.js +++ b/tests/baselines/reference/bitwiseNotOperatorWithEnumType.js @@ -30,11 +30,11 @@ var ENUM1; // enum type var var ResultIsNumber1 = ~ENUM1; // enum type expressions -var ResultIsNumber2 = ~ENUM1["A"]; -var ResultIsNumber3 = ~(0 /* A */ + ENUM1["B"]); +var ResultIsNumber2 = ~0 /* "A" */; +var ResultIsNumber3 = ~(0 /* A */ + 1 /* "B" */); // multiple ~ operators -var ResultIsNumber4 = ~~~(ENUM1["A"] + 1 /* B */); +var ResultIsNumber4 = ~~~(0 /* "A" */ + 1 /* B */); // miss assignment operators ~ENUM1; -~ENUM1["A"]; -~0 /* A */, ~ENUM1["B"]; +~0 /* "A" */; +~0 /* A */, ~1 /* "B" */; diff --git a/tests/baselines/reference/constEnumDeclarations.js b/tests/baselines/reference/constEnumDeclarations.js new file mode 100644 index 0000000000000..1cbacb13bbd5f --- /dev/null +++ b/tests/baselines/reference/constEnumDeclarations.js @@ -0,0 +1,28 @@ +//// [constEnumDeclarations.ts] + +const enum E { + A = 1, + B = 2, + C = A | B +} + +const enum E2 { + A = 1, + B, + C +} + +//// [constEnumDeclarations.js] + + +//// [constEnumDeclarations.d.ts] +declare const enum E { + A = 1, + B = 2, + C = 3, +} +declare const enum E2 { + A = 1, + B = 2, + C = 3, +} diff --git a/tests/baselines/reference/constEnumDeclarations.types b/tests/baselines/reference/constEnumDeclarations.types new file mode 100644 index 0000000000000..9dc87eadef7c6 --- /dev/null +++ b/tests/baselines/reference/constEnumDeclarations.types @@ -0,0 +1,30 @@ +=== tests/cases/compiler/constEnumDeclarations.ts === + +const enum E { +>E : E + + A = 1, +>A : E + + B = 2, +>B : E + + C = A | B +>C : E +>A | B : number +>A : E +>B : E +} + +const enum E2 { +>E2 : E2 + + A = 1, +>A : E2 + + B, +>B : E2 + + C +>C : E2 +} diff --git a/tests/baselines/reference/constEnumErrors.errors.txt b/tests/baselines/reference/constEnumErrors.errors.txt new file mode 100644 index 0000000000000..541f814af983d --- /dev/null +++ b/tests/baselines/reference/constEnumErrors.errors.txt @@ -0,0 +1,85 @@ +tests/cases/compiler/constEnumErrors.ts(1,12): error TS2300: Duplicate identifier 'E'. +tests/cases/compiler/constEnumErrors.ts(5,8): error TS2300: Duplicate identifier 'E'. +tests/cases/compiler/constEnumErrors.ts(12,9): error TS4083: In 'const' enum declarations member initializer must be constant expression. +tests/cases/compiler/constEnumErrors.ts(14,9): error TS4083: In 'const' enum declarations member initializer must be constant expression. +tests/cases/compiler/constEnumErrors.ts(15,10): error TS4083: In 'const' enum declarations member initializer must be constant expression. +tests/cases/compiler/constEnumErrors.ts(22,13): error TS4085: Index expression arguments in 'const' enums must be of type 'string'. +tests/cases/compiler/constEnumErrors.ts(24,13): error TS4085: Index expression arguments in 'const' enums must be of type 'string'. +tests/cases/compiler/constEnumErrors.ts(26,9): error TS4084: 'const' enums can only be used in property or index access expressions or the right hand side of an import declaration or export assignment. +tests/cases/compiler/constEnumErrors.ts(27,10): error TS4084: 'const' enums can only be used in property or index access expressions or the right hand side of an import declaration or export assignment. +tests/cases/compiler/constEnumErrors.ts(32,5): error TS4084: 'const' enums can only be used in property or index access expressions or the right hand side of an import declaration or export assignment. +tests/cases/compiler/constEnumErrors.ts(40,9): error TS4086: 'const' enum member initializer was evaluated to a non-finite value. +tests/cases/compiler/constEnumErrors.ts(41,9): error TS4086: 'const' enum member initializer was evaluated to a non-finite value. +tests/cases/compiler/constEnumErrors.ts(42,9): error TS4087: 'const' enum member initializer was evaluated to disallowed value 'NaN'. + + +==== tests/cases/compiler/constEnumErrors.ts (13 errors) ==== + const enum E { + ~ +!!! error TS2300: Duplicate identifier 'E'. + A + } + + module E { + ~ +!!! error TS2300: Duplicate identifier 'E'. + var x = 1; + } + + const enum E1 { + // illegal case + // forward reference to the element of the same enum + X = Y, + ~ +!!! error TS4083: In 'const' enum declarations member initializer must be constant expression. + // forward reference to the element of the same enum + Y = E1.Z, + ~~~~ +!!! error TS4083: In 'const' enum declarations member initializer must be constant expression. + Y1 = E1["Z"] + ~~~~~~~ +!!! error TS4083: In 'const' enum declarations member initializer must be constant expression. + } + + const enum E2 { + A + } + + var y0 = E2[1] + ~ +!!! error TS4085: Index expression arguments in 'const' enums must be of type 'string'. + var name = "A"; + var y1 = E2[name]; + ~~~~ +!!! error TS4085: Index expression arguments in 'const' enums must be of type 'string'. + + var x = E2; + ~~ +!!! error TS4084: 'const' enums can only be used in property or index access expressions or the right hand side of an import declaration or export assignment. + var y = [E2]; + ~~ +!!! error TS4084: 'const' enums can only be used in property or index access expressions or the right hand side of an import declaration or export assignment. + + function foo(t: any): void { + } + + foo(E2); + ~~ +!!! error TS4084: 'const' enums can only be used in property or index access expressions or the right hand side of an import declaration or export assignment. + + const enum NaNOrInfinity { + A = 9007199254740992, + B = A * A, + C = B * B, + D = C * C, + E = D * D, + F = E * E, // overflow + ~~~~~ +!!! error TS4086: 'const' enum member initializer was evaluated to a non-finite value. + G = 1 / 0, // overflow + ~~~~~ +!!! error TS4086: 'const' enum member initializer was evaluated to a non-finite value. + H = 0 / 0 // NaN + ~~~~~ +!!! error TS4087: 'const' enum member initializer was evaluated to disallowed value 'NaN'. + } \ No newline at end of file diff --git a/tests/baselines/reference/constEnumExternalModule.js b/tests/baselines/reference/constEnumExternalModule.js new file mode 100644 index 0000000000000..4054ba788cb63 --- /dev/null +++ b/tests/baselines/reference/constEnumExternalModule.js @@ -0,0 +1,19 @@ +//// [tests/cases/compiler/constEnumExternalModule.ts] //// + +//// [m1.ts] +const enum E { + V = 100 +} + +export = E +//// [m2.ts] +import A = require('m1') +var v = A.V; + +//// [m1.js] +define(["require", "exports"], function (require, exports) { +}); +//// [m2.js] +define(["require", "exports"], function (require, exports) { + var v = 100 /* V */; +}); diff --git a/tests/baselines/reference/constEnumExternalModule.types b/tests/baselines/reference/constEnumExternalModule.types new file mode 100644 index 0000000000000..9c7b43a24d45a --- /dev/null +++ b/tests/baselines/reference/constEnumExternalModule.types @@ -0,0 +1,21 @@ +=== tests/cases/compiler/m2.ts === +import A = require('m1') +>A : typeof A + +var v = A.V; +>v : A +>A.V : A +>A : typeof A +>V : A + +=== tests/cases/compiler/m1.ts === +const enum E { +>E : E + + V = 100 +>V : E +} + +export = E +>E : E + diff --git a/tests/baselines/reference/constEnums.js b/tests/baselines/reference/constEnums.js new file mode 100644 index 0000000000000..b7077c74df1da --- /dev/null +++ b/tests/baselines/reference/constEnums.js @@ -0,0 +1,226 @@ +//// [constEnums.ts] +const enum Enum1 { + A0 = 100, +} + +const 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 | 5), + 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, + W3 = Enum1["A0"], + W4 = Enum1["W"], +} + + +module A { + export module B { + export module C { + export const enum E { + V1 = 1, + V2 = A.B.C.E.V1 | 100 + } + } + } +} + +module A { + export module B { + export module C { + export const enum E { + V3 = A.B.C.E["V2"] & 200, + } + } + } +} + +module A1 { + export module B { + export module C { + export const enum E { + V1 = 10, + V2 = 110, + } + } + } +} + +module A2 { + export module B { + export module C { + export const enum E { + V1 = 10, + V2 = 110, + } + } + // module C will be classified as value + export module C { + var x = 1 + } + } +} + +import I = A.B.C.E; +import I1 = A1.B; +import I2 = A2.B; + +function foo0(e: I): void { + if (e === I.V1) { + } + else if (e === I.V2) { + } +} + +function foo1(e: I1.C.E): void { + if (e === I1.C.E.V1) { + } + else if (e === I1.C.E.V2) { + } +} + +function foo2(e: I2.C.E): void { + if (e === I2.C.E.V1) { + } + else if (e === I2.C.E.V2) { + } +} + + +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.W3: + case Enum1.W4: + 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; + } +} + +//// [constEnums.js] +var A2; +(function (A2) { + var B; + (function (B) { + // module C will be classified as value + var C; + (function (C) { + var x = 1; + })(C = B.C || (B.C = {})); + })(B = A2.B || (A2.B = {})); +})(A2 || (A2 = {})); +var I2 = A2.B; +function foo0(e) { + if (e === 1 /* V1 */) { + } + else if (e === 101 /* V2 */) { + } +} +function foo1(e) { + if (e === 10 /* V1 */) { + } + else if (e === 110 /* V2 */) { + } +} +function foo2(e) { + if (e === 10 /* V1 */) { + } + else if (e === 110 /* V2 */) { + } +} +function foo(x) { + switch (x) { + case 0 /* A */: + case 1 /* B */: + case 10 /* C */: + case 1 /* D */: + case 1 /* E */: + case 1 /* F */: + case 1 /* G */: + case -2 /* H */: + case 0 /* I */: + case 0 /* J */: + case -6 /* 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 100 /* W3 */: + case 11 /* W4 */: + break; + } +} +function bar(e) { + switch (e) { + case 1 /* V1 */: + return 1; + case 101 /* V2 */: + return 1; + case 64 /* V3 */: + return 1; + } +} diff --git a/tests/baselines/reference/constEnums.types b/tests/baselines/reference/constEnums.types new file mode 100644 index 0000000000000..396b2e1c32b52 --- /dev/null +++ b/tests/baselines/reference/constEnums.types @@ -0,0 +1,556 @@ +=== tests/cases/compiler/constEnums.ts === +const enum Enum1 { +>Enum1 : Enum1 + + A0 = 100, +>A0 : Enum1 +} + +const 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 +>1 & 1 : number + + H = ~(A | B), +>H : Enum1 +>~(A | B) : number +>(A | B) : number +>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 | 5), +>K : Enum1 +>~(1 | 5) : number +>(1 | 5) : number +>1 | 5 : 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 + + W3 = Enum1["A0"], +>W3 : Enum1 +>Enum1["A0"] : Enum1 +>Enum1 : typeof Enum1 + + W4 = Enum1["W"], +>W4 : Enum1 +>Enum1["W"] : Enum1 +>Enum1 : typeof Enum1 +} + + +module A { +>A : typeof A + + export module B { +>B : typeof B + + export module C { +>C : typeof C + + export const 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 const 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 + } + } + } +} + +module A1 { +>A1 : typeof A1 + + export module B { +>B : typeof B + + export module C { +>C : typeof C + + export const enum E { +>E : E + + V1 = 10, +>V1 : E + + V2 = 110, +>V2 : E + } + } + } +} + +module A2 { +>A2 : typeof A2 + + export module B { +>B : typeof B + + export module C { +>C : typeof C + + export const enum E { +>E : E + + V1 = 10, +>V1 : E + + V2 = 110, +>V2 : E + } + } + // module C will be classified as value + export module C { +>C : typeof C + + var x = 1 +>x : number + } + } +} + +import I = A.B.C.E; +>I : typeof I +>A : typeof A +>B : typeof A.B +>C : typeof A.B.C +>E : I + +import I1 = A1.B; +>I1 : typeof I1 +>A1 : typeof A1 +>B : typeof I1 + +import I2 = A2.B; +>I2 : typeof I2 +>A2 : typeof A2 +>B : typeof I2 + +function foo0(e: I): void { +>foo0 : (e: I) => void +>e : I +>I : I + + if (e === I.V1) { +>e === I.V1 : boolean +>e : I +>I.V1 : I +>I : typeof I +>V1 : I + } + else if (e === I.V2) { +>e === I.V2 : boolean +>e : I +>I.V2 : I +>I : typeof I +>V2 : I + } +} + +function foo1(e: I1.C.E): void { +>foo1 : (e: I1.C.E) => void +>e : I1.C.E +>I1 : unknown +>C : unknown +>E : I1.C.E + + if (e === I1.C.E.V1) { +>e === I1.C.E.V1 : boolean +>e : I1.C.E +>I1.C.E.V1 : I1.C.E +>I1.C.E : typeof I1.C.E +>I1.C : typeof I1.C +>I1 : typeof I1 +>C : typeof I1.C +>E : typeof I1.C.E +>V1 : I1.C.E + } + else if (e === I1.C.E.V2) { +>e === I1.C.E.V2 : boolean +>e : I1.C.E +>I1.C.E.V2 : I1.C.E +>I1.C.E : typeof I1.C.E +>I1.C : typeof I1.C +>I1 : typeof I1 +>C : typeof I1.C +>E : typeof I1.C.E +>V2 : I1.C.E + } +} + +function foo2(e: I2.C.E): void { +>foo2 : (e: I2.C.E) => void +>e : I2.C.E +>I2 : unknown +>C : unknown +>E : I2.C.E + + if (e === I2.C.E.V1) { +>e === I2.C.E.V1 : boolean +>e : I2.C.E +>I2.C.E.V1 : I2.C.E +>I2.C.E : typeof I2.C.E +>I2.C : typeof I2.C +>I2 : typeof I2 +>C : typeof I2.C +>E : typeof I2.C.E +>V1 : I2.C.E + } + else if (e === I2.C.E.V2) { +>e === I2.C.E.V2 : boolean +>e : I2.C.E +>I2.C.E.V2 : I2.C.E +>I2.C.E : typeof I2.C.E +>I2.C : typeof I2.C +>I2 : typeof I2 +>C : typeof I2.C +>E : typeof I2.C.E +>V2 : I2.C.E + } +} + + +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 + + 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.W3: +>Enum1.W3 : Enum1 +>Enum1 : typeof Enum1 +>W3 : Enum1 + + case Enum1.W4: +>Enum1.W4 : Enum1 +>Enum1 : typeof Enum1 +>W4 : Enum1 + + break; + } +} + +function bar(e: A.B.C.E): number { +>bar : (e: I) => number +>e : I +>A : unknown +>B : unknown +>C : unknown +>E : I + + switch (e) { +>e : I + + case A.B.C.E.V1: return 1; +>A.B.C.E.V1 : I +>A.B.C.E : typeof I +>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 I +>V1 : I + + case A.B.C.E.V2: return 1; +>A.B.C.E.V2 : I +>A.B.C.E : typeof I +>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 I +>V2 : I + + case A.B.C.E.V3: return 1; +>A.B.C.E.V3 : I +>A.B.C.E : typeof I +>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 I +>V3 : I + } +} diff --git a/tests/baselines/reference/decrementOperatorWithEnumType.js b/tests/baselines/reference/decrementOperatorWithEnumType.js index 13947a6a9488c..15247ea4225b6 100644 --- a/tests/baselines/reference/decrementOperatorWithEnumType.js +++ b/tests/baselines/reference/decrementOperatorWithEnumType.js @@ -22,8 +22,8 @@ var ENUM1; })(ENUM1 || (ENUM1 = {})); ; // expression -var ResultIsNumber1 = --ENUM1["A"]; +var ResultIsNumber1 = --0 /* "A" */; var ResultIsNumber2 = 0 /* A */--; // miss assignment operator ---ENUM1["A"]; +--0 /* "A" */; ENUM1[A]--; diff --git a/tests/baselines/reference/deleteOperatorWithEnumType.js b/tests/baselines/reference/deleteOperatorWithEnumType.js index e694790091c4b..b87ab8298a725 100644 --- a/tests/baselines/reference/deleteOperatorWithEnumType.js +++ b/tests/baselines/reference/deleteOperatorWithEnumType.js @@ -39,11 +39,11 @@ var ENUM1; var ResultIsBoolean1 = delete ENUM; var ResultIsBoolean2 = delete ENUM1; // enum type expressions -var ResultIsBoolean3 = delete ENUM1["A"]; -var ResultIsBoolean4 = delete (ENUM[0] + ENUM1["B"]); +var ResultIsBoolean3 = delete 0 /* "A" */; +var ResultIsBoolean4 = delete (ENUM[0] + 1 /* "B" */); // multiple delete operators var ResultIsBoolean5 = delete delete ENUM; -var ResultIsBoolean6 = delete delete delete (ENUM[0] + ENUM1["B"]); +var ResultIsBoolean6 = delete delete delete (ENUM[0] + 1 /* "B" */); // miss assignment operators delete ENUM; delete ENUM1; diff --git a/tests/baselines/reference/incrementOperatorWithEnumType.js b/tests/baselines/reference/incrementOperatorWithEnumType.js index 2baa326f3997e..590815ffec55a 100644 --- a/tests/baselines/reference/incrementOperatorWithEnumType.js +++ b/tests/baselines/reference/incrementOperatorWithEnumType.js @@ -22,8 +22,8 @@ var ENUM1; })(ENUM1 || (ENUM1 = {})); ; // expression -var ResultIsNumber1 = ++ENUM1["B"]; +var ResultIsNumber1 = ++1 /* "B" */; var ResultIsNumber2 = 1 /* B */++; // miss assignment operator -++ENUM1["B"]; +++1 /* "B" */; 1 /* B */++; diff --git a/tests/baselines/reference/logicalNotOperatorWithEnumType.js b/tests/baselines/reference/logicalNotOperatorWithEnumType.js index 69ea283051de0..89ee1a148335a 100644 --- a/tests/baselines/reference/logicalNotOperatorWithEnumType.js +++ b/tests/baselines/reference/logicalNotOperatorWithEnumType.js @@ -37,11 +37,11 @@ var ENUM1; // enum type var var ResultIsBoolean1 = !ENUM; // enum type expressions -var ResultIsBoolean2 = !ENUM["B"]; -var ResultIsBoolean3 = !(1 /* B */ + ENUM["C"]); +var ResultIsBoolean2 = !1 /* "B" */; +var ResultIsBoolean3 = !(1 /* B */ + 2 /* "C" */); // multiple ! operators var ResultIsBoolean4 = !!ENUM; -var ResultIsBoolean5 = !!!(ENUM["B"] + 2 /* C */); +var ResultIsBoolean5 = !!!(1 /* "B" */ + 2 /* C */); // miss assignment operators !ENUM; !ENUM1; diff --git a/tests/baselines/reference/negateOperatorWithEnumType.js b/tests/baselines/reference/negateOperatorWithEnumType.js index 876bed45cc2be..77458f442df9a 100644 --- a/tests/baselines/reference/negateOperatorWithEnumType.js +++ b/tests/baselines/reference/negateOperatorWithEnumType.js @@ -33,10 +33,10 @@ var ENUM1; // enum type var var ResultIsNumber1 = -ENUM; // expressions -var ResultIsNumber2 = -ENUM1["B"]; -var ResultIsNumber3 = -(1 /* B */ + ENUM1[""]); +var ResultIsNumber2 = -1 /* "B" */; +var ResultIsNumber3 = -(1 /* B */ + 2 /* "" */); // miss assignment operators -ENUM; -ENUM1; --ENUM1["B"]; +-1 /* "B" */; -ENUM, ENUM1; diff --git a/tests/baselines/reference/noImplicitAnyIndexing.js b/tests/baselines/reference/noImplicitAnyIndexing.js index ebadbd9a12197..91dbcae730c93 100644 --- a/tests/baselines/reference/noImplicitAnyIndexing.js +++ b/tests/baselines/reference/noImplicitAnyIndexing.js @@ -61,7 +61,7 @@ var strRepresentation2 = MyEmusEnum[0 /* emu */]; // Should be implicit 'any' ; property access fails, no string indexer. var strRepresentation3 = MyEmusEnum["monehh"]; // Should be okay; should be a MyEmusEnum -var strRepresentation4 = MyEmusEnum["emu"]; +var strRepresentation4 = 0 /* "emu" */; // Should report an implicit 'any'. var x = {}["hi"]; // Should report an implicit 'any'. diff --git a/tests/baselines/reference/plusOperatorWithEnumType.js b/tests/baselines/reference/plusOperatorWithEnumType.js index 4d6eaad892ecd..1ce6036d5ce15 100644 --- a/tests/baselines/reference/plusOperatorWithEnumType.js +++ b/tests/baselines/reference/plusOperatorWithEnumType.js @@ -35,8 +35,8 @@ var ENUM1; var ResultIsNumber1 = +ENUM; var ResultIsNumber2 = +ENUM1; // enum type expressions -var ResultIsNumber3 = +ENUM1["A"]; -var ResultIsNumber4 = +(ENUM[0] + ENUM1["B"]); +var ResultIsNumber3 = +0 /* "A" */; +var ResultIsNumber4 = +(ENUM[0] + 1 /* "B" */); // miss assignment operators +ENUM; +ENUM1; diff --git a/tests/baselines/reference/preserveConstEnums.js b/tests/baselines/reference/preserveConstEnums.js new file mode 100644 index 0000000000000..541c4488ec842 --- /dev/null +++ b/tests/baselines/reference/preserveConstEnums.js @@ -0,0 +1,10 @@ +//// [preserveConstEnums.ts] +const enum E { + Value = 1 +} + +//// [preserveConstEnums.js] +var E; +(function (E) { + E[E["Value"] = 1] = "Value"; +})(E || (E = {})); diff --git a/tests/baselines/reference/preserveConstEnums.types b/tests/baselines/reference/preserveConstEnums.types new file mode 100644 index 0000000000000..7e886caefe6ed --- /dev/null +++ b/tests/baselines/reference/preserveConstEnums.types @@ -0,0 +1,7 @@ +=== tests/cases/compiler/preserveConstEnums.ts === +const enum E { +>E : E + + Value = 1 +>Value : E +} diff --git a/tests/baselines/reference/typeofOperatorWithEnumType.js b/tests/baselines/reference/typeofOperatorWithEnumType.js index 272feacf63d5d..900b610cef463 100644 --- a/tests/baselines/reference/typeofOperatorWithEnumType.js +++ b/tests/baselines/reference/typeofOperatorWithEnumType.js @@ -44,15 +44,15 @@ var ENUM1; var ResultIsString1 = typeof ENUM; var ResultIsString2 = typeof ENUM1; // enum type expressions -var ResultIsString3 = typeof ENUM1["A"]; -var ResultIsString4 = typeof (ENUM[0] + ENUM1["B"]); +var ResultIsString3 = typeof 0 /* "A" */; +var ResultIsString4 = typeof (ENUM[0] + 1 /* "B" */); // multiple typeof operators var ResultIsString5 = typeof typeof ENUM; var ResultIsString6 = typeof typeof typeof (ENUM[0] + 1 /* B */); // miss assignment operators typeof ENUM; typeof ENUM1; -typeof ENUM1["B"]; +typeof 1 /* "B" */; typeof ENUM, ENUM1; // use typeof in type query var z; diff --git a/tests/baselines/reference/voidOperatorWithEnumType.js b/tests/baselines/reference/voidOperatorWithEnumType.js index 8d20842ea7b5c..478ac85651b4d 100644 --- a/tests/baselines/reference/voidOperatorWithEnumType.js +++ b/tests/baselines/reference/voidOperatorWithEnumType.js @@ -39,13 +39,13 @@ var ENUM1; var ResultIsAny1 = void ENUM; var ResultIsAny2 = void ENUM1; // enum type expressions -var ResultIsAny3 = void ENUM1["A"]; -var ResultIsAny4 = void (ENUM[0] + ENUM1["B"]); +var ResultIsAny3 = void 0 /* "A" */; +var ResultIsAny4 = void (ENUM[0] + 1 /* "B" */); // multiple void operators var ResultIsAny5 = void void ENUM; var ResultIsAny6 = void void void (ENUM[0] + 1 /* B */); // miss assignment operators void ENUM; void ENUM1; -void ENUM1["B"]; +void 1 /* "B" */; void ENUM, ENUM1; diff --git a/tests/cases/compiler/constEnumDeclarations.ts b/tests/cases/compiler/constEnumDeclarations.ts new file mode 100644 index 0000000000000..c3c11f55aa38d --- /dev/null +++ b/tests/cases/compiler/constEnumDeclarations.ts @@ -0,0 +1,13 @@ +// @declaration: true + +const enum E { + A = 1, + B = 2, + C = A | B +} + +const enum E2 { + A = 1, + B, + C +} \ No newline at end of file diff --git a/tests/cases/compiler/constEnumErrors.ts b/tests/cases/compiler/constEnumErrors.ts new file mode 100644 index 0000000000000..87e6b7ccb98fd --- /dev/null +++ b/tests/cases/compiler/constEnumErrors.ts @@ -0,0 +1,43 @@ +const enum E { + A +} + +module E { + var x = 1; +} + +const enum E1 { + // illegal case + // forward reference to the element of the same enum + X = Y, + // forward reference to the element of the same enum + Y = E1.Z, + Y1 = E1["Z"] +} + +const enum E2 { + A +} + +var y0 = E2[1] +var name = "A"; +var y1 = E2[name]; + +var x = E2; +var y = [E2]; + +function foo(t: any): void { +} + +foo(E2); + +const enum NaNOrInfinity { + A = 9007199254740992, + B = A * A, + C = B * B, + D = C * C, + E = D * D, + F = E * E, // overflow + G = 1 / 0, // overflow + H = 0 / 0 // NaN +} \ No newline at end of file diff --git a/tests/cases/compiler/constEnumExternalModule.ts b/tests/cases/compiler/constEnumExternalModule.ts new file mode 100644 index 0000000000000..d15240fe64d57 --- /dev/null +++ b/tests/cases/compiler/constEnumExternalModule.ts @@ -0,0 +1,10 @@ +//@module: amd +//@Filename: m1.ts +const enum E { + V = 100 +} + +export = E +//@Filename: m2.ts +import A = require('m1') +var v = A.V; \ No newline at end of file diff --git a/tests/cases/compiler/constEnums.ts b/tests/cases/compiler/constEnums.ts new file mode 100644 index 0000000000000..72b75a3f31ec9 --- /dev/null +++ b/tests/cases/compiler/constEnums.ts @@ -0,0 +1,151 @@ +const enum Enum1 { + A0 = 100, +} + +const 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 | 5), + 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, + W3 = Enum1["A0"], + W4 = Enum1["W"], +} + + +module A { + export module B { + export module C { + export const enum E { + V1 = 1, + V2 = A.B.C.E.V1 | 100 + } + } + } +} + +module A { + export module B { + export module C { + export const enum E { + V3 = A.B.C.E["V2"] & 200, + } + } + } +} + +module A1 { + export module B { + export module C { + export const enum E { + V1 = 10, + V2 = 110, + } + } + } +} + +module A2 { + export module B { + export module C { + export const enum E { + V1 = 10, + V2 = 110, + } + } + // module C will be classified as value + export module C { + var x = 1 + } + } +} + +import I = A.B.C.E; +import I1 = A1.B; +import I2 = A2.B; + +function foo0(e: I): void { + if (e === I.V1) { + } + else if (e === I.V2) { + } +} + +function foo1(e: I1.C.E): void { + if (e === I1.C.E.V1) { + } + else if (e === I1.C.E.V2) { + } +} + +function foo2(e: I2.C.E): void { + if (e === I2.C.E.V1) { + } + else if (e === I2.C.E.V2) { + } +} + + +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.W3: + case Enum1.W4: + 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; + } +} \ No newline at end of file diff --git a/tests/cases/compiler/preserveConstEnums.ts b/tests/cases/compiler/preserveConstEnums.ts new file mode 100644 index 0000000000000..58e07930cc129 --- /dev/null +++ b/tests/cases/compiler/preserveConstEnums.ts @@ -0,0 +1,4 @@ +// @preserveConstEnums: true +const enum E { + Value = 1 +} \ No newline at end of file