diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index bcff651c518..c60abea743c 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -1714,7 +1714,7 @@ module ts { function getTypeFromBindingPattern(pattern: BindingPattern): Type { if (pattern.kind === SyntaxKind.ArrayBindingPattern) { - return createTupleType(map(pattern.elements, getTypeFromBindingElement)); + return createTupleType(map(pattern.elements, e => e.kind === SyntaxKind.OmittedExpression ? anyType : getTypeFromBindingElement(e))); } var members: SymbolTable = {}; forEach(pattern.elements, e => { @@ -4628,7 +4628,6 @@ module ts { checkCollisionWithCapturedSuperVariable(node, node); checkCollisionWithCapturedThisVariable(node, node); - checkCollisionWithIndexVariableInGeneratedCode(node, node); return getNarrowedTypeOfSymbol(getExportSymbolOfValueSymbolIfExported(symbol), node); } @@ -6759,7 +6758,6 @@ module ts { if (isBindingPattern(node.name)) { return; } - checkCollisionWithIndexVariableInGeneratedCode(node, node.name); var func = getContainingFunction(node); if (node.flags & (NodeFlags.Public | NodeFlags.Private | NodeFlags.Protected) && !(func.kind === SyntaxKind.Constructor && func.body)) { @@ -7418,85 +7416,6 @@ module ts { }); } - function checkCollisionWithIndexVariableInGeneratedCode(node: Node, name: Identifier) { - if (!(name && name.text === "_i")) { - return; - } - - if (node.kind === SyntaxKind.Parameter) { - // report error if parameter has name '_i' when: - // - function has implementation (not a signature) - // - function has rest parameters - // - context is not ambient (otherwise no codegen impact) - 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; - } - - var symbol = getNodeLinks(node).resolvedSymbol; - if (symbol === unknownSymbol) { - return; - } - - // we would like to discover cases like one below: - // - // var _i = "!"; - // function foo(...a) { - // function bar() { - // var x = { get baz() { return _i; } } - // } - // } - // - // at runtime '_i' referenced in getter will be resolved to the generated index variable '_i' used to initialize rest parameters. - // legitimate case: when '_i' is defined inside the function declaration with rest parameters. - // - // function foo(...a) { - // var _i = "!"; - // function bar() { - // var x = { get baz() { return _i; } } - // } - // } - - //// if resolved symbol for node has more than one declaration - this is definitely an error - //// (there is nothing value-like in the language that can be nested in function and consists of multiple declarations) - //if (symbol.declarations.length > 1) { - // error(node, Diagnostics.Expression_resolves_to_variable_declaration_i_that_compiler_uses_to_initialize_rest_parameter); - // return; - //} - - // short gist of the check: - // - otherwise - // - walk to the top of the tree starting from the 'node' - // - at every step check if 'current' node contains any declaration of original node - // yes - return - // no - check if current declaration is function with rest parameters - // yes - report error since '_i' from this function will shadow '_i' defined in the outer scope - // no - go up to the next level - var current = node; - while (current) { - var definedOnCurrentLevel = forEach(symbol.declarations, d => getContainingFunction(d) === current); - if (definedOnCurrentLevel) { - return; - } - switch (current.kind) { - // all kinds that might have rest parameters - case SyntaxKind.FunctionDeclaration: - case SyntaxKind.FunctionExpression: - case SyntaxKind.Method: - case SyntaxKind.ArrowFunction: - case SyntaxKind.Constructor: - if (hasRestParameters(current)) { - error(node, Diagnostics.Expression_resolves_to_variable_declaration_i_that_compiler_uses_to_initialize_rest_parameter); - return; - } - break; - } - current = current.parent; - } - } - // TODO(jfreeman): Decide what to do for computed properties function needCollisionCheckForIdentifier(node: Node, identifier: DeclarationName, name: string): boolean { if (!(identifier && (identifier).text === name)) { diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index dabd6c943e7..3e462729dd4 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -1503,6 +1503,8 @@ module ts { emitEnd(node.name); } + // Rewrites a destructuring variable declaration into a sequence of regular variable declarations. + // Nodes generated by the syntactic rewrite have no parent references. function rewriteDestructuringDeclaration(node: VariableDeclaration): NodeArray { var declarations = >[]; rewriteDeclaration(node, undefined); @@ -1533,9 +1535,12 @@ module ts { } function createDefaultValueCheck(value: Expression, defaultValue: Expression): Expression { + // The value expression will be evaluated twice, so for anything but a simple identifier + // we need to generate a temporary variable if (value.kind !== SyntaxKind.Identifier) { value = createTemporaryVariable(value); } + // Return the expression 'value === void 0 ? defaultValue : value' var equals = createNode(SyntaxKind.BinaryExpression); equals.left = value; equals.operator = SyntaxKind.EqualsEqualsEqualsToken; @@ -1581,24 +1586,30 @@ module ts { function rewriteDeclaration(target: BindingElement, value: Expression) { if (target.initializer) { + // Combine value and initializer value = value ? createDefaultValueCheck(value, target.initializer) : target.initializer; } else if (!value) { + // Use 'void 0' in absence of value and initializer value = createVoidZero(); } if (isBindingPattern(target.name)) { var pattern = target.name; var elements = pattern.elements; if (elements.length !== 1) { + // For anything but a single element destructuring we need to generate a temporary + // to ensure value is evaluated exactly once. value = createTemporaryVariable(value); } for (var i = 0; i < elements.length; i++) { var element = elements[i]; if (pattern.kind === SyntaxKind.ObjectBindingPattern) { + // Rewrite element to a declaration with an initializer that fetches property var propName = element.propertyName || element.name; rewriteDeclaration(element, createPropertyAccess(value, propName)); } - else { + else if (element.kind !== SyntaxKind.OmittedExpression) { + // Rewrite element to a declaration that accesses array element at index i rewriteDeclaration(element, createIndexedAccess(value, createNumericLiteral(i))); } } @@ -1690,7 +1701,7 @@ module ts { if (hasRestParameters(node)) { var restIndex = node.parameters.length - 1; var restParam = node.parameters[restIndex]; - var tempName = "_i"; + var tempName = resolver.isUnknownIdentifier(node, "_i") ? "_i" : getTempVariableName(node); writeLine(); emitLeadingComments(restParam); emitStart(restParam);