mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-15 03:23:08 -06:00
Emit of rest parameter for loop uses unique temporary variable name
This commit is contained in:
parent
cf3e3ac6b8
commit
28a73bc936
@ -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, <Identifier>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(<FunctionLikeDeclaration>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>identifier).text === name)) {
|
||||
|
||||
@ -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<VariableDeclaration> {
|
||||
var declarations = <NodeArray<VariableDeclaration>>[];
|
||||
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 = <BinaryExpression>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 = <BindingPattern>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 || <Identifier>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);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user