diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 302635aaff..6ce63e1a4d 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -3750,55 +3750,15 @@ 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 - // coercing a number to a string. Sometimes this coercion can yield a string - // in scientific notation. - // We also don't need special logic for hex because a hex integer is converted - // to decimal when it is coerced. - return /^[0-9]+([eE]\+?[0-9]+)?$/.test(literalExpression.text); - } - if (expression.kind === SyntaxKind.PrefixOperator) { - var unaryExpression = expression; - if (unaryExpression.operator === SyntaxKind.PlusToken || unaryExpression.operator === SyntaxKind.MinusToken) { - expression = unaryExpression.operand; - } - } - if (expression.kind === SyntaxKind.NumericLiteral) { - return isInteger(expression); - } - - return false; - } - - var inConstantEnumMemberSection = true; // In an ambient declaration, the grammar only allows integer literals as initializers. // In a non-ambient declaration, the grammar allows uninitialized members only in a // ConstantEnumMemberSection, which starts at the beginning of an enum declaration // or any time an integer literal initializer is encountered. - function parseAndCheckEnumMember(): EnumMember { + function parseEnumMember(): EnumMember { var node = createNode(SyntaxKind.EnumMember); - var errorCountBeforeEnumMember = file._parserDiagnostics.length; node.name = parsePropertyName(); node.initializer = parseInitializer(/*inParameter*/ false); - - // 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._parserDiagnostics.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._parserDiagnostics.length) { - grammarErrorOnNode(node.name, Diagnostics.Enum_member_must_have_initializer); - } - } return finishNode(node); } @@ -3811,7 +3771,7 @@ module ts { node.name = parseIdentifier(); if (parseExpected(SyntaxKind.OpenBraceToken)) { node.members = parseDelimitedList(ParsingContext.EnumMembers, - parseAndCheckEnumMember, /*allowTrailingComma*/ true); + parseEnumMember, /*allowTrailingComma*/ true); parseExpected(SyntaxKind.CloseBraceToken); } else { @@ -4174,6 +4134,7 @@ module ts { case SyntaxKind.Constructor: return visitConstructor(node); case SyntaxKind.ConstructorType: return visitConstructorType(node); case SyntaxKind.ConstructSignature: return visitConstructSignature(node); + case SyntaxKind.EnumDeclaration: return visitEnumDeclaration(node); case SyntaxKind.FunctionDeclaration: return visitFunctionDeclaration(node); case SyntaxKind.FunctionExpression: return visitFunctionExpression(node); case SyntaxKind.FunctionType: return visitFunctionType(node); @@ -4224,6 +4185,53 @@ module ts { checkParameterList(node.parameters); } + function visitEnumDeclaration(enumDecl: EnumDeclaration) { + var enumIsConst = (enumDecl.flags & NodeFlags.Const) !== 0; + + // 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) { + var inConstantEnumMemberSection = true; + for (var i = 0, n = enumDecl.members.length; i < n; i++) { + var node = enumDecl.members[i]; + if (inAmbientContext) { + if (node.initializer && !isIntegerLiteral(node.initializer)) { + grammarErrorOnNode(node.name, Diagnostics.Ambient_enum_elements_can_only_have_integer_literal_initializers); + } + } + else if (node.initializer) { + inConstantEnumMemberSection = isIntegerLiteral(node.initializer); + } + else if (!inConstantEnumMemberSection) { + grammarErrorOnNode(node.name, Diagnostics.Enum_member_must_have_initializer); + } + } + } + } + + function isIntegerLiteral(expression: Expression): boolean { + function isInteger(literalExpression: LiteralExpression): boolean { + // Allows for scientific notation since literalExpression.text was formed by + // coercing a number to a string. Sometimes this coercion can yield a string + // in scientific notation. + // We also don't need special logic for hex because a hex integer is converted + // to decimal when it is coerced. + return /^[0-9]+([eE]\+?[0-9]+)?$/.test(literalExpression.text); + } + + if (expression.kind === SyntaxKind.PrefixOperator) { + var unaryExpression = expression; + if (unaryExpression.operator === SyntaxKind.PlusToken || unaryExpression.operator === SyntaxKind.MinusToken) { + expression = unaryExpression.operand; + } + } + if (expression.kind === SyntaxKind.NumericLiteral) { + return isInteger(expression); + } + + return false; + } + function visitFunctionDeclaration(node: FunctionLikeDeclaration) { checkParameterList(node.parameters); } diff --git a/tests/baselines/reference/ambientEnum1.errors.txt b/tests/baselines/reference/ambientEnum1.errors.txt new file mode 100644 index 0000000000..b4bb534f5d --- /dev/null +++ b/tests/baselines/reference/ambientEnum1.errors.txt @@ -0,0 +1,17 @@ +tests/cases/compiler/ambientEnum1.ts(2,9): error TS1066: Ambient enum elements can only have integer literal initializers. +tests/cases/compiler/ambientEnum1.ts(7,9): error TS1066: Ambient enum elements can only have integer literal initializers. + + +==== tests/cases/compiler/ambientEnum1.ts (2 errors) ==== + declare enum E1 { + y = 4.23 + ~ +!!! error TS1066: Ambient enum elements can only have integer literal initializers. + } + + // Ambient enum with computer member + declare enum E2 { + x = 'foo'.length + ~ +!!! error TS1066: Ambient enum elements can only have integer literal initializers. + } \ No newline at end of file diff --git a/tests/baselines/reference/ambientErrors.errors.txt b/tests/baselines/reference/ambientErrors.errors.txt index 975dc9ad63..618829c8bd 100644 --- a/tests/baselines/reference/ambientErrors.errors.txt +++ b/tests/baselines/reference/ambientErrors.errors.txt @@ -1,7 +1,5 @@ tests/cases/conformance/ambient/ambientErrors.ts(2,15): error TS1039: Initializers are not allowed in ambient contexts. tests/cases/conformance/ambient/ambientErrors.ts(20,24): error TS1037: A function implementation cannot be declared in an ambient context. -tests/cases/conformance/ambient/ambientErrors.ts(24,5): error TS1066: Ambient enum elements can only have integer literal initializers. -tests/cases/conformance/ambient/ambientErrors.ts(29,5): error TS1066: Ambient enum elements can only have integer literal initializers. tests/cases/conformance/ambient/ambientErrors.ts(34,11): error TS1039: Initializers are not allowed in ambient contexts. tests/cases/conformance/ambient/ambientErrors.ts(35,19): error TS1037: A function implementation cannot be declared in an ambient context. tests/cases/conformance/ambient/ambientErrors.ts(37,18): error TS1039: Initializers are not allowed in ambient contexts. @@ -16,7 +14,7 @@ tests/cases/conformance/ambient/ambientErrors.ts(51,16): error TS2436: Ambient e tests/cases/conformance/ambient/ambientErrors.ts(57,5): error TS2309: An export assignment cannot be used in a module with other exported elements. -==== tests/cases/conformance/ambient/ambientErrors.ts (16 errors) ==== +==== tests/cases/conformance/ambient/ambientErrors.ts (14 errors) ==== // Ambient variable with an initializer declare var x = 4; ~ @@ -49,15 +47,11 @@ tests/cases/conformance/ambient/ambientErrors.ts(57,5): error TS2309: An export // Ambient enum with non - integer literal constant member declare enum E1 { y = 4.23 - ~ -!!! error TS1066: Ambient enum elements can only have integer literal initializers. } // Ambient enum with computer member declare enum E2 { x = 'foo'.length - ~ -!!! error TS1066: Ambient enum elements can only have integer literal initializers. } // Ambient module with initializers for values, bodies for functions / classes diff --git a/tests/cases/compiler/ambientEnum1.ts b/tests/cases/compiler/ambientEnum1.ts new file mode 100644 index 0000000000..05b0cdd3a4 --- /dev/null +++ b/tests/cases/compiler/ambientEnum1.ts @@ -0,0 +1,8 @@ + declare enum E1 { + y = 4.23 + } + + // Ambient enum with computer member + declare enum E2 { + x = 'foo'.length + } \ No newline at end of file