diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index f0b2d71ccad..f1d6050f8f4 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -289,7 +289,6 @@ module ts { } declareSymbol(blockScopeContainer.locals, undefined, node, SymbolFlags.BlockScopedVariable, SymbolFlags.BlockScopedVariableExcludes); } - bindChildren(node, SymbolFlags.BlockScopedVariable, /*isBlockScopeContainer*/ false); } @@ -303,11 +302,17 @@ module ts { bindDeclaration(node, SymbolFlags.FunctionScopedVariable, SymbolFlags.ParameterExcludes, /*isBlockScopeContainer*/ false); break; case SyntaxKind.VariableDeclaration: - if (node.flags & NodeFlags.BlockScoped) { - bindBlockScopedVariableDeclaration(node); + case SyntaxKind.PatternDeclaration: + if ((node).name) { + if (node.flags & NodeFlags.BlockScoped) { + bindBlockScopedVariableDeclaration(node); + } + else { + bindDeclaration(node, SymbolFlags.FunctionScopedVariable, SymbolFlags.FunctionScopedVariableExcludes, /*isBlockScopeContainer*/ false); + } } else { - bindDeclaration(node, SymbolFlags.FunctionScopedVariable, SymbolFlags.FunctionScopedVariableExcludes, /*isBlockScopeContainer*/ false); + bindChildren(node, 0, /*isBlockScopeContainer*/ false); } break; case SyntaxKind.Property: @@ -377,7 +382,6 @@ module ts { bindAnonymousDeclaration(node, SymbolFlags.ValueModule, '"' + removeFileExtension((node).filename) + '"', /*isBlockScopeContainer*/ true); break; } - case SyntaxKind.Block: case SyntaxKind.TryBlock: case SyntaxKind.CatchBlock: @@ -385,9 +389,8 @@ module ts { case SyntaxKind.ForStatement: case SyntaxKind.ForInStatement: case SyntaxKind.SwitchStatement: - bindChildren(node, 0 , true); + bindChildren(node, 0, /*isBlockScopeContainer*/ true); break; - default: var saveParent = parent; parent = node; diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index ca4aeda4982..3cdc89a9752 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -4255,7 +4255,10 @@ module ts { case SyntaxKind.BinaryExpression: return isAssignedInBinaryExpression(node); case SyntaxKind.VariableDeclaration: + case SyntaxKind.PatternDeclaration: return isAssignedInVariableDeclaration(node); + case SyntaxKind.ObjectBindingPattern: + case SyntaxKind.ArrayBindingPattern: case SyntaxKind.ArrayLiteral: case SyntaxKind.ObjectLiteral: case SyntaxKind.PropertyAccess: @@ -8034,6 +8037,9 @@ module ts { break; case SyntaxKind.Parameter: 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/diagnosticInformationMap.generated.ts b/src/compiler/diagnosticInformationMap.generated.ts index abf2f78e966..2f00a727ba1 100644 --- a/src/compiler/diagnosticInformationMap.generated.ts +++ b/src/compiler/diagnosticInformationMap.generated.ts @@ -122,6 +122,8 @@ module ts { let_declarations_can_only_be_declared_inside_a_block: { code: 1157, category: DiagnosticCategory.Error, key: "'let' declarations can only be declared inside a block." }, Invalid_template_literal_expected: { code: 1158, category: DiagnosticCategory.Error, key: "Invalid template literal; expected '}'" }, Tagged_templates_are_only_available_when_targeting_ECMAScript_6_and_higher: { code: 1159, category: DiagnosticCategory.Error, key: "Tagged templates are only available when targeting ECMAScript 6 and higher." }, + Property_destructuring_pattern_expected: { code: 1160, category: DiagnosticCategory.Error, key: "Property destructuring pattern expected." }, + Array_element_destructuring_pattern_expected: { code: 1161, category: DiagnosticCategory.Error, key: "Array element destructuring pattern expected." }, Duplicate_identifier_0: { code: 2300, category: DiagnosticCategory.Error, key: "Duplicate identifier '{0}'." }, Initializer_of_instance_member_variable_0_cannot_reference_identifier_1_declared_in_the_constructor: { code: 2301, category: DiagnosticCategory.Error, key: "Initializer of instance member variable '{0}' cannot reference identifier '{1}' declared in the constructor." }, Static_members_cannot_reference_class_type_parameters: { code: 2302, category: DiagnosticCategory.Error, key: "Static members cannot reference class type parameters." }, diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 6aa34240bcd..0269850936d 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -479,6 +479,14 @@ "category": "Error", "code": 1159 }, + "Property destructuring pattern expected.": { + "category": "Error", + "code": 1160 + }, + "Array element destructuring pattern expected.": { + "category": "Error", + "code": 1161 + }, "Duplicate identifier '{0}'.": { "category": "Error", diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 1980d0a4014..242a60f5bde 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -229,6 +229,14 @@ module ts { return children((node).types); case SyntaxKind.ParenType: 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).pattern) || + child((node).initializer); case SyntaxKind.ArrayLiteral: return children((node).elements); case SyntaxKind.ObjectLiteral: @@ -506,6 +514,7 @@ module ts { case SyntaxKind.Property: case SyntaxKind.EnumMember: case SyntaxKind.PropertyAssignment: + case SyntaxKind.PatternDeclaration: return (parent).initializer === node; case SyntaxKind.ExpressionStatement: case SyntaxKind.IfStatement: @@ -663,24 +672,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 - 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 + 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 } enum Tristate { @@ -701,6 +712,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.ArgumentExpressions: return Diagnostics.Argument_expression_expected; case ParsingContext.ObjectLiteralMembers: return Diagnostics.Property_assignment_expected; case ParsingContext.ArrayLiteralMembers: return Diagnostics.Expression_or_comma_expected; @@ -1174,6 +1187,9 @@ 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: + return isIdentifierOrPattern(); case ParsingContext.TypeParameters: return isIdentifier(); case ParsingContext.ArgumentExpressions: @@ -1205,6 +1221,7 @@ module ts { case ParsingContext.ClassMembers: case ParsingContext.EnumMembers: case ParsingContext.ObjectLiteralMembers: + case ParsingContext.ObjectBindingDeclarations: return token === SyntaxKind.CloseBraceToken; case ParsingContext.SwitchClauseStatements: return token === SyntaxKind.CloseBraceToken || token === SyntaxKind.CaseKeyword || token === SyntaxKind.DefaultKeyword; @@ -1220,6 +1237,7 @@ module ts { return token === SyntaxKind.CloseParenToken || token === SyntaxKind.SemicolonToken; case ParsingContext.ArrayLiteralMembers: case ParsingContext.TupleElementTypes: + case ParsingContext.ArrayBindingDeclarations: return token === SyntaxKind.CloseBracketToken; case ParsingContext.Parameters: // Tokens other than ')' and ']' (the latter for index signatures) are here for better error recovery @@ -3356,11 +3374,68 @@ module ts { // DECLARATIONS + function parseBindingDeclaration(flags: NodeFlags, context: ParsingContext): PatternDeclaration { + var node = createNode(SyntaxKind.PatternDeclaration); + node.flags = flags; + if (context === ParsingContext.ObjectBindingDeclarations) { + var id = parseIdentifier(); + if (parseOptional(SyntaxKind.ColonToken)) { + node.propertyName = id; + parseIdentifierOrPatternOfNode(node, flags); + } + else { + node.name = id; + } + } + else { + parseIdentifierOrPatternOfNode(node, flags); + } + return finishNode(node); + } + + function parseBindingList(flags: NodeFlags, context: ParsingContext): NodeArray { + return parseDelimitedList(context, () => parseBindingDeclaration(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); + parseExpected(SyntaxKind.CloseBraceToken); + return finishNode(node); + } + + function parseArrayBindingPattern(flags: NodeFlags): BindingPattern { + var node = createNode(SyntaxKind.ArrayBindingPattern); + node.flags = flags; + parseExpected(SyntaxKind.OpenBracketToken); + node.declarations = parseBindingList(flags, ParsingContext.ArrayBindingDeclarations); + parseExpected(SyntaxKind.CloseBracketToken); + return finishNode(node); + } + + function isIdentifierOrPattern() { + return token === SyntaxKind.OpenBraceToken || token === SyntaxKind.OpenBracketToken || isIdentifier(); + } + + function parseIdentifierOrPatternOfNode(node: NameOrPatternNode, flags: NodeFlags) { + if (token === SyntaxKind.OpenBracketToken) { + node.pattern = parseArrayBindingPattern(flags); + } + else if (token === SyntaxKind.OpenBraceToken) { + node.pattern = parseObjectBindingPattern(flags); + } + else { + node.name = parseIdentifier(); + } + } + function parseVariableDeclaration(flags: NodeFlags, noIn?: boolean): VariableDeclaration { var node = createNode(SyntaxKind.VariableDeclaration); node.flags = flags; var errorCountBeforeVariableDeclaration = file.syntacticErrors.length; - node.name = parseIdentifier(); + parseIdentifierOrPatternOfNode(node, flags); node.type = parseTypeAnnotation(); // Issue any initializer-related errors on the equals token diff --git a/src/compiler/types.ts b/src/compiler/types.ts index ead84957731..b52919edb85 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -162,6 +162,10 @@ module ts { TupleType, UnionType, ParenType, + // Binding patterns + ObjectBindingPattern, + ArrayBindingPattern, + PatternDeclaration, // Expression ArrayLiteral, ObjectLiteral, @@ -313,10 +317,27 @@ module ts { export interface SignatureDeclaration extends Declaration, ParsedSignature { } export interface VariableDeclaration extends Declaration { + pattern?: BindingPattern; type?: TypeNode; initializer?: Expression; } + export interface BindingPattern extends Node { + declarations: PatternDeclaration[]; + } + + export interface PatternDeclaration extends Declaration { + propertyName?: Identifier; // Binding property name + name?: Identifier; // Declared variable name (pattern = undefined) + pattern?: BindingPattern; // Nested binding pattern (name = undefined) + initializer?: Expression; // Optional initializer + } + + export interface NameOrPatternNode extends Node { + name?: Identifier; + pattern?: BindingPattern; + } + export interface PropertyDeclaration extends VariableDeclaration { } export interface ParameterDeclaration extends VariableDeclaration { }