Move enum checks to the grammar checker.
This commit is contained in:
parent
e5c3661e30
commit
78af519f5f
|
@ -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 = <UnaryExpression>expression;
|
||||
if (unaryExpression.operator === SyntaxKind.PlusToken || unaryExpression.operator === SyntaxKind.MinusToken) {
|
||||
expression = unaryExpression.operand;
|
||||
}
|
||||
}
|
||||
if (expression.kind === SyntaxKind.NumericLiteral) {
|
||||
return isInteger(<LiteralExpression>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 = <EnumMember>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(<ConstructorDeclaration>node);
|
||||
case SyntaxKind.ConstructorType: return visitConstructorType(<SignatureDeclaration>node);
|
||||
case SyntaxKind.ConstructSignature: return visitConstructSignature(<SignatureDeclaration>node);
|
||||
case SyntaxKind.EnumDeclaration: return visitEnumDeclaration(<EnumDeclaration>node);
|
||||
case SyntaxKind.FunctionDeclaration: return visitFunctionDeclaration(<FunctionLikeDeclaration>node);
|
||||
case SyntaxKind.FunctionExpression: return visitFunctionExpression(<FunctionExpression>node);
|
||||
case SyntaxKind.FunctionType: return visitFunctionType(<SignatureDeclaration>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 = <UnaryExpression>expression;
|
||||
if (unaryExpression.operator === SyntaxKind.PlusToken || unaryExpression.operator === SyntaxKind.MinusToken) {
|
||||
expression = unaryExpression.operand;
|
||||
}
|
||||
}
|
||||
if (expression.kind === SyntaxKind.NumericLiteral) {
|
||||
return isInteger(<LiteralExpression>expression);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function visitFunctionDeclaration(node: FunctionLikeDeclaration) {
|
||||
checkParameterList(node.parameters);
|
||||
}
|
||||
|
|
17
tests/baselines/reference/ambientEnum1.errors.txt
Normal file
17
tests/baselines/reference/ambientEnum1.errors.txt
Normal file
|
@ -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.
|
||||
}
|
|
@ -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
|
||||
|
|
8
tests/cases/compiler/ambientEnum1.ts
Normal file
8
tests/cases/compiler/ambientEnum1.ts
Normal file
|
@ -0,0 +1,8 @@
|
|||
declare enum E1 {
|
||||
y = 4.23
|
||||
}
|
||||
|
||||
// Ambient enum with computer member
|
||||
declare enum E2 {
|
||||
x = 'foo'.length
|
||||
}
|
Loading…
Reference in a new issue