From af00c718ccc553ca059d35b05d1456589b75cb5b Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Mon, 10 Nov 2014 13:05:47 -0800 Subject: [PATCH] End-to-end support for destructuring in variable declarations --- src/compiler/binder.ts | 15 ++- src/compiler/checker.ts | 221 +++++++++++++++++++++++++++------------- src/compiler/emitter.ts | 29 +++++- src/compiler/parser.ts | 103 ++++++++++--------- src/compiler/types.ts | 18 ++-- 5 files changed, 245 insertions(+), 141 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index e06e2cf62aa..8cdba4c9058 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -340,17 +340,14 @@ module ts { bindDeclaration(node, SymbolFlags.FunctionScopedVariable, SymbolFlags.ParameterExcludes, /*isBlockScopeContainer*/ false); break; case SyntaxKind.VariableDeclaration: - case SyntaxKind.PatternDeclaration: - if ((node).name) { - if (node.flags & NodeFlags.BlockScoped) { - bindBlockScopedVariableDeclaration(node); - } - else { - bindDeclaration(node, SymbolFlags.FunctionScopedVariable, SymbolFlags.FunctionScopedVariableExcludes, /*isBlockScopeContainer*/ false); - } + if (isBindingPattern((node).name)) { + bindChildren(node, 0, /*isBlockScopeContainer*/ false); + } + else if (node.flags & NodeFlags.BlockScoped) { + bindBlockScopedVariableDeclaration(node); } else { - bindChildren(node, 0, /*isBlockScopeContainer*/ false); + bindDeclaration(node, SymbolFlags.FunctionScopedVariable, SymbolFlags.FunctionScopedVariableExcludes, /*isBlockScopeContainer*/ false); } break; case SyntaxKind.Property: diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 62d25eb227c..5061c2f0262 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -1628,11 +1628,48 @@ module ts { return classType.typeParameters ? createTypeReference(classType, map(classType.typeParameters, _ => anyType)) : classType; } - function getTypeOfVariableOrPropertyDeclaration(declaration: VariableDeclaration | PropertyDeclaration): Type { + function getTypeOfPropertyOfType(type: Type, name: string): Type { + var prop = getPropertyOfType(type, name); + return prop ? getTypeOfSymbol(prop) : undefined; + } + + function getTypeForBindingElement(declaration: BindingElement): Type { + var pattern = declaration.parent; + var parentType = getTypeForVariableDeclaration(pattern.parent); + if (parentType === unknownType) { + return unknownType; + } + if (!parentType || parentType === anyType) { + if (declaration.initializer) { + return checkExpressionCached(declaration.initializer); + } + return parentType; + } + if (pattern.kind === SyntaxKind.ObjectBindingPattern) { + var name = (declaration.propertyName || declaration.name).text; + var type = getTypeOfPropertyOfType(parentType, name) || + isNumericName(name) && getIndexTypeOfType(parentType, IndexKind.Number) || + getIndexTypeOfType(parentType, IndexKind.String); + } + else { + var index = indexOf(pattern.elements, declaration); + var type = getTypeOfPropertyOfType(parentType, "" + index) || getIndexTypeOfType(parentType, IndexKind.Number); + } + if (!type) { + // Error: Type {0} has no property {1} + return unknownType; + } + return type; + } + + function getTypeForVariableDeclaration(declaration: VariableDeclaration | PropertyDeclaration): Type { // A variable declared in a for..in statement is always of type any if (declaration.parent.kind === SyntaxKind.ForInStatement) { return anyType; } + if (isBindingPattern(declaration.parent)) { + return getTypeForBindingElement(declaration); + } // Use type from type annotation if one is present if (declaration.type) { return getTypeFromTypeNode(declaration.type); @@ -1654,7 +1691,26 @@ module ts { } // Use the type of the initializer expression if one is present if (declaration.initializer) { - var type = checkAndMarkExpression(declaration.initializer); + return checkExpressionCached(declaration.initializer); + } + // No type specified and nothing can be inferred + return undefined; + } + + function getWidenedTypeForVariableDeclaration(declaration: VariableDeclaration | PropertyDeclaration): Type { + var type = getTypeForVariableDeclaration(declaration); + if (type) { + return getWidenedType(type); + } + if (declaration.flags & NodeFlags.Rest) { + return createArrayType(anyType); + } + return anyType; + } + + function getTypeOfVariableOrPropertyDeclaration(declaration: VariableDeclaration | PropertyDeclaration): Type { + var type = getTypeForVariableDeclaration(declaration); + if (type) { if (declaration.kind !== SyntaxKind.PropertyAssignment) { reportErrorsFromWidening(declaration, type); type = getWidenedType(type); @@ -1670,6 +1726,48 @@ module ts { return type; } + //function getTypeOfVariableOrPropertyDeclaration(declaration: VariableDeclaration | PropertyDeclaration): Type { + // // A variable declared in a for..in statement is always of type any + // if (declaration.parent.kind === SyntaxKind.ForInStatement) { + // return anyType; + // } + // // Use type from type annotation if one is present + // if (declaration.type) { + // return getTypeFromTypeNode(declaration.type); + // } + // if (declaration.kind === SyntaxKind.Parameter) { + // var func = declaration.parent; + // // For a parameter of a set accessor, use the type of the get accessor if one is present + // if (func.kind === SyntaxKind.SetAccessor) { + // var getter = getDeclarationOfKind(declaration.parent.symbol, SyntaxKind.GetAccessor); + // if (getter) { + // return getReturnTypeOfSignature(getSignatureFromDeclaration(getter)); + // } + // } + // // Use contextual parameter type if one is available + // var type = getContextuallyTypedParameterType(declaration); + // if (type) { + // return type; + // } + // } + // // Use the type of the initializer expression if one is present + // if (declaration.initializer) { + // var type = checkAndMarkExpression(declaration.initializer); + // if (declaration.kind !== SyntaxKind.PropertyAssignment) { + // reportErrorsFromWidening(declaration, type); + // type = getWidenedType(type); + // } + // return type; + // } + // // Rest parameters default to type any[], other parameters default to type any + // var type = declaration.flags & NodeFlags.Rest ? createArrayType(anyType) : anyType; + // // Report implicit any errors unless this is a private property within an ambient declaration + // if (compilerOptions.noImplicitAny && !isPrivateWithinAmbient(declaration) && !(declaration.kind === SyntaxKind.Parameter && isPrivateWithinAmbient(declaration.parent))) { + // reportImplicitAnyError(declaration, type); + // } + // return type; + //} + function getTypeOfVariableOrParameterOrProperty(symbol: Symbol): Type { var links = getSymbolLinks(symbol); if (!links.type) { @@ -4245,6 +4343,10 @@ module ts { return type; } + function hasInitializer(node: VariableDeclaration): boolean { + return !!(node.initializer || isBindingPattern(node.parent) && hasInitializer(node.parent.parent)); + } + // Check if a given variable is assigned within a given syntax node function isVariableAssignedWithin(symbol: Symbol, node: Node): boolean { var links = getNodeLinks(node); @@ -4273,7 +4375,7 @@ module ts { } function isAssignedInVariableDeclaration(node: VariableDeclaration) { - if (getSymbolOfNode(node) === symbol && node.initializer) { + if (!isBindingPattern(node.name) && getSymbolOfNode(node) === symbol && hasInitializer(node)) { return true; } return forEachChild(node, isAssignedIn); @@ -4284,7 +4386,6 @@ module ts { case SyntaxKind.BinaryExpression: return isAssignedInBinaryExpression(node); case SyntaxKind.VariableDeclaration: - case SyntaxKind.PatternDeclaration: return isAssignedInVariableDeclaration(node); case SyntaxKind.ObjectBindingPattern: case SyntaxKind.ArrayBindingPattern: @@ -5743,7 +5844,7 @@ module ts { function getReturnTypeFromBody(func: FunctionLikeDeclaration, contextualMapper?: TypeMapper): Type { var contextualSignature = getContextualSignature(func); if (func.body.kind !== SyntaxKind.FunctionBlock) { - var type = checkAndMarkExpression(func.body, contextualMapper); + var type = checkExpressionCached(func.body, contextualMapper); } else { // Aggregate the types of expressions within all the return statements. @@ -5772,7 +5873,7 @@ module ts { forEachReturnStatement(body, returnStatement => { var expr = returnStatement.expression; if (expr) { - var type = checkAndMarkExpression(expr, contextualMapper); + var type = checkExpressionCached(expr, contextualMapper); if (!contains(aggregatedTypes, type)) { aggregatedTypes.push(type); } @@ -6225,10 +6326,12 @@ module ts { return result; } - function checkAndMarkExpression(node: Expression, contextualMapper?: TypeMapper): Type { - var result = checkExpression(node, contextualMapper); - getNodeLinks(node).flags |= NodeCheckFlags.TypeChecked; - return result; + function checkExpressionCached(node: Expression, contextualMapper?: TypeMapper): Type { + var links = getNodeLinks(node); + if (!links.resolvedType) { + links.resolvedType = checkExpression(node, contextualMapper); + } + return links.resolvedType; } // Checks an expression and returns its type. The contextualMapper parameter serves two purposes: When @@ -7221,53 +7324,40 @@ module ts { } } - function isBindingPattern(name: DeclarationName) { - return name.kind === SyntaxKind.ArrayBindingPattern || name.kind === SyntaxKind.ObjectBindingPattern; - } - - // TODO(andersh): Support destructuring function checkVariableDeclaration(node: VariableDeclaration | PropertyDeclaration) { if (isBindingPattern(node.name)) { + forEach((node.name).elements, e => { + if (e.kind === SyntaxKind.VariableDeclaration) { + checkVariableDeclaration(e); + } + }); + if (node.initializer) { + checkTypeAssignableTo(checkExpressionCached(node.initializer), getWidenedTypeForVariableDeclaration(node), node, /*headMessage*/ undefined); + } return; } checkSourceElement(node.type); - checkExportsOnMergedDeclarations(node); - - if (fullTypeCheck) { - var symbol = getSymbolOfNode(node); - - var typeOfValueDeclaration = getTypeOfVariableOrParameterOrProperty(symbol); - var type: Type; - var useTypeFromValueDeclaration = node === symbol.valueDeclaration; - if (useTypeFromValueDeclaration) { - type = typeOfValueDeclaration; - } - else { - type = getTypeOfVariableOrPropertyDeclaration(node); - } - - + var symbol = getSymbolOfNode(node); + var type = getTypeOfVariableOrParameterOrProperty(symbol); + if (node === symbol.valueDeclaration) { if (node.initializer) { - if (!(getNodeLinks(node.initializer).flags & NodeCheckFlags.TypeChecked)) { - // Use default messages - checkTypeAssignableTo(checkAndMarkExpression(node.initializer), type, node, /*headMessage*/ undefined); - } - //TODO(jfreeman): Check that it is not a computed property - checkCollisionWithConstDeclarations(node); - } - - checkCollisionWithCapturedSuperVariable(node, node.name); - checkCollisionWithCapturedThisVariable(node, node.name); - checkCollisionWithRequireExportsInGeneratedCode(node, node.name); - if (!useTypeFromValueDeclaration) { - // TypeScript 1.0 spec (April 2014): 5.1 - // Multiple declarations for the same variable name in the same declaration space are permitted, - // provided that each declaration associates the same type with the variable. - if (typeOfValueDeclaration !== unknownType && type !== unknownType && !isTypeIdenticalTo(typeOfValueDeclaration, type)) { - error(node.name, Diagnostics.Subsequent_variable_declarations_must_have_the_same_type_Variable_0_must_be_of_type_1_but_here_has_type_2, declarationNameToString(node.name), typeToString(typeOfValueDeclaration), typeToString(type)); - } + checkTypeAssignableTo(checkExpressionCached(node.initializer), type, node, /*headMessage*/ undefined); } } + else { + var declarationType = getWidenedTypeForVariableDeclaration(node); + if (type !== unknownType && declarationType !== unknownType && !isTypeIdenticalTo(type, declarationType)) { + error(node.name, Diagnostics.Subsequent_variable_declarations_must_have_the_same_type_Variable_0_must_be_of_type_1_but_here_has_type_2, declarationNameToString(node.name), typeToString(type), typeToString(declarationType)); + } + if (node.initializer) { + checkTypeAssignableTo(checkExpressionCached(node.initializer), declarationType, node, /*headMessage*/ undefined); + } + } + checkExportsOnMergedDeclarations(node); + checkCollisionWithConstDeclarations(node); + checkCollisionWithCapturedSuperVariable(node, node.name); + checkCollisionWithCapturedThisVariable(node, node.name); + checkCollisionWithRequireExportsInGeneratedCode(node, node.name); } function checkVariableStatement(node: VariableStatement) { @@ -7345,34 +7435,28 @@ module ts { // TODO: Check that target label is valid } + function isGetAccessorWithAnnotatatedSetAccessor(node: FunctionLikeDeclaration) { + return !!(node.kind === SyntaxKind.GetAccessor && getSetAccessorTypeAnnotationNode(getDeclarationOfKind(node.symbol, SyntaxKind.SetAccessor))); + } + function checkReturnStatement(node: ReturnStatement) { - if (node.expression && !(getNodeLinks(node.expression).flags & NodeCheckFlags.TypeChecked)) { + if (node.expression) { var func = getContainingFunction(node); if (func) { + var returnType = getReturnTypeOfSignature(getSignatureFromDeclaration(func)); + var exprType = checkExpressionCached(node.expression); if (func.kind === SyntaxKind.SetAccessor) { - if (node.expression) { - error(node.expression, Diagnostics.Setters_cannot_return_a_value); - } + error(node.expression, Diagnostics.Setters_cannot_return_a_value); } else { - var returnType = getReturnTypeOfSignature(getSignatureFromDeclaration(func)); - // do assignability check only if we short circuited in determining return type - // - function has explicit type annotation - // - function is getter with no type annotation and setter parameter type is used - // - function is a constructor (will be special cased below) - var checkAssignability = - func.type || - (func.kind === SyntaxKind.GetAccessor && getSetAccessorTypeAnnotationNode(getDeclarationOfKind(func.symbol, SyntaxKind.SetAccessor))); - if (checkAssignability) { - checkTypeAssignableTo(checkExpression(node.expression), returnType, node.expression, /*headMessage*/ undefined); - } - else if (func.kind == SyntaxKind.Constructor) { - // constructor doesn't have explicit return type annotation and yet its return type is known - declaring type - // handle constructors and issue specialized error message for them. - if (!isTypeAssignableTo(checkExpression(node.expression), returnType)) { + if (func.kind === SyntaxKind.Constructor) { + if (!isTypeAssignableTo(exprType, returnType)) { error(node.expression, Diagnostics.Return_type_of_constructor_signature_must_be_assignable_to_the_instance_type_of_the_class); } } + else if (func.type || isGetAccessorWithAnnotatatedSetAccessor(func)) { + checkTypeAssignableTo(exprType, returnType, node.expression, /*headMessage*/ undefined); + } } } } @@ -8201,7 +8285,6 @@ module ts { case SyntaxKind.Property: case SyntaxKind.ObjectBindingPattern: case SyntaxKind.ArrayBindingPattern: - case SyntaxKind.PatternDeclaration: case SyntaxKind.ArrayLiteral: case SyntaxKind.ObjectLiteral: case SyntaxKind.PropertyAssignment: diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index f61d4403eec..122a5a8386e 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -990,6 +990,18 @@ module ts { } } + function emitObjectBindingPattern(node: BindingPattern) { + write("{ "); + emitCommaList(node.elements, /*includeTrailingComma*/ true); + write(" }"); + } + + function emitArrayBindingPattern(node: BindingPattern) { + write("["); + emitCommaList(node.elements, /*includeTrailingComma*/ true); + write("]"); + } + function emitArrayLiteral(node: ArrayLiteral) { if (node.flags & NodeFlags.MultiLine) { write("["); @@ -1437,7 +1449,16 @@ module ts { function emitVariableDeclaration(node: VariableDeclaration) { emitLeadingComments(node); - emitModuleMemberName(node); + if (node.propertyName) { + emit(node.propertyName); + write(": "); + } + if (node.name.kind === SyntaxKind.Identifier) { + emitModuleMemberName(node); + } + else { + emit(node.name); + } emitOptional(" = ", node.initializer); emitTrailingComments(node); } @@ -2207,11 +2228,9 @@ module ts { if (!node) { return; } - if (node.flags & NodeFlags.Ambient) { return emitPinnedOrTripleSlashComments(node); } - switch (node.kind) { case SyntaxKind.Identifier: return emitIdentifier(node); @@ -2244,6 +2263,10 @@ module ts { return emitTemplateSpan(node); case SyntaxKind.QualifiedName: return emitPropertyAccess(node); + case SyntaxKind.ObjectBindingPattern: + return emitObjectBindingPattern(node); + case SyntaxKind.ArrayBindingPattern: + return emitArrayBindingPattern(node); case SyntaxKind.ArrayLiteral: return emitArrayLiteral(node); case SyntaxKind.ObjectLiteral: diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 1ce3b193ea4..69199b2b75b 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -236,11 +236,7 @@ module ts { return child((node).type); case SyntaxKind.ObjectBindingPattern: case SyntaxKind.ArrayBindingPattern: - return children((node).declarations); - case SyntaxKind.PatternDeclaration: - return child((node).propertyName) || - child((node).name) || - child((node).initializer); + return children((node).elements); case SyntaxKind.ArrayLiteral: return children((node).elements); case SyntaxKind.ObjectLiteral: @@ -334,7 +330,8 @@ module ts { return child((node).variable) || children((node).statements); case SyntaxKind.VariableDeclaration: - return child((node).name) || + return child((node).propertyName) || + child((node).name) || child((node).type) || child((node).initializer); case SyntaxKind.ClassDeclaration: @@ -421,11 +418,11 @@ module ts { return false; } - export function getContainingFunction(node: Node): SignatureDeclaration { + export function getContainingFunction(node: Node): FunctionLikeDeclaration { while (true) { node = node.parent; if (!node || isAnyFunction(node)) { - return node; + return node; } } } @@ -518,7 +515,6 @@ module ts { case SyntaxKind.Property: case SyntaxKind.EnumMember: case SyntaxKind.PropertyAssignment: - case SyntaxKind.PatternDeclaration: return (parent).initializer === node; case SyntaxKind.ExpressionStatement: case SyntaxKind.IfStatement: @@ -565,6 +561,10 @@ module ts { return SyntaxKind.FirstTemplateToken <= kind && kind <= SyntaxKind.LastTemplateToken; } + export function isBindingPattern(node: Node) { + return node.kind === SyntaxKind.ArrayBindingPattern || node.kind === SyntaxKind.ObjectBindingPattern; + } + export function isInAmbientContext(node: Node): boolean { while (node) { if (node.flags & (NodeFlags.Ambient | NodeFlags.DeclarationFile)) return true; @@ -573,7 +573,6 @@ module ts { return false; } - export function isDeclaration(node: Node): boolean { switch (node.kind) { case SyntaxKind.TypeParameter: @@ -676,26 +675,26 @@ module ts { } enum ParsingContext { - SourceElements, // Elements in source file - ModuleElements, // Elements in module declaration - BlockStatements, // Statements in block - SwitchClauses, // Clauses in switch statement - SwitchClauseStatements, // Statements in switch clause - TypeMembers, // Members in interface or type literal - ClassMembers, // Members in class declaration - EnumMembers, // Members in enum declaration - BaseTypeReferences, // Type references in extends or implements clause - VariableDeclarations, // Variable declarations in variable statement - ObjectBindingDeclarations, // Binding elements in object binding list - ArrayBindingDeclarations, // Binding elements in array binding list - ArgumentExpressions, // Expressions in argument list - ObjectLiteralMembers, // Members in object literal - ArrayLiteralMembers, // Members in array literal - Parameters, // Parameters in parameter list - TypeParameters, // Type parameters in type parameter list - TypeArguments, // Type arguments in type argument list - TupleElementTypes, // Element types in tuple element type list - Count // Number of parsing contexts + SourceElements, // Elements in source file + ModuleElements, // Elements in module declaration + BlockStatements, // Statements in block + SwitchClauses, // Clauses in switch statement + SwitchClauseStatements, // Statements in switch clause + TypeMembers, // Members in interface or type literal + ClassMembers, // Members in class declaration + EnumMembers, // Members in enum declaration + BaseTypeReferences, // Type references in extends or implements clause + VariableDeclarations, // Variable declarations in variable statement + ObjectBindingElements, // Binding elements in object binding list + ArrayBindingElements, // Binding elements in array binding list + ArgumentExpressions, // Expressions in argument list + ObjectLiteralMembers, // Members in object literal + ArrayLiteralMembers, // Members in array literal + Parameters, // Parameters in parameter list + TypeParameters, // Type parameters in type parameter list + TypeArguments, // Type arguments in type argument list + TupleElementTypes, // Element types in tuple element type list + Count // Number of parsing contexts } const enum Tristate { @@ -716,8 +715,8 @@ module ts { case ParsingContext.EnumMembers: return Diagnostics.Enum_member_expected; case ParsingContext.BaseTypeReferences: return Diagnostics.Type_reference_expected; case ParsingContext.VariableDeclarations: return Diagnostics.Variable_declaration_expected; - case ParsingContext.ObjectBindingDeclarations: return Diagnostics.Property_destructuring_pattern_expected; - case ParsingContext.ArrayBindingDeclarations: return Diagnostics.Array_element_destructuring_pattern_expected; + case ParsingContext.ObjectBindingElements: return Diagnostics.Property_destructuring_pattern_expected; + case ParsingContext.ArrayBindingElements: return Diagnostics.Array_element_destructuring_pattern_expected; case ParsingContext.ArgumentExpressions: return Diagnostics.Argument_expression_expected; case ParsingContext.ObjectLiteralMembers: return Diagnostics.Property_assignment_expected; case ParsingContext.ArrayLiteralMembers: return Diagnostics.Expression_or_comma_expected; @@ -1191,9 +1190,10 @@ module ts { case ParsingContext.BaseTypeReferences: return isIdentifier() && ((token !== SyntaxKind.ExtendsKeyword && token !== SyntaxKind.ImplementsKeyword) || !lookAhead(() => (nextToken(), isIdentifier()))); case ParsingContext.VariableDeclarations: - case ParsingContext.ObjectBindingDeclarations: - case ParsingContext.ArrayBindingDeclarations: + case ParsingContext.ObjectBindingElements: return isIdentifierOrPattern(); + case ParsingContext.ArrayBindingElements: + return token === SyntaxKind.CommaToken || isIdentifierOrPattern(); case ParsingContext.TypeParameters: return isIdentifier(); case ParsingContext.ArgumentExpressions: @@ -1225,7 +1225,7 @@ module ts { case ParsingContext.ClassMembers: case ParsingContext.EnumMembers: case ParsingContext.ObjectLiteralMembers: - case ParsingContext.ObjectBindingDeclarations: + case ParsingContext.ObjectBindingElements: return token === SyntaxKind.CloseBraceToken; case ParsingContext.SwitchClauseStatements: return token === SyntaxKind.CloseBraceToken || token === SyntaxKind.CaseKeyword || token === SyntaxKind.DefaultKeyword; @@ -1241,7 +1241,7 @@ module ts { return token === SyntaxKind.CloseParenToken || token === SyntaxKind.SemicolonToken; case ParsingContext.ArrayLiteralMembers: case ParsingContext.TupleElementTypes: - case ParsingContext.ArrayBindingDeclarations: + case ParsingContext.ArrayBindingElements: return token === SyntaxKind.CloseBracketToken; case ParsingContext.Parameters: // Tokens other than ')' and ']' (the latter for index signatures) are here for better error recovery @@ -3389,10 +3389,13 @@ module ts { // DECLARATIONS - function parseBindingDeclaration(flags: NodeFlags, context: ParsingContext): PatternDeclaration { - var node = createNode(SyntaxKind.PatternDeclaration); + function parseBindingElement(kind: SyntaxKind, flags: NodeFlags, context: ParsingContext): BindingElement { + if (context === ParsingContext.ArrayBindingElements && token === SyntaxKind.CommaToken) { + return createNode(SyntaxKind.OmittedExpression); + } + var node = createNode(kind); node.flags = flags; - if (context === ParsingContext.ObjectBindingDeclarations) { + if (context === ParsingContext.ObjectBindingElements) { var id = parseIdentifier(); if (parseOptional(SyntaxKind.ColonToken)) { node.propertyName = id; @@ -3405,18 +3408,19 @@ module ts { else { node.name = parseIdentifierOrPattern(flags); } + node.initializer = parseInitializer(/*inParameter*/ false); return finishNode(node); } - function parseBindingList(flags: NodeFlags, context: ParsingContext): NodeArray { - return parseDelimitedList(context, () => parseBindingDeclaration(flags, context), /*allowTrailingComma*/ true); + function parseBindingList(flags: NodeFlags, context: ParsingContext): NodeArray { + return parseDelimitedList(context, () => parseBindingElement(SyntaxKind.VariableDeclaration, flags, context), /*allowTrailingComma*/ true); } function parseObjectBindingPattern(flags: NodeFlags): BindingPattern { var node = createNode(SyntaxKind.ObjectBindingPattern); node.flags = flags; parseExpected(SyntaxKind.OpenBraceToken); - node.declarations = parseBindingList(flags, ParsingContext.ObjectBindingDeclarations); + node.elements = parseBindingList(flags, ParsingContext.ObjectBindingElements); parseExpected(SyntaxKind.CloseBraceToken); return finishNode(node); } @@ -3425,7 +3429,7 @@ module ts { var node = createNode(SyntaxKind.ArrayBindingPattern); node.flags = flags; parseExpected(SyntaxKind.OpenBracketToken); - node.declarations = parseBindingList(flags, ParsingContext.ArrayBindingDeclarations); + node.elements = parseBindingList(flags, ParsingContext.ArrayBindingElements); parseExpected(SyntaxKind.CloseBracketToken); return finishNode(node); } @@ -3441,7 +3445,13 @@ module ts { if (token === SyntaxKind.OpenBraceToken) { return parseObjectBindingPattern(flags); } - return parseIdentifier(); + var id = parseIdentifier(); + if (isInStrictMode && isEvalOrArgumentsIdentifier(id)) { + // It is a SyntaxError if a VariableDeclaration or VariableDeclarationNoIn occurs within strict code + // and its Identifier is eval or arguments + reportInvalidUseInStrictMode(id); + } + return id; } function parseVariableDeclaration(flags: NodeFlags, noIn?: boolean): VariableDeclaration { @@ -3462,11 +3472,6 @@ module ts { if (!inAmbientContext && !node.initializer && flags & NodeFlags.Const) { grammarErrorOnNode(node, Diagnostics.const_declarations_must_be_initialized); } - if (isInStrictMode && isEvalOrArgumentsIdentifier(node.name)) { - // It is a SyntaxError if a VariableDeclaration or VariableDeclarationNoIn occurs within strict code - // and its Identifier is eval or arguments - reportInvalidUseInStrictMode(node.name); - } return finishNode(node); } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 79084a45583..0f75b7f410e 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -164,7 +164,6 @@ module ts { // Binding patterns ObjectBindingPattern, ArrayBindingPattern, - PatternDeclaration, // Expression ArrayLiteral, ObjectLiteral, @@ -323,20 +322,17 @@ module ts { export interface SignatureDeclaration extends Declaration, ParsedSignature { } export interface VariableDeclaration extends Declaration { - name: Identifier | BindingPattern; - type?: TypeNode; - initializer?: Expression; + propertyName?: Identifier; // Binding property name (in object binding pattern) + name: Identifier | BindingPattern; // Declared variable name + type?: TypeNode; // Optional type annotation + initializer?: Expression; // Optional initializer } export interface BindingPattern extends Node { - declarations: PatternDeclaration[]; + elements: NodeArray; } - export interface PatternDeclaration extends Declaration { - propertyName?: Identifier; // Binding property name - name: Identifier | BindingPattern; // Declared variable name (pattern = undefined) - initializer?: Expression; // Optional initializer - } + export interface BindingElement extends VariableDeclaration { } export interface PropertyDeclaration extends Declaration { type?: TypeNode; @@ -866,7 +862,7 @@ module ts { Prototype = 0x20000000, // Prototype property (no source representation) UnionProperty = 0x40000000, // Property in union type - Enum = RegularEnum | ConstEnum, + Enum = RegularEnum | ConstEnum, Variable = FunctionScopedVariable | BlockScopedVariable, Value = Variable | Property | EnumMember | Function | Class | Enum | ValueModule | Method | GetAccessor | SetAccessor, Type = Class | Interface | Enum | TypeLiteral | ObjectLiteral | TypeParameter | TypeAlias,