Move enum checks to the grammar checker.
This commit is contained in:
parent
e5c3661e30
commit
78af519f5f
4 changed files with 76 additions and 49 deletions
|
@ -3750,55 +3750,15 @@ module ts {
|
||||||
|
|
||||||
function parseAndCheckEnumDeclaration(pos: number, flags: NodeFlags): EnumDeclaration {
|
function parseAndCheckEnumDeclaration(pos: number, flags: NodeFlags): EnumDeclaration {
|
||||||
var enumIsConst = flags & NodeFlags.Const;
|
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 an ambient declaration, the grammar only allows integer literals as initializers.
|
||||||
// In a non-ambient declaration, the grammar allows uninitialized members only in a
|
// In a non-ambient declaration, the grammar allows uninitialized members only in a
|
||||||
// ConstantEnumMemberSection, which starts at the beginning of an enum declaration
|
// ConstantEnumMemberSection, which starts at the beginning of an enum declaration
|
||||||
// or any time an integer literal initializer is encountered.
|
// or any time an integer literal initializer is encountered.
|
||||||
function parseAndCheckEnumMember(): EnumMember {
|
function parseEnumMember(): EnumMember {
|
||||||
var node = <EnumMember>createNode(SyntaxKind.EnumMember);
|
var node = <EnumMember>createNode(SyntaxKind.EnumMember);
|
||||||
var errorCountBeforeEnumMember = file._parserDiagnostics.length;
|
|
||||||
node.name = parsePropertyName();
|
node.name = parsePropertyName();
|
||||||
node.initializer = parseInitializer(/*inParameter*/ false);
|
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);
|
return finishNode(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3811,7 +3771,7 @@ module ts {
|
||||||
node.name = parseIdentifier();
|
node.name = parseIdentifier();
|
||||||
if (parseExpected(SyntaxKind.OpenBraceToken)) {
|
if (parseExpected(SyntaxKind.OpenBraceToken)) {
|
||||||
node.members = parseDelimitedList(ParsingContext.EnumMembers,
|
node.members = parseDelimitedList(ParsingContext.EnumMembers,
|
||||||
parseAndCheckEnumMember, /*allowTrailingComma*/ true);
|
parseEnumMember, /*allowTrailingComma*/ true);
|
||||||
parseExpected(SyntaxKind.CloseBraceToken);
|
parseExpected(SyntaxKind.CloseBraceToken);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -4174,6 +4134,7 @@ module ts {
|
||||||
case SyntaxKind.Constructor: return visitConstructor(<ConstructorDeclaration>node);
|
case SyntaxKind.Constructor: return visitConstructor(<ConstructorDeclaration>node);
|
||||||
case SyntaxKind.ConstructorType: return visitConstructorType(<SignatureDeclaration>node);
|
case SyntaxKind.ConstructorType: return visitConstructorType(<SignatureDeclaration>node);
|
||||||
case SyntaxKind.ConstructSignature: return visitConstructSignature(<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.FunctionDeclaration: return visitFunctionDeclaration(<FunctionLikeDeclaration>node);
|
||||||
case SyntaxKind.FunctionExpression: return visitFunctionExpression(<FunctionExpression>node);
|
case SyntaxKind.FunctionExpression: return visitFunctionExpression(<FunctionExpression>node);
|
||||||
case SyntaxKind.FunctionType: return visitFunctionType(<SignatureDeclaration>node);
|
case SyntaxKind.FunctionType: return visitFunctionType(<SignatureDeclaration>node);
|
||||||
|
@ -4224,6 +4185,53 @@ module ts {
|
||||||
checkParameterList(node.parameters);
|
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) {
|
function visitFunctionDeclaration(node: FunctionLikeDeclaration) {
|
||||||
checkParameterList(node.parameters);
|
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(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(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(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(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.
|
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(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
|
// Ambient variable with an initializer
|
||||||
declare var x = 4;
|
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
|
// Ambient enum with non - integer literal constant member
|
||||||
declare enum E1 {
|
declare enum E1 {
|
||||||
y = 4.23
|
y = 4.23
|
||||||
~
|
|
||||||
!!! error TS1066: Ambient enum elements can only have integer literal initializers.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ambient enum with computer member
|
// Ambient enum with computer member
|
||||||
declare enum E2 {
|
declare enum E2 {
|
||||||
x = 'foo'.length
|
x = 'foo'.length
|
||||||
~
|
|
||||||
!!! error TS1066: Ambient enum elements can only have integer literal initializers.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ambient module with initializers for values, bodies for functions / classes
|
// 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