From e6b588a956d5046469dbfa240d35ab1cc09c0046 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Tue, 11 Oct 2016 10:14:06 -0700 Subject: [PATCH] Support parentheses and comma operator with evolving arrays --- src/compiler/binder.ts | 10 +++++----- src/compiler/checker.ts | 34 ++++++++++++++++++++++++---------- src/compiler/types.ts | 2 +- 3 files changed, 30 insertions(+), 16 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 1b121394a8c..6926403d9bb 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -760,7 +760,7 @@ namespace ts { }; } - function createFlowArrayMutation(antecedent: FlowNode, node: Expression): FlowNode { + function createFlowArrayMutation(antecedent: FlowNode, node: CallExpression | BinaryExpression): FlowNode { setFlowNodeReferenced(antecedent); return { flags: FlowFlags.ArrayMutation, @@ -1151,8 +1151,8 @@ namespace ts { bindAssignmentTargetFlow(node.left); if (node.left.kind === SyntaxKind.ElementAccessExpression) { const elementAccess = node.left; - if (isNarrowableReference(elementAccess.expression)) { - currentFlow = createFlowArrayMutation(currentFlow, elementAccess.expression); + if (isNarrowableOperand(elementAccess.expression)) { + currentFlow = createFlowArrayMutation(currentFlow, node); } } } @@ -1217,8 +1217,8 @@ namespace ts { } if (node.expression.kind === SyntaxKind.PropertyAccessExpression) { const propertyAccess = node.expression; - if (isNarrowableReference(propertyAccess.expression) && propertyAccess.name.text === "push") { - currentFlow = createFlowArrayMutation(currentFlow, propertyAccess.expression); + if (isNarrowableOperand(propertyAccess.expression) && propertyAccess.name.text === "push") { + currentFlow = createFlowArrayMutation(currentFlow, node); } } } diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 2ff5700082a..379d61850e4 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -8424,6 +8424,14 @@ namespace ts { return node; } + function getReferenceParent(node: Node): Node { + const parent = node.parent; + return parent.kind === SyntaxKind.ParenthesizedExpression || + parent.kind === SyntaxKind.BinaryExpression && (parent).operatorToken.kind === SyntaxKind.EqualsToken && (parent).left === node || + parent.kind === SyntaxKind.BinaryExpression && (parent).operatorToken.kind === SyntaxKind.CommaToken && (parent).right === node ? + getReferenceParent(parent) : parent; + } + function getTypeOfSwitchClause(clause: CaseClause | DefaultClause) { if (clause.kind === SyntaxKind.CaseClause) { const caseType = getRegularTypeOfLiteralType(checkExpression((clause).expression)); @@ -8556,15 +8564,16 @@ namespace ts { // Return true if the given node is 'x' in an 'x.push(value)' operation. function isPushCallTarget(node: Node) { - return node.parent.kind === SyntaxKind.PropertyAccessExpression && - (node.parent).name.text === "push" && - node.parent.parent.kind === SyntaxKind.CallExpression; + const parent = getReferenceParent(node); + return parent.kind === SyntaxKind.PropertyAccessExpression && + (parent).name.text === "push" && + parent.parent.kind === SyntaxKind.CallExpression; } // Return true if the given node is 'x' in an 'x[n] = value' operation, where 'n' is an // expression of type any, undefined, or a number-like type. function isElementAssignmentTarget(node: Node) { - const parent = node.parent; + const parent = getReferenceParent(node); return parent.kind === SyntaxKind.ElementAccessExpression && (parent).expression === node && parent.parent.kind === SyntaxKind.BinaryExpression && @@ -8696,19 +8705,24 @@ namespace ts { function getTypeAtFlowArrayMutation(flow: FlowArrayMutation): FlowType { const node = flow.node; - if (isMatchingReference(reference, node)) { + const expr = node.kind === SyntaxKind.CallExpression ? + ((node).expression).expression : + ((node).left).expression; + if (isMatchingReference(reference, getReferenceCandidate(expr))) { const flowType = getTypeAtFlowNode(flow.antecedent); const type = getTypeFromFlowType(flowType); if (isEvolvingArrayType(type)) { - const parent = node.parent; let evolvedType = type; - if (parent.kind === SyntaxKind.PropertyAccessExpression) { - for (const arg of (parent.parent).arguments) { + if (node.kind === SyntaxKind.CallExpression) { + for (const arg of (node).arguments) { evolvedType = addEvolvingArrayElementType(evolvedType, arg); } } - else if (isTypeAnyOrAllConstituentTypesHaveKind(checkExpression((parent).argumentExpression), TypeFlags.NumberLike)) { - evolvedType = addEvolvingArrayElementType(evolvedType, (parent.parent).right); + else { + const indexType = checkExpression(((node).left).argumentExpression); + if (isTypeAnyOrAllConstituentTypesHaveKind(indexType, TypeFlags.NumberLike | TypeFlags.Undefined)) { + evolvedType = addEvolvingArrayElementType(evolvedType, (node).right); + } } return evolvedType === type ? flowType : createFlowType(evolvedType, isIncomplete(flowType)); } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 9759c2ab540..5c32649d97c 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -1955,7 +1955,7 @@ namespace ts { // FlowArrayMutation represents a node potentially mutates an array, i.e. an // operation of the form 'x.push(value)' or 'x[n] = value'. export interface FlowArrayMutation extends FlowNode { - node: Expression; + node: CallExpression | BinaryExpression; antecedent: FlowNode; }