Parse computed property names
This commit is contained in:
parent
3e1b6b896c
commit
dbc48d222f
12
computed.txt
Normal file
12
computed.txt
Normal file
|
@ -0,0 +1,12 @@
|
|||
Parse computed expressions and add tests
|
||||
Disallow computed expressions in class instance properties
|
||||
Disallow computed expressions in object literal properties, methods, or accessors
|
||||
Disallow in interfaces
|
||||
|
||||
Emit computed properties for classes and object literals
|
||||
Discuss down level support for computed properties
|
||||
|
||||
Tests that need to move:
|
||||
tests\cases\conformance\parser\ecmascript5\IndexSignatures\parserIndexSignature4.ts
|
||||
tests\cases\conformance\parser\ecmascript5\IndexSignatures\parserIndexSignature5.ts
|
||||
tests\cases\conformance\parser\ecmascript5\IndexSignatures\parserIndexSignature11.ts
|
|
@ -1242,30 +1242,41 @@ module ts {
|
|||
return createIdentifier(token >= SyntaxKind.Identifier);
|
||||
}
|
||||
|
||||
function isPropertyName(): boolean {
|
||||
function isLiteralPropertyName(): boolean {
|
||||
return token >= SyntaxKind.Identifier ||
|
||||
token === SyntaxKind.StringLiteral ||
|
||||
token === SyntaxKind.NumericLiteral;
|
||||
}
|
||||
|
||||
function parsePropertyName(): Identifier {
|
||||
function parsePropertyName(): DeclarationName {
|
||||
if (token === SyntaxKind.StringLiteral || token === SyntaxKind.NumericLiteral) {
|
||||
return parseLiteralNode(/*internName:*/ true);
|
||||
}
|
||||
if (token === SyntaxKind.OpenBracketToken) {
|
||||
return parseComputedPropertyName();
|
||||
}
|
||||
return parseIdentifierName();
|
||||
}
|
||||
|
||||
function parseComputedPropertyName(): ComputedPropertyName {
|
||||
var node = <ComputedPropertyName>createNode(SyntaxKind.ComputedPropertyName);
|
||||
parseExpected(SyntaxKind.OpenBracketToken);
|
||||
node.expression = parseAssignmentExpression(/*noIn*/ false);
|
||||
parseExpected(SyntaxKind.CloseBracketToken);
|
||||
return finishNode(node);
|
||||
}
|
||||
|
||||
function parseContextualModifier(t: SyntaxKind): boolean {
|
||||
return token === t && tryParse(() => {
|
||||
nextToken();
|
||||
return token === SyntaxKind.OpenBracketToken || isPropertyName();
|
||||
return token === SyntaxKind.OpenBracketToken || isLiteralPropertyName();
|
||||
});
|
||||
}
|
||||
|
||||
function parseAnyContextualModifier(): boolean {
|
||||
return isModifier(token) && tryParse(() => {
|
||||
nextToken();
|
||||
return token === SyntaxKind.OpenBracketToken || token === SyntaxKind.AsteriskToken || isPropertyName();
|
||||
return token === SyntaxKind.OpenBracketToken || isPropertyName();
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -1285,9 +1296,11 @@ module ts {
|
|||
case ParsingContext.ClassMembers:
|
||||
return lookAhead(isClassMemberStart);
|
||||
case ParsingContext.EnumMembers:
|
||||
return isPropertyName();
|
||||
// Include open bracket computed properties. This technically also lets in indexers,
|
||||
// which would be a candidate for improved error reporting.
|
||||
return token === SyntaxKind.OpenBracketToken || isLiteralPropertyName();
|
||||
case ParsingContext.ObjectLiteralMembers:
|
||||
return token === SyntaxKind.AsteriskToken || isPropertyName();
|
||||
return token === SyntaxKind.OpenBracketToken || token === SyntaxKind.AsteriskToken || isLiteralPropertyName();
|
||||
case ParsingContext.BaseTypeReferences:
|
||||
return isIdentifier() && ((token !== SyntaxKind.ExtendsKeyword && token !== SyntaxKind.ImplementsKeyword) || !lookAhead(() => (nextToken(), isIdentifier())));
|
||||
case ParsingContext.VariableDeclarations:
|
||||
|
@ -1774,6 +1787,42 @@ module ts {
|
|||
return finishNode(node);
|
||||
}
|
||||
|
||||
function isIndexSignature(): boolean {
|
||||
if (token !== SyntaxKind.OpenBracketToken) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return lookAhead(() => {
|
||||
// The only allowed sequence is:
|
||||
//
|
||||
// [id:
|
||||
//
|
||||
// However, for error recovery, we also check the following cases:
|
||||
//
|
||||
// [...
|
||||
// [id,
|
||||
// [public id
|
||||
// [private id
|
||||
// [protected id
|
||||
// []
|
||||
//
|
||||
if (nextToken() === SyntaxKind.DotDotDotToken
|
||||
|| token === SyntaxKind.CloseBracketToken
|
||||
|| token === SyntaxKind.PublicKeyword
|
||||
|| token === SyntaxKind.PrivateKeyword
|
||||
|| token === SyntaxKind.ProtectedKeyword) {
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!isIdentifier()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return nextToken() === SyntaxKind.ColonToken || token === SyntaxKind.CommaToken;
|
||||
});
|
||||
}
|
||||
|
||||
function parseIndexSignatureMember(fullStart: number, modifiers: ModifiersArray): SignatureDeclaration {
|
||||
var node = <SignatureDeclaration>createNode(SyntaxKind.IndexSignature, fullStart);
|
||||
setModifiers(node, modifiers);
|
||||
|
@ -1817,10 +1866,10 @@ module ts {
|
|||
switch (token) {
|
||||
case SyntaxKind.OpenParenToken:
|
||||
case SyntaxKind.LessThanToken:
|
||||
case SyntaxKind.OpenBracketToken:
|
||||
case SyntaxKind.OpenBracketToken: // Both for indexers and computed properties
|
||||
return true;
|
||||
default:
|
||||
return isPropertyName() && lookAhead(() => nextToken() === SyntaxKind.OpenParenToken || token === SyntaxKind.LessThanToken || token === SyntaxKind.QuestionToken ||
|
||||
return isLiteralPropertyName() && lookAhead(() => nextToken() === SyntaxKind.OpenParenToken || token === SyntaxKind.LessThanToken || token === SyntaxKind.QuestionToken ||
|
||||
token === SyntaxKind.ColonToken || canParseSemicolon());
|
||||
}
|
||||
}
|
||||
|
@ -1831,7 +1880,8 @@ module ts {
|
|||
case SyntaxKind.LessThanToken:
|
||||
return parseSignatureMember(SyntaxKind.CallSignature, SyntaxKind.ColonToken);
|
||||
case SyntaxKind.OpenBracketToken:
|
||||
return parseIndexSignatureMember(scanner.getStartPos(), /*modifiers:*/ undefined);
|
||||
// Indexer or computed property
|
||||
return isIndexSignature() ? parseIndexSignatureMember(scanner.getStartPos(), /*modifiers:*/ undefined) : parsePropertyOrMethod();
|
||||
case SyntaxKind.NewKeyword:
|
||||
if (lookAhead(() => nextToken() === SyntaxKind.OpenParenToken || token === SyntaxKind.LessThanToken)) {
|
||||
return parseSignatureMember(SyntaxKind.ConstructSignature, SyntaxKind.ColonToken);
|
||||
|
@ -3368,12 +3418,12 @@ module ts {
|
|||
|
||||
// Try to get the first property-like token following all modifiers.
|
||||
// This can either be an identifier or the 'get' or 'set' keywords.
|
||||
if (isPropertyName()) {
|
||||
if (isLiteralPropertyName()) {
|
||||
idToken = token;
|
||||
nextToken();
|
||||
}
|
||||
|
||||
// Index signatures are class members; we can parse.
|
||||
// Index signatures and computed properties are class members; we can parse.
|
||||
if (token === SyntaxKind.OpenBracketToken) {
|
||||
return true;
|
||||
}
|
||||
|
@ -3442,12 +3492,15 @@ module ts {
|
|||
if (token === SyntaxKind.ConstructorKeyword) {
|
||||
return parseConstructorDeclaration(fullStart, modifiers);
|
||||
}
|
||||
if (token >= SyntaxKind.Identifier || token === SyntaxKind.StringLiteral || token === SyntaxKind.NumericLiteral || token === SyntaxKind.AsteriskToken) {
|
||||
return parsePropertyMemberDeclaration(fullStart, modifiers);
|
||||
}
|
||||
if (token === SyntaxKind.OpenBracketToken) {
|
||||
if (isIndexSignature()) {
|
||||
return parseIndexSignatureMember(fullStart, modifiers);
|
||||
}
|
||||
// It is very important that we check this *after* checking indexers because
|
||||
// the [ token can start an index signature or a computed property name
|
||||
if (token >= SyntaxKind.Identifier || token === SyntaxKind.StringLiteral || token === SyntaxKind.NumericLiteral ||
|
||||
token === SyntaxKind.AsteriskToken || token === SyntaxKind.OpenBracketToken) {
|
||||
return parsePropertyMemberDeclaration(fullStart, modifiers);
|
||||
}
|
||||
|
||||
// 'isClassMemberStart' should have hinted not to attempt parsing.
|
||||
Debug.fail("Should not have attempted to parse class member declaration.");
|
||||
|
@ -4438,8 +4491,7 @@ module ts {
|
|||
|
||||
for (var i = 0, n = node.properties.length; i < n; i++) {
|
||||
var prop = node.properties[i];
|
||||
// TODO(jfreeman): continue if we have a computed property
|
||||
if (prop.kind === SyntaxKind.OmittedExpression) {
|
||||
if (prop.kind === SyntaxKind.OmittedExpression || p.name.kind === SyntaxKind.ComputedPropertyName) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
|
@ -141,6 +141,7 @@ module ts {
|
|||
Missing,
|
||||
// Names
|
||||
QualifiedName,
|
||||
ComputedPropertyName,
|
||||
// Signature elements
|
||||
TypeParameter,
|
||||
Parameter,
|
||||
|
@ -632,7 +633,9 @@ module ts {
|
|||
}
|
||||
|
||||
export interface EnumMember extends Declaration {
|
||||
name: Identifier | LiteralExpression;
|
||||
// This does include ComputedPropertyName, but the parser will give an error
|
||||
// if it parses a ComputedPropertyName in an EnumMember
|
||||
name: DeclarationName;
|
||||
initializer?: Expression;
|
||||
}
|
||||
|
||||
|
@ -1255,7 +1258,7 @@ module ts {
|
|||
export interface CommandLineOption {
|
||||
name: string;
|
||||
type: string | Map<number>; // "string", "number", "boolean", or an object literal mapping named values to actual values
|
||||
shortName?: string; // A short pneumonic for convenience - for instance, 'h' can be used in place of 'help'.
|
||||
shortName?: string; // A short mnemonic for convenience - for instance, 'h' can be used in place of 'help'.
|
||||
description?: DiagnosticMessage; // The message describing what the command line switch does
|
||||
paramName?: DiagnosticMessage; // The name to be used for a non-boolean option's parameter.
|
||||
error?: DiagnosticMessage; // The error given when the argument does not fit a customized 'type'.
|
||||
|
|
Loading…
Reference in a new issue