diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index d5963593b2f..72127dd0514 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -12767,28 +12767,7 @@ namespace ts { } let initializer = member.initializer; if (initializer) { - autoValue = getConstantValueForEnumMemberInitializer(initializer); - 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); - } - } - + autoValue = computeConstantValueForEnumMemberInitializer(initializer, enumType, enumIsConst, ambient); } else if (ambient && !enumIsConst) { autoValue = undefined; @@ -12802,8 +12781,36 @@ namespace ts { nodeLinks.flags |= NodeCheckFlags.EnumValuesComputed; } - function getConstantValueForEnumMemberInitializer(initializer: Expression): number { - return evalConstant(initializer); + function computeConstantValueForEnumMemberInitializer(initializer: Expression, enumType: Type, enumIsConst: boolean, ambient: boolean): number { + // Controls if error should be reported after evaluation of constant value is completed + // Can be false if another more precise error was already reported during evaluation. + let reportError = true; + let value = evalConstant(initializer); + + if (reportError) { + if (value === 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(value)) { + error(initializer, Diagnostics.const_enum_member_initializer_was_evaluated_to_disallowed_value_NaN); + } + else if (!isFinite(value)) { + error(initializer, Diagnostics.const_enum_member_initializer_was_evaluated_to_a_non_finite_value); + } + } + } + + return value; function evalConstant(e: Node): number { switch (e.kind) { @@ -12912,6 +12919,8 @@ namespace ts { // illegal case: forward reference if (!isDefinedBefore(propertyDecl, member)) { + reportError = false; + error(e, Diagnostics.A_member_initializer_in_a_const_enum_declaration_cannot_reference_members_declared_after_it_including_members_defined_in_other_const_enums); return undefined; } diff --git a/src/compiler/diagnosticInformationMap.generated.ts b/src/compiler/diagnosticInformationMap.generated.ts index 87e73e0c9c9..5ccc0a0db9b 100644 --- a/src/compiler/diagnosticInformationMap.generated.ts +++ b/src/compiler/diagnosticInformationMap.generated.ts @@ -425,7 +425,8 @@ namespace ts { JSX_element_class_does_not_support_attributes_because_it_does_not_have_a_0_property: { code: 2607, category: DiagnosticCategory.Error, key: "JSX element class does not support attributes because it does not have a '{0}' property" }, The_global_type_JSX_0_may_not_have_more_than_one_property: { code: 2608, category: DiagnosticCategory.Error, key: "The global type 'JSX.{0}' may not have more than one property" }, Cannot_emit_namespaced_JSX_elements_in_React: { code: 2650, category: DiagnosticCategory.Error, key: "Cannot emit namespaced JSX elements in React" }, - Merged_declaration_0_cannot_include_a_default_export_declaration_Consider_adding_a_separate_export_default_0_declaration_instead: { code: 2651, category: DiagnosticCategory.Error, key: "Merged declaration '{0}' cannot include a default export declaration. Consider adding a separate 'export default {0}' declaration instead." }, + A_member_initializer_in_a_const_enum_declaration_cannot_reference_members_declared_after_it_including_members_defined_in_other_const_enums: { code: 2651, category: DiagnosticCategory.Error, key: "A member initializer in a 'const' enum declaration cannot reference members declared after it, including members defined in other 'const' enums." }, + Merged_declaration_0_cannot_include_a_default_export_declaration_Consider_adding_a_separate_export_default_0_declaration_instead: { code: 2652, category: DiagnosticCategory.Error, key: "Merged declaration '{0}' cannot include a default export declaration. Consider adding a separate 'export default {0}' declaration instead." }, Import_declaration_0_is_using_private_name_1: { code: 4000, category: DiagnosticCategory.Error, key: "Import declaration '{0}' is using private name '{1}'." }, Type_parameter_0_of_exported_class_has_or_is_using_private_name_1: { code: 4002, category: DiagnosticCategory.Error, key: "Type parameter '{0}' of exported class has or is using private name '{1}'." }, Type_parameter_0_of_exported_interface_has_or_is_using_private_name_1: { code: 4004, category: DiagnosticCategory.Error, key: "Type parameter '{0}' of exported interface has or is using private name '{1}'." }, diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 903ad00add3..e138e2ddac4 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -1689,9 +1689,13 @@ "category": "Error", "code": 2650 }, - "Merged declaration '{0}' cannot include a default export declaration. Consider adding a separate 'export default {0}' declaration instead.": { + "A member initializer in a 'const' enum declaration cannot reference members declared after it, including members defined in other 'const' enums.": { "category": "Error", "code": 2651 + }, + "Merged declaration '{0}' cannot include a default export declaration. Consider adding a separate 'export default {0}' declaration instead.": { + "category": "Error", + "code": 2652 }, "Import declaration '{0}' is using private name '{1}'.": { "category": "Error", diff --git a/tests/baselines/reference/constEnumErrors.errors.txt b/tests/baselines/reference/constEnumErrors.errors.txt index be7dad54eeb..4236544defa 100644 --- a/tests/baselines/reference/constEnumErrors.errors.txt +++ b/tests/baselines/reference/constEnumErrors.errors.txt @@ -1,6 +1,6 @@ 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 TS2474: In 'const' enum declarations member initializer must be constant expression. +tests/cases/compiler/constEnumErrors.ts(12,9): error TS2651: A member initializer in a 'const' enum declaration cannot reference members declared after it, including members defined in other 'const' enums. tests/cases/compiler/constEnumErrors.ts(14,9): error TS2474: In 'const' enum declarations member initializer must be constant expression. tests/cases/compiler/constEnumErrors.ts(15,10): error TS2474: In 'const' enum declarations member initializer must be constant expression. tests/cases/compiler/constEnumErrors.ts(22,13): error TS2476: A const enum member can only be accessed using a string literal. @@ -31,7 +31,7 @@ tests/cases/compiler/constEnumErrors.ts(42,9): error TS2478: 'const' enum member // forward reference to the element of the same enum X = Y, ~ -!!! error TS2474: In 'const' enum declarations member initializer must be constant expression. +!!! error TS2651: A member initializer in a 'const' enum declaration cannot reference members declared after it, including members defined in other 'const' enums. // forward reference to the element of the same enum Y = E1.Z, ~~~~ diff --git a/tests/baselines/reference/defaultExportsCannotMerge01.errors.txt b/tests/baselines/reference/defaultExportsCannotMerge01.errors.txt index 6e3955b4ef8..be08d976eeb 100644 --- a/tests/baselines/reference/defaultExportsCannotMerge01.errors.txt +++ b/tests/baselines/reference/defaultExportsCannotMerge01.errors.txt @@ -1,5 +1,5 @@ -tests/cases/conformance/es6/modules/m1.ts(2,25): error TS2651: Merged declaration 'Decl' cannot include a default export declaration. Consider adding a separate 'export default Decl' declaration instead. -tests/cases/conformance/es6/modules/m1.ts(11,18): error TS2651: Merged declaration 'Decl' cannot include a default export declaration. Consider adding a separate 'export default Decl' declaration instead. +tests/cases/conformance/es6/modules/m1.ts(2,25): error TS2652: Merged declaration 'Decl' cannot include a default export declaration. Consider adding a separate 'export default Decl' declaration instead. +tests/cases/conformance/es6/modules/m1.ts(11,18): error TS2652: Merged declaration 'Decl' cannot include a default export declaration. Consider adding a separate 'export default Decl' declaration instead. tests/cases/conformance/es6/modules/m2.ts(5,8): error TS2304: Cannot find name 'Entity'. tests/cases/conformance/es6/modules/m2.ts(6,8): error TS2503: Cannot find namespace 'Entity'. tests/cases/conformance/es6/modules/m2.ts(8,8): error TS2339: Property 'x' does not exist on type '() => number'. @@ -10,7 +10,7 @@ tests/cases/conformance/es6/modules/m2.ts(9,8): error TS2339: Property 'y' does export default function Decl() { ~~~~ -!!! error TS2651: Merged declaration 'Decl' cannot include a default export declaration. Consider adding a separate 'export default Decl' declaration instead. +!!! error TS2652: Merged declaration 'Decl' cannot include a default export declaration. Consider adding a separate 'export default Decl' declaration instead. return 0; } @@ -21,7 +21,7 @@ tests/cases/conformance/es6/modules/m2.ts(9,8): error TS2339: Property 'y' does export namespace Decl { ~~~~ -!!! error TS2651: Merged declaration 'Decl' cannot include a default export declaration. Consider adding a separate 'export default Decl' declaration instead. +!!! error TS2652: Merged declaration 'Decl' cannot include a default export declaration. Consider adding a separate 'export default Decl' declaration instead. export var x = 10; export var y = 20; diff --git a/tests/baselines/reference/defaultExportsCannotMerge02.errors.txt b/tests/baselines/reference/defaultExportsCannotMerge02.errors.txt index ef4ecdccfc3..ad9b85c7e94 100644 --- a/tests/baselines/reference/defaultExportsCannotMerge02.errors.txt +++ b/tests/baselines/reference/defaultExportsCannotMerge02.errors.txt @@ -1,5 +1,5 @@ -tests/cases/conformance/es6/modules/m1.ts(2,22): error TS2651: Merged declaration 'Decl' cannot include a default export declaration. Consider adding a separate 'export default Decl' declaration instead. -tests/cases/conformance/es6/modules/m1.ts(5,18): error TS2651: Merged declaration 'Decl' cannot include a default export declaration. Consider adding a separate 'export default Decl' declaration instead. +tests/cases/conformance/es6/modules/m1.ts(2,22): error TS2652: Merged declaration 'Decl' cannot include a default export declaration. Consider adding a separate 'export default Decl' declaration instead. +tests/cases/conformance/es6/modules/m1.ts(5,18): error TS2652: Merged declaration 'Decl' cannot include a default export declaration. Consider adding a separate 'export default Decl' declaration instead. tests/cases/conformance/es6/modules/m2.ts(3,1): error TS2348: Value of type 'typeof Decl' is not callable. Did you mean to include 'new'? tests/cases/conformance/es6/modules/m2.ts(6,8): error TS2503: Cannot find namespace 'Entity'. tests/cases/conformance/es6/modules/m2.ts(8,13): error TS2339: Property 'p1' does not exist on type 'Decl'. @@ -10,12 +10,12 @@ tests/cases/conformance/es6/modules/m2.ts(8,20): error TS2339: Property 'p2' doe export default class Decl { ~~~~ -!!! error TS2651: Merged declaration 'Decl' cannot include a default export declaration. Consider adding a separate 'export default Decl' declaration instead. +!!! error TS2652: Merged declaration 'Decl' cannot include a default export declaration. Consider adding a separate 'export default Decl' declaration instead. } export interface Decl { ~~~~ -!!! error TS2651: Merged declaration 'Decl' cannot include a default export declaration. Consider adding a separate 'export default Decl' declaration instead. +!!! error TS2652: Merged declaration 'Decl' cannot include a default export declaration. Consider adding a separate 'export default Decl' declaration instead. p1: number; p2: number; } diff --git a/tests/baselines/reference/defaultExportsCannotMerge03.errors.txt b/tests/baselines/reference/defaultExportsCannotMerge03.errors.txt index 219b59a5633..b1d405671e3 100644 --- a/tests/baselines/reference/defaultExportsCannotMerge03.errors.txt +++ b/tests/baselines/reference/defaultExportsCannotMerge03.errors.txt @@ -1,6 +1,6 @@ -tests/cases/conformance/es6/modules/m1.ts(2,22): error TS2651: Merged declaration 'Decl' cannot include a default export declaration. Consider adding a separate 'export default Decl' declaration instead. +tests/cases/conformance/es6/modules/m1.ts(2,22): error TS2652: Merged declaration 'Decl' cannot include a default export declaration. Consider adding a separate 'export default Decl' declaration instead. tests/cases/conformance/es6/modules/m1.ts(5,11): error TS2518: Only an ambient class can be merged with an interface. -tests/cases/conformance/es6/modules/m1.ts(5,11): error TS2651: Merged declaration 'Decl' cannot include a default export declaration. Consider adding a separate 'export default Decl' declaration instead. +tests/cases/conformance/es6/modules/m1.ts(5,11): error TS2652: Merged declaration 'Decl' cannot include a default export declaration. Consider adding a separate 'export default Decl' declaration instead. tests/cases/conformance/es6/modules/m2.ts(3,1): error TS2348: Value of type 'typeof Decl' is not callable. Did you mean to include 'new'? tests/cases/conformance/es6/modules/m2.ts(6,8): error TS2503: Cannot find namespace 'Entity'. tests/cases/conformance/es6/modules/m2.ts(8,13): error TS2339: Property 'p1' does not exist on type 'Decl'. @@ -11,14 +11,14 @@ tests/cases/conformance/es6/modules/m2.ts(8,20): error TS2339: Property 'p2' doe export default class Decl { ~~~~ -!!! error TS2651: Merged declaration 'Decl' cannot include a default export declaration. Consider adding a separate 'export default Decl' declaration instead. +!!! error TS2652: Merged declaration 'Decl' cannot include a default export declaration. Consider adding a separate 'export default Decl' declaration instead. } interface Decl { ~~~~ !!! error TS2518: Only an ambient class can be merged with an interface. ~~~~ -!!! error TS2651: Merged declaration 'Decl' cannot include a default export declaration. Consider adding a separate 'export default Decl' declaration instead. +!!! error TS2652: Merged declaration 'Decl' cannot include a default export declaration. Consider adding a separate 'export default Decl' declaration instead. p1: number; p2: number; } diff --git a/tests/baselines/reference/defaultExportsCannotMerge04.errors.txt b/tests/baselines/reference/defaultExportsCannotMerge04.errors.txt index ee36491e0c7..000a9b8f30e 100644 --- a/tests/baselines/reference/defaultExportsCannotMerge04.errors.txt +++ b/tests/baselines/reference/defaultExportsCannotMerge04.errors.txt @@ -1,5 +1,5 @@ -tests/cases/conformance/es6/modules/defaultExportsCannotMerge04.ts(2,25): error TS2651: Merged declaration 'Foo' cannot include a default export declaration. Consider adding a separate 'export default Foo' declaration instead. -tests/cases/conformance/es6/modules/defaultExportsCannotMerge04.ts(5,11): error TS2651: Merged declaration 'Foo' cannot include a default export declaration. Consider adding a separate 'export default Foo' declaration instead. +tests/cases/conformance/es6/modules/defaultExportsCannotMerge04.ts(2,25): error TS2652: Merged declaration 'Foo' cannot include a default export declaration. Consider adding a separate 'export default Foo' declaration instead. +tests/cases/conformance/es6/modules/defaultExportsCannotMerge04.ts(5,11): error TS2652: Merged declaration 'Foo' cannot include a default export declaration. Consider adding a separate 'export default Foo' declaration instead. tests/cases/conformance/es6/modules/defaultExportsCannotMerge04.ts(9,11): error TS2395: Individual declarations in merged declaration 'Foo' must be all exported or all local. tests/cases/conformance/es6/modules/defaultExportsCannotMerge04.ts(12,18): error TS2395: Individual declarations in merged declaration 'Foo' must be all exported or all local. @@ -8,12 +8,12 @@ tests/cases/conformance/es6/modules/defaultExportsCannotMerge04.ts(12,18): error export default function Foo() { ~~~ -!!! error TS2651: Merged declaration 'Foo' cannot include a default export declaration. Consider adding a separate 'export default Foo' declaration instead. +!!! error TS2652: Merged declaration 'Foo' cannot include a default export declaration. Consider adding a separate 'export default Foo' declaration instead. } namespace Foo { ~~~ -!!! error TS2651: Merged declaration 'Foo' cannot include a default export declaration. Consider adding a separate 'export default Foo' declaration instead. +!!! error TS2652: Merged declaration 'Foo' cannot include a default export declaration. Consider adding a separate 'export default Foo' declaration instead. export var x; }