Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Destructuring fixes #2754

Merged
merged 21 commits into from
Apr 16, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
2d3b22c
Disallow binding patterns in binding rest elements (fixes #2519)
JsonFreeman Apr 9, 2015
e9f5acc
Disallow binding patterns in rest parameters
JsonFreeman Apr 10, 2015
f84ebba
Rest assignment element allows nested destructuring (fixes #2156)
JsonFreeman Apr 10, 2015
5bfbcdc
Assignment rest element pretends to get the contextual element type
JsonFreeman Apr 10, 2015
e6ffb33
Fix crash in rest element destructuring
JsonFreeman Apr 10, 2015
2b21cd9
Disallow initializers in assignment rest elements
JsonFreeman Apr 11, 2015
1ec6164
Remove unnecessary parameter to emitDestructuring
JsonFreeman Apr 11, 2015
051aa39
Some cleanup in emitDestructuring
JsonFreeman Apr 11, 2015
2b83dfb
Properly emit nested destructuring in rest elements (fixes #2587)
JsonFreeman Apr 13, 2015
70c06ce
Fix crash in emitter on rest parameters with binding patterns
JsonFreeman Apr 14, 2015
66b50a1
Simplify handling of rest parameters with binding patterns in the che…
JsonFreeman Apr 14, 2015
15e39f7
Change parenthesizeForAccess to check isLeftHandSideExpression
JsonFreeman Apr 14, 2015
25fafd3
Update baselines after rebase
JsonFreeman Apr 14, 2015
8f57867
Merge branch 'master' of https://github.com/Microsoft/TypeScript into…
JsonFreeman Apr 14, 2015
cb9954b
The element/iterated type of any is any
JsonFreeman Apr 15, 2015
2cadea2
Merge branch 'master' of https://github.com/Microsoft/TypeScript into…
JsonFreeman Apr 15, 2015
1cda3dc
Fix parenthesizeForAccess to always parenthesize NewExpressions and N…
JsonFreeman Apr 16, 2015
edf1ae6
Merge branch 'master' of https://github.com/Microsoft/TypeScript into…
JsonFreeman Apr 16, 2015
ce0dfc1
Update baseline after merge
JsonFreeman Apr 16, 2015
0dd6609
Merge branch 'master' of https://github.com/Microsoft/TypeScript into…
JsonFreeman Apr 16, 2015
2c45e72
Update baseline after merge again
JsonFreeman Apr 16, 2015
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 56 additions & 30 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2197,25 +2197,7 @@ module ts {
}
else if (hasSpreadElement) {
let unionOfElements = getUnionType(elementTypes);
if (languageVersion >= ScriptTarget.ES6) {
// If the user has something like:
//
// function fun(...[a, ...b]) { }
//
// Normally, in ES6, the implied type of an array binding pattern with a rest element is
// an iterable. However, there is a requirement in our type system that all rest
// parameters be array types. To satisfy this, we have an exception to the rule that
// says the type of an array binding pattern with a rest element is an array type
// if it is *itself* in a rest parameter. It will still be compatible with a spreaded
// iterable argument, but within the function it will be an array.
let parent = pattern.parent;
let isRestParameter = parent.kind === SyntaxKind.Parameter &&
pattern === (<ParameterDeclaration>parent).name &&
(<ParameterDeclaration>parent).dotDotDotToken !== undefined;
return isRestParameter ? createArrayType(unionOfElements) : createIterableType(unionOfElements);
}

return createArrayType(unionOfElements);
return languageVersion >= ScriptTarget.ES6 ? createIterableType(unionOfElements) : createArrayType(unionOfElements);
}

// If the pattern has at least one element, and no rest element, then it should imply a tuple type.
Expand Down Expand Up @@ -6035,14 +6017,38 @@ module ts {
}
let hasSpreadElement = false;
let elementTypes: Type[] = [];
let inDestructuringPattern = isAssignmentTarget(node);
for (let e of elements) {
let type = checkExpression(e, contextualMapper);
elementTypes.push(type);
if (inDestructuringPattern && e.kind === SyntaxKind.SpreadElementExpression) {
// Given the following situation:
// var c: {};
// [...c] = ["", 0];
//
// c is represented in the tree as a spread element in an array literal.
// But c really functions as a rest element, and its purpose is to provide
// a contextual type for the right hand side of the assignment. Therefore,
// instead of calling checkExpression on "...c", which will give an error
// if c is not iterable/array-like, we need to act as if we are trying to
// get the contextual element type from it. So we do something similar to
// getContextualTypeForElementExpression, which will crucially not error
// if there is no index type / iterated type.
let restArrayType = checkExpression((<SpreadElementExpression>e).expression, contextualMapper);
let restElementType = getIndexTypeOfType(restArrayType, IndexKind.Number) ||
(languageVersion >= ScriptTarget.ES6 ? checkIteratedType(restArrayType, /*expressionForError*/ undefined) : undefined);

if (restElementType) {
elementTypes.push(restElementType);
}
}
else {
let type = checkExpression(e, contextualMapper);
elementTypes.push(type);
}
hasSpreadElement = hasSpreadElement || e.kind === SyntaxKind.SpreadElementExpression;
}
if (!hasSpreadElement) {
let contextualType = getContextualType(node);
if (contextualType && contextualTypeIsTupleLikeType(contextualType) || isAssignmentTarget(node)) {
if (contextualType && contextualTypeIsTupleLikeType(contextualType) || inDestructuringPattern) {
return createTupleType(elementTypes);
}
}
Expand Down Expand Up @@ -7633,7 +7639,7 @@ module ts {
// This elementType will be used if the specific property corresponding to this index is not
// present (aka the tuple element property). This call also checks that the parentType is in
// fact an iterable or array (depending on target language).
let elementType = checkIteratedTypeOrElementType(sourceType, node, /*allowStringInput*/ false);
let elementType = checkIteratedTypeOrElementType(sourceType, node, /*allowStringInput*/ false) || unknownType;
let elements = node.elements;
for (let i = 0; i < elements.length; i++) {
let e = elements[i];
Expand All @@ -7657,11 +7663,17 @@ module ts {
}
}
else {
if (i === elements.length - 1) {
checkReferenceAssignment((<SpreadElementExpression>e).expression, createArrayType(elementType), contextualMapper);
if (i < elements.length - 1) {
error(e, Diagnostics.A_rest_element_must_be_last_in_an_array_destructuring_pattern);
}
else {
error(e, Diagnostics.A_rest_element_must_be_last_in_an_array_destructuring_pattern);
let restExpression = (<SpreadElementExpression>e).expression;
if (restExpression.kind === SyntaxKind.BinaryExpression && (<BinaryExpression>restExpression).operatorToken.kind === SyntaxKind.EqualsToken) {
error((<BinaryExpression>restExpression).operatorToken, Diagnostics.A_rest_element_cannot_have_an_initializer);
}
else {
checkDestructuringAssignment(restExpression, createArrayType(elementType), contextualMapper);
}
}
}
}
Expand Down Expand Up @@ -8121,10 +8133,11 @@ module ts {
if (node.questionToken && isBindingPattern(node.name) && func.body) {
error(node, Diagnostics.A_binding_pattern_parameter_cannot_be_optional_in_an_implementation_signature);
}
if (node.dotDotDotToken) {
if (!isArrayType(getTypeOfSymbol(node.symbol))) {
error(node, Diagnostics.A_rest_parameter_must_be_of_an_array_type);
}

// Only check rest parameter type if it's not a binding pattern. Since binding patterns are
// not allowed in a rest parameter, we already have an error from checkGrammarParameterList.
if (node.dotDotDotToken && !isBindingPattern(node.name) && !isArrayType(getTypeOfSymbol(node.symbol))) {
error(node, Diagnostics.A_rest_parameter_must_be_of_an_array_type);
}
}

Expand Down Expand Up @@ -9427,6 +9440,10 @@ module ts {
}

function checkIteratedTypeOrElementType(inputType: Type, errorNode: Node, allowStringInput: boolean): Type {
if (inputType.flags & TypeFlags.Any) {
return inputType;
}

if (languageVersion >= ScriptTarget.ES6) {
return checkIteratedType(inputType, errorNode) || anyType;
}
Expand Down Expand Up @@ -12270,6 +12287,10 @@ module ts {
return grammarErrorOnNode(parameter.dotDotDotToken, Diagnostics.A_rest_parameter_must_be_last_in_a_parameter_list);
}

if (isBindingPattern(parameter.name)) {
return grammarErrorOnNode(parameter.name, Diagnostics.A_rest_element_cannot_contain_a_binding_pattern);
}

if (parameter.questionToken) {
return grammarErrorOnNode(parameter.questionToken, Diagnostics.A_rest_parameter_cannot_be_optional);
}
Expand Down Expand Up @@ -12756,6 +12777,11 @@ module ts {
if (node !== elements[elements.length - 1]) {
return grammarErrorOnNode(node, Diagnostics.A_rest_element_must_be_last_in_an_array_destructuring_pattern);
}

if (node.name.kind === SyntaxKind.ArrayBindingPattern || node.name.kind === SyntaxKind.ObjectBindingPattern) {
return grammarErrorOnNode(node.name, Diagnostics.A_rest_element_cannot_contain_a_binding_pattern);
}

if (node.initializer) {
// Error on equals token which immediate precedes the initializer
return grammarErrorAtPos(getSourceFileOfNode(node), node.initializer.pos - 1, 1, Diagnostics.A_rest_element_cannot_have_an_initializer);
Expand Down
1 change: 1 addition & 0 deletions src/compiler/diagnosticInformationMap.generated.ts
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,7 @@ module ts {
External_module_0_uses_export_and_cannot_be_used_with_export_Asterisk: { code: 2498, category: DiagnosticCategory.Error, key: "External module '{0}' uses 'export =' and cannot be used with 'export *'." },
An_interface_can_only_extend_an_identifier_Slashqualified_name_with_optional_type_arguments: { code: 2499, category: DiagnosticCategory.Error, key: "An interface can only extend an identifier/qualified-name with optional type arguments." },
A_class_can_only_implement_an_identifier_Slashqualified_name_with_optional_type_arguments: { code: 2500, category: DiagnosticCategory.Error, key: "A class can only implement an identifier/qualified-name with optional type arguments." },
A_rest_element_cannot_contain_a_binding_pattern: { code: 2501, category: DiagnosticCategory.Error, key: "A rest element cannot contain a binding pattern." },
Import_declaration_0_is_using_private_name_1: { code: 4000, category: DiagnosticCategory.Error, key: "Import declaration '{0}' is using private name '{1}'." },
Type_parameter_0_of_exported_class_has_or_is_using_private_name_1: { code: 4002, category: DiagnosticCategory.Error, key: "Type parameter '{0}' of exported class has or is using private name '{1}'." },
Type_parameter_0_of_exported_interface_has_or_is_using_private_name_1: { code: 4004, category: DiagnosticCategory.Error, key: "Type parameter '{0}' of exported interface has or is using private name '{1}'." },
Expand Down
4 changes: 4 additions & 0 deletions src/compiler/diagnosticMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -1439,6 +1439,10 @@
"category": "Error",
"code": 2500
},
"A rest element cannot contain a binding pattern.": {
"category": "Error",
"code": 2501
},

"Import declaration '{0}' is using private name '{1}'.": {
"category": "Error",
Expand Down
Loading