Destructuring parameter declarations

This commit is contained in:
Anders Hejlsberg 2014-11-19 14:42:06 -08:00
parent 7840f0d6b8
commit 850f3cb609
4 changed files with 105 additions and 95 deletions

View File

@ -330,6 +330,10 @@ module ts {
bindChildren(node, SymbolFlags.BlockScopedVariable, /*isBlockScopeContainer*/ false);
}
function getDestructuringParameterName(node: Declaration) {
return "__" + indexOf((<SignatureDeclaration>node.parent).parameters, node);
}
function bind(node: Node) {
node.parent = parent;
switch (node.kind) {
@ -337,7 +341,17 @@ module ts {
bindDeclaration(<Declaration>node, SymbolFlags.TypeParameter, SymbolFlags.TypeParameterExcludes, /*isBlockScopeContainer*/ false);
break;
case SyntaxKind.Parameter:
bindDeclaration(<Declaration>node, SymbolFlags.FunctionScopedVariable, SymbolFlags.ParameterExcludes, /*isBlockScopeContainer*/ false);
if (isBindingPattern((<Declaration>node).name)) {
if (isBindingPattern(parent)) {
bindChildren(node, 0, /*isBlockScopeContainer*/ false);
}
else {
bindAnonymousDeclaration(node, SymbolFlags.FunctionScopedVariable, getDestructuringParameterName(node), /*isBlockScopeContainer*/ false);
}
}
else {
bindDeclaration(<Declaration>node, SymbolFlags.FunctionScopedVariable, SymbolFlags.ParameterExcludes, /*isBlockScopeContainer*/ false);
}
break;
case SyntaxKind.VariableDeclaration:
if (isBindingPattern((<Declaration>node).name)) {
@ -351,6 +365,8 @@ module ts {
}
break;
case SyntaxKind.Property:
bindDeclaration(<Declaration>node, SymbolFlags.Property | (node.flags & NodeFlags.QuestionMark ? SymbolFlags.Optional : 0), SymbolFlags.PropertyExcludes, /*isBlockScopeContainer*/ false);
break;
case SyntaxKind.PropertyAssignment:
bindDeclaration(<Declaration>node, SymbolFlags.Property, SymbolFlags.PropertyExcludes, /*isBlockScopeContainer*/ false);
break;
@ -358,16 +374,12 @@ module ts {
bindDeclaration(<Declaration>node, SymbolFlags.EnumMember, SymbolFlags.EnumMemberExcludes, /*isBlockScopeContainer*/ false);
break;
case SyntaxKind.CallSignature:
bindDeclaration(<Declaration>node, SymbolFlags.CallSignature, 0, /*isBlockScopeContainer*/ false);
case SyntaxKind.ConstructSignature:
case SyntaxKind.IndexSignature:
bindDeclaration(<Declaration>node, SymbolFlags.Signature, 0, /*isBlockScopeContainer*/ false);
break;
case SyntaxKind.Method:
bindDeclaration(<Declaration>node, SymbolFlags.Method, SymbolFlags.MethodExcludes, /*isBlockScopeContainer*/ true);
break;
case SyntaxKind.ConstructSignature:
bindDeclaration(<Declaration>node, SymbolFlags.ConstructSignature, 0, /*isBlockScopeContainer*/ true);
break;
case SyntaxKind.IndexSignature:
bindDeclaration(<Declaration>node, SymbolFlags.IndexSignature, 0, /*isBlockScopeContainer*/ false);
bindDeclaration(<Declaration>node, SymbolFlags.Method | (node.flags & NodeFlags.QuestionMark ? SymbolFlags.Optional : 0), SymbolFlags.MethodExcludes, /*isBlockScopeContainer*/ true);
break;
case SyntaxKind.FunctionDeclaration:
bindDeclaration(<Declaration>node, SymbolFlags.Function, SymbolFlags.FunctionExcludes, /*isBlockScopeContainer*/ true);

View File

@ -741,17 +741,6 @@ module ts {
members, callSignatures, constructSignatures, stringIndexType, numberIndexType);
}
function isOptionalProperty(propertySymbol: Symbol): boolean {
// class C {
// constructor(public x?) { }
// }
//
// x is an optional parameter, but it is a required property.
return propertySymbol.valueDeclaration &&
propertySymbol.valueDeclaration.flags & NodeFlags.QuestionMark &&
propertySymbol.valueDeclaration.kind !== SyntaxKind.Parameter;
}
function forEachSymbolTableInScope<T>(enclosingDeclaration: Node, callback: (symbolTable: SymbolTable) => T): T {
var result: T;
for (var location = enclosingDeclaration; location; location = location.parent) {
@ -1366,7 +1355,7 @@ module ts {
var signatures = getSignaturesOfType(t, SignatureKind.Call);
for (var j = 0; j < signatures.length; j++) {
buildSymbolDisplay(p, writer);
if (isOptionalProperty(p)) {
if (p.flags & SymbolFlags.Optional) {
writePunctuation(writer, SyntaxKind.QuestionToken);
}
buildSignatureDisplay(signatures[j], writer, enclosingDeclaration, globalFlagsToPass, typeStack);
@ -1376,7 +1365,7 @@ module ts {
}
else {
buildSymbolDisplay(p, writer);
if (isOptionalProperty(p)) {
if (p.flags & SymbolFlags.Optional) {
writePunctuation(writer, SyntaxKind.QuestionToken);
}
writePunctuation(writer, SyntaxKind.ColonToken);
@ -1699,6 +1688,31 @@ module ts {
return undefined;
}
function getTypeFromBindingElement(element: BindingElement): Type {
if (element.initializer) {
return getWidenedType(checkExpressionCached(element.initializer));
}
if (isBindingPattern(element.name)) {
return getTypeFromBindingPattern(<BindingPattern>element.name);
}
return anyType;
}
function getTypeFromBindingPattern(pattern: BindingPattern): Type {
if (pattern.kind === SyntaxKind.ArrayBindingPattern) {
return createTupleType(map(pattern.elements, getTypeFromBindingElement));
}
var members: SymbolTable = {};
forEach(pattern.elements, e => {
var flags = SymbolFlags.Property | SymbolFlags.Transient | (e.initializer ? SymbolFlags.Optional : 0);
var name = e.propertyName || <Identifier>e.name;
var symbol = <TransientSymbol>createSymbol(flags, name.text);
symbol.type = getTypeFromBindingElement(e);
members[symbol.name] = symbol;
});
return createAnonymousType(undefined, members, emptyArray, emptyArray, undefined, undefined);
}
function getWidenedTypeForVariableDeclaration(declaration: VariableDeclaration | PropertyDeclaration, reportErrors?: boolean): Type {
var type = getTypeForVariableDeclaration(declaration);
if (type) {
@ -1707,6 +1721,9 @@ module ts {
}
return declaration.kind !== SyntaxKind.PropertyAssignment ? getWidenedType(type) : type;
}
if (isBindingPattern(declaration.name)) {
return getTypeFromBindingPattern(<BindingPattern>declaration.name);
}
// Rest parameters default to type any[], other parameters default to type any type = declaration.flags & NodeFlags.Rest ? createArrayType(anyType) : anyType;
// Report implicit any errors unless this is a private property within an ambient declaration
if (reportErrors && compilerOptions.noImplicitAny && !isPrivateWithinAmbient(declaration) && !(declaration.kind === SyntaxKind.Parameter && isPrivateWithinAmbient(declaration.parent))) {
@ -3528,7 +3545,7 @@ module ts {
var sourceProp = getPropertyOfType(source, targetProp.name);
if (sourceProp !== targetProp) {
if (!sourceProp) {
if (relation === subtypeRelation || !isOptionalProperty(targetProp)) {
if (relation === subtypeRelation || !(targetProp.flags & SymbolFlags.Optional)) {
if (reportErrors) {
reportError(Diagnostics.Property_0_is_missing_in_type_1, symbolToString(targetProp), typeToString(source));
}
@ -3580,7 +3597,7 @@ module ts {
return Ternary.False;
}
result &= related;
if (isOptionalProperty(sourceProp) && !isOptionalProperty(targetProp)) {
if (sourceProp.flags & SymbolFlags.Optional && !(targetProp.flags & SymbolFlags.Optional)) {
// TypeScript 1.0 spec (April 2014): 3.8.3
// S is a subtype of a type T, and T is a supertype of S if ...
// S' and T are object types and, for each member M in T..
@ -3821,7 +3838,7 @@ module ts {
}
}
else {
if (isOptionalProperty(sourceProp) !== isOptionalProperty(targetProp)) {
if ((sourceProp.flags & SymbolFlags.Optional) !== (targetProp.flags & SymbolFlags.Optional)) {
return Ternary.False;
}
}
@ -6389,30 +6406,27 @@ module ts {
// TODO: Check multiple declarations are identical
}
// TODO(andersh): Support destructuring
function checkParameter(parameterDeclaration: ParameterDeclaration) {
if (isBindingPattern(parameterDeclaration.name)) {
function checkParameter(node: ParameterDeclaration) {
checkVariableDeclaration(node);
if (isBindingPattern(node.name)) {
return;
}
checkVariableDeclaration(parameterDeclaration);
if (fullTypeCheck) {
checkCollisionWithIndexVariableInGeneratedCode(parameterDeclaration, <Identifier>parameterDeclaration.name);
if (parameterDeclaration.flags & (NodeFlags.Public | NodeFlags.Private | NodeFlags.Protected) &&
!(parameterDeclaration.parent.kind === SyntaxKind.Constructor && (<ConstructorDeclaration>parameterDeclaration.parent).body)) {
error(parameterDeclaration, Diagnostics.A_parameter_property_is_only_allowed_in_a_constructor_implementation);
checkCollisionWithIndexVariableInGeneratedCode(node, <Identifier>node.name);
var func = getContainingFunction(node);
if (node.flags & (NodeFlags.Public | NodeFlags.Private | NodeFlags.Protected) &&
!(func.kind === SyntaxKind.Constructor && func.body)) {
error(node, Diagnostics.A_parameter_property_is_only_allowed_in_a_constructor_implementation);
}
if (node.flags & NodeFlags.Rest) {
if (!isArrayType(getTypeOfSymbol(node.symbol))) {
error(node, Diagnostics.A_rest_parameter_must_be_of_an_array_type);
}
if (parameterDeclaration.flags & NodeFlags.Rest) {
if (!isArrayType(getTypeOfSymbol(parameterDeclaration.symbol))) {
error(parameterDeclaration, Diagnostics.A_rest_parameter_must_be_of_an_array_type);
}
}
else {
if (parameterDeclaration.initializer && !(<FunctionLikeDeclaration>parameterDeclaration.parent).body) {
error(parameterDeclaration, Diagnostics.A_parameter_initializer_is_only_allowed_in_a_function_or_constructor_implementation);
}
}
if (node.initializer) {
if (!func.body) {
error(node, Diagnostics.A_parameter_initializer_is_only_allowed_in_a_function_or_constructor_implementation);
}
checkReferencesInInitializer(node.initializer);
}
function checkReferencesInInitializer(n: Node): void {
@ -6420,33 +6434,25 @@ module ts {
var referencedSymbol = getNodeLinks(n).resolvedSymbol;
// check FunctionLikeDeclaration.locals (stores parameters\function local variable)
// if it contains entry with a specified name and if this entry matches the resolved symbol
if (referencedSymbol && referencedSymbol !== unknownSymbol && getSymbol(parameterDeclaration.parent.locals, referencedSymbol.name, SymbolFlags.Value) === referencedSymbol) {
if (referencedSymbol && referencedSymbol !== unknownSymbol && getSymbol(func.locals, referencedSymbol.name, SymbolFlags.Value) === referencedSymbol) {
if (referencedSymbol.valueDeclaration.kind === SyntaxKind.Parameter) {
if (referencedSymbol.valueDeclaration === parameterDeclaration) {
error(n, Diagnostics.Parameter_0_cannot_be_referenced_in_its_initializer, declarationNameToString(parameterDeclaration.name));
if (referencedSymbol.valueDeclaration === node) {
error(n, Diagnostics.Parameter_0_cannot_be_referenced_in_its_initializer, declarationNameToString(node.name));
return;
}
var enclosingOrReferencedParameter =
forEach((<FunctionLikeDeclaration>parameterDeclaration.parent).parameters, p => p === parameterDeclaration || p === referencedSymbol.valueDeclaration ? p : undefined);
if (enclosingOrReferencedParameter === referencedSymbol.valueDeclaration) {
if (referencedSymbol.valueDeclaration.pos < node.pos) {
// legal case - parameter initializer references some parameter strictly on left of current parameter declaration
return;
}
// fall through to error reporting
}
error(n, Diagnostics.Initializer_of_parameter_0_cannot_reference_identifier_1_declared_after_it, declarationNameToString(parameterDeclaration.name), declarationNameToString(<Identifier>n));
error(n, Diagnostics.Initializer_of_parameter_0_cannot_reference_identifier_1_declared_after_it, declarationNameToString(node.name), declarationNameToString(<Identifier>n));
}
}
else {
forEachChild(n, checkReferencesInInitializer);
}
}
if (parameterDeclaration.initializer) {
checkReferencesInInitializer(parameterDeclaration.initializer);
}
}
function checkSignatureDeclaration(node: SignatureDeclaration) {
@ -7075,7 +7081,8 @@ module ts {
// - function has implementation (not a signature)
// - function has rest parameters
// - context is not ambient (otherwise no codegen impact)
if ((<FunctionLikeDeclaration>node.parent).body && hasRestParameters(<FunctionLikeDeclaration>node.parent) && !isInAmbientContext(node)) {
var func = getContainingFunction(node);
if (func.body && hasRestParameters(func) && !isInAmbientContext(node)) {
error(node, Diagnostics.Duplicate_identifier_i_Compiler_uses_i_to_initialize_rest_parameter);
}
return;
@ -7122,7 +7129,7 @@ module ts {
// no - go up to the next level
var current = node;
while (current) {
var definedOnCurrentLevel = forEach(symbol.declarations, d => d.parent === current ? d : undefined);
var definedOnCurrentLevel = forEach(symbol.declarations, d => getContainingFunction(d) === current);
if (definedOnCurrentLevel) {
return;
}
@ -7273,11 +7280,7 @@ module ts {
function checkVariableDeclaration(node: VariableDeclaration | PropertyDeclaration) {
if (isBindingPattern(node.name)) {
forEach((<BindingPattern>node.name).elements, e => {
if (e.kind === SyntaxKind.VariableDeclaration) {
checkVariableDeclaration(e);
}
});
forEach((<BindingPattern>node.name).elements, checkSourceElement);
if (node.initializer) {
checkTypeAssignableTo(checkExpressionCached(node.initializer), getWidenedTypeForVariableDeclaration(node), node, /*headMessage*/ undefined);
}
@ -7308,7 +7311,7 @@ module ts {
}
function checkVariableStatement(node: VariableStatement) {
forEach(node.declarations, checkVariableDeclaration);
forEach(node.declarations, checkSourceElement);
}
function checkExpressionStatement(node: ExpressionStatement) {
@ -8184,7 +8187,7 @@ module ts {
case SyntaxKind.TryStatement:
return checkTryStatement(<TryStatement>node);
case SyntaxKind.VariableDeclaration:
return Debug.fail("Checker encountered variable declaration");
return checkVariableDeclaration(<VariableDeclaration>node);
case SyntaxKind.ClassDeclaration:
return checkClassDeclaration(<ClassDeclaration>node);
case SyntaxKind.InterfaceDeclaration:

View File

@ -411,6 +411,9 @@ module ts {
case SyntaxKind.GetAccessor:
case SyntaxKind.SetAccessor:
case SyntaxKind.Constructor:
case SyntaxKind.CallSignature:
case SyntaxKind.ConstructSignature:
case SyntaxKind.IndexSignature:
return true;
}
}
@ -1245,7 +1248,7 @@ module ts {
return token === SyntaxKind.CloseBracketToken;
case ParsingContext.Parameters:
// Tokens other than ')' and ']' (the latter for index signatures) are here for better error recovery
return token === SyntaxKind.CloseParenToken || token === SyntaxKind.CloseBracketToken || token === SyntaxKind.OpenBraceToken;
return token === SyntaxKind.CloseParenToken || token === SyntaxKind.CloseBracketToken /*|| token === SyntaxKind.OpenBraceToken*/;
case ParsingContext.TypeArguments:
// Tokens other than '>' are here for better error recovery
return token === SyntaxKind.GreaterThanToken || token === SyntaxKind.OpenParenToken;
@ -1568,7 +1571,7 @@ module ts {
}
function isStartOfParameter(): boolean {
return token === SyntaxKind.DotDotDotToken || isIdentifier() || isModifier(token);
return token === SyntaxKind.DotDotDotToken || isIdentifierOrPattern() || isModifier(token);
}
function parseParameter(flags: NodeFlags = 0): ParameterDeclaration {
@ -1576,8 +1579,11 @@ module ts {
node.flags |= parseAndCheckModifiers(ModifierContext.Parameters);
if (parseOptional(SyntaxKind.DotDotDotToken)) {
node.flags |= NodeFlags.Rest;
node.name = parseIdentifier();
}
else {
node.name = parseIdentifierOrPattern(node.flags);
}
node.name = parseIdentifier();
if (node.name.kind === SyntaxKind.Missing && node.flags === 0 && isModifier(token)) {
// in cases like
// 'use strict'
@ -1645,16 +1651,7 @@ module ts {
for (var i = 0; i < parameterCount; i++) {
var parameter = parameters[i];
// It is a SyntaxError if the Identifier "eval" or the Identifier "arguments" occurs as the
// Identifier in a PropertySetParameterList of a PropertyAssignment that is contained in strict code
// or if its FunctionBody is strict code(11.1.5).
// It is a SyntaxError if the identifier eval or arguments appears within a FormalParameterList of a
// strict mode FunctionLikeDeclaration or FunctionExpression(13.1)
if (isInStrictMode && isEvalOrArgumentsIdentifier(parameter.name)) {
reportInvalidUseInStrictMode(<Identifier>parameter.name);
return;
}
else if (parameter.flags & NodeFlags.Rest) {
if (parameter.flags & NodeFlags.Rest) {
if (i !== (parameterCount - 1)) {
grammarErrorOnNode(parameter.name, Diagnostics.A_rest_parameter_must_be_last_in_a_parameter_list);
return;

View File

@ -845,22 +845,21 @@ module ts {
Constructor = 0x00004000, // Constructor
GetAccessor = 0x00008000, // Get accessor
SetAccessor = 0x00010000, // Set accessor
CallSignature = 0x00020000, // Call signature
ConstructSignature = 0x00040000, // Construct signature
IndexSignature = 0x00080000, // Index signature
TypeParameter = 0x00100000, // Type parameter
TypeAlias = 0x00200000, // Type alias
Signature = 0x00020000, // Call, construct, or index signature
TypeParameter = 0x00040000, // Type parameter
TypeAlias = 0x00080000, // Type alias
// Export markers (see comment in declareModuleMember in binder)
ExportValue = 0x00400000, // Exported value marker
ExportType = 0x00800000, // Exported type marker
ExportNamespace = 0x01000000, // Exported namespace marker
Import = 0x02000000, // Import
Instantiated = 0x04000000, // Instantiated symbol
Merged = 0x08000000, // Merged symbol (created during program binding)
Transient = 0x10000000, // Transient symbol (created during type check)
Prototype = 0x20000000, // Prototype property (no source representation)
UnionProperty = 0x40000000, // Property in union type
ExportValue = 0x00100000, // Exported value marker
ExportType = 0x00200000, // Exported type marker
ExportNamespace = 0x00400000, // Exported namespace marker
Import = 0x00800000, // Import
Instantiated = 0x01000000, // Instantiated symbol
Merged = 0x02000000, // Merged symbol (created during program binding)
Transient = 0x04000000, // Transient symbol (created during type check)
Prototype = 0x08000000, // Prototype property (no source representation)
UnionProperty = 0x10000000, // Property in union type
Optional = 0x20000000, // Optional property
Enum = RegularEnum | ConstEnum,
Variable = FunctionScopedVariable | BlockScopedVariable,
@ -869,7 +868,6 @@ module ts {
Namespace = ValueModule | NamespaceModule,
Module = ValueModule | NamespaceModule,
Accessor = GetAccessor | SetAccessor,
Signature = CallSignature | ConstructSignature | IndexSignature,
// Variables can be redeclared, but can not redeclare a block-scoped declaration with the
// same name, or any other value that is not a variable, e.g. ValueModule or Class