Allow assignments to a narrowable reference to be considered narrowable
This commit is contained in:
parent
03b658035d
commit
b9db6413bf
|
@ -837,7 +837,8 @@ namespace ts {
|
|||
function isNarrowableReference(expr: Expression): boolean {
|
||||
return expr.kind === SyntaxKind.Identifier || expr.kind === SyntaxKind.ThisKeyword || expr.kind === SyntaxKind.SuperKeyword ||
|
||||
(isPropertyAccessExpression(expr) || isNonNullExpression(expr) || isParenthesizedExpression(expr)) && isNarrowableReference(expr.expression) ||
|
||||
isElementAccessExpression(expr) && isStringOrNumericLiteralLike(expr.argumentExpression) && isNarrowableReference(expr.expression);
|
||||
isElementAccessExpression(expr) && isStringOrNumericLiteralLike(expr.argumentExpression) && isNarrowableReference(expr.expression) ||
|
||||
isAssignmentExpression(expr) && isNarrowableReference(expr.left);
|
||||
}
|
||||
|
||||
function containsNarrowableReference(expr: Expression): boolean {
|
||||
|
|
|
@ -20131,6 +20131,11 @@ namespace ts {
|
|||
case SyntaxKind.ParenthesizedExpression:
|
||||
case SyntaxKind.NonNullExpression:
|
||||
return isMatchingReference(source, (target as NonNullExpression | ParenthesizedExpression).expression);
|
||||
case SyntaxKind.BinaryExpression:
|
||||
if (isAssignmentExpression(target)) {
|
||||
return isMatchingReference(source, target.left);
|
||||
}
|
||||
break;
|
||||
}
|
||||
switch (source.kind) {
|
||||
case SyntaxKind.Identifier:
|
||||
|
|
|
@ -11,6 +11,10 @@ namespace Debug {
|
|||
type MethodDeclaration = Node;
|
||||
type Expression = Node;
|
||||
type SourceFile = Node;
|
||||
type VariableDeclaration = Node;
|
||||
type BindingElement = Node;
|
||||
type CallExpression = Node;
|
||||
type BinaryExpression = Node;
|
||||
|
||||
interface SwitchStatement extends Node {
|
||||
caseBlock: CaseBlock;
|
||||
|
@ -59,8 +63,6 @@ namespace Debug {
|
|||
}
|
||||
|
||||
type FlowNode =
|
||||
| AfterFinallyFlow
|
||||
| PreFinallyFlow
|
||||
| FlowStart
|
||||
| FlowLabel
|
||||
| FlowAssignment
|
||||
|
@ -76,14 +78,6 @@ namespace Debug {
|
|||
id?: number;
|
||||
}
|
||||
|
||||
interface AfterFinallyFlow extends FlowNodeBase {
|
||||
antecedent: FlowNode;
|
||||
}
|
||||
|
||||
interface PreFinallyFlow extends FlowNodeBase {
|
||||
antecedent: FlowNode;
|
||||
}
|
||||
|
||||
interface FlowStart extends FlowNodeBase {
|
||||
node?: FunctionExpression | ArrowFunction | MethodDeclaration;
|
||||
}
|
||||
|
@ -93,12 +87,12 @@ namespace Debug {
|
|||
}
|
||||
|
||||
interface FlowAssignment extends FlowNodeBase {
|
||||
node: Expression;
|
||||
node: Expression | VariableDeclaration | BindingElement;
|
||||
antecedent: FlowNode;
|
||||
}
|
||||
|
||||
interface FlowCall extends FlowNodeBase {
|
||||
node: Expression;
|
||||
node: CallExpression;
|
||||
antecedent: FlowNode;
|
||||
}
|
||||
|
||||
|
@ -115,7 +109,7 @@ namespace Debug {
|
|||
}
|
||||
|
||||
interface FlowArrayMutation extends FlowNodeBase {
|
||||
node: Expression;
|
||||
node: CallExpression | BinaryExpression;
|
||||
antecedent: FlowNode;
|
||||
}
|
||||
|
||||
|
@ -192,6 +186,7 @@ namespace Debug {
|
|||
lane: number;
|
||||
endLane: number;
|
||||
level: number;
|
||||
circular: boolean | "circularity";
|
||||
}
|
||||
|
||||
interface FlowGraphEdge {
|
||||
|
@ -217,8 +212,9 @@ namespace Debug {
|
|||
const links: Record<number, FlowGraphNode> = Object.create(/*o*/ null); // eslint-disable-line no-null/no-null
|
||||
const nodes: FlowGraphNode[] = [];
|
||||
const edges: FlowGraphEdge[] = [];
|
||||
const root = buildGraphNode(flowNode);
|
||||
const root = buildGraphNode(flowNode, new Set());
|
||||
for (const node of nodes) {
|
||||
node.text = renderFlowNode(node.flowNode, node.circular);
|
||||
computeLevel(node);
|
||||
}
|
||||
|
||||
|
@ -263,26 +259,43 @@ namespace Debug {
|
|||
return parents;
|
||||
}
|
||||
|
||||
function buildGraphNode(flowNode: FlowNode) {
|
||||
function buildGraphNode(flowNode: FlowNode, seen: Set<FlowNode>): FlowGraphNode {
|
||||
const id = getDebugFlowNodeId(flowNode);
|
||||
let graphNode = links[id];
|
||||
if (graphNode && seen.has(flowNode)) {
|
||||
graphNode.circular = true;
|
||||
graphNode = {
|
||||
id: -1,
|
||||
flowNode,
|
||||
edges: [],
|
||||
text: "",
|
||||
lane: -1,
|
||||
endLane: -1,
|
||||
level: -1,
|
||||
circular: "circularity"
|
||||
};
|
||||
nodes.push(graphNode);
|
||||
return graphNode;
|
||||
}
|
||||
seen.add(flowNode);
|
||||
if (!graphNode) {
|
||||
links[id] = graphNode = { id, flowNode, edges: [], text: renderFlowNode(flowNode), lane: -1, endLane: -1, level: -1 };
|
||||
links[id] = graphNode = { id, flowNode, edges: [], text: "", lane: -1, endLane: -1, level: -1, circular: false };
|
||||
nodes.push(graphNode);
|
||||
if (hasAntecedents(flowNode)) {
|
||||
for (const antecedent of flowNode.antecedents) {
|
||||
buildGraphEdge(graphNode, antecedent);
|
||||
buildGraphEdge(graphNode, antecedent, seen);
|
||||
}
|
||||
}
|
||||
else if (hasAntecedent(flowNode)) {
|
||||
buildGraphEdge(graphNode, flowNode.antecedent);
|
||||
buildGraphEdge(graphNode, flowNode.antecedent, seen);
|
||||
}
|
||||
}
|
||||
seen.delete(flowNode);
|
||||
return graphNode;
|
||||
}
|
||||
|
||||
function buildGraphEdge(source: FlowGraphNode, antecedent: FlowNode) {
|
||||
const target = buildGraphNode(antecedent);
|
||||
function buildGraphEdge(source: FlowGraphNode, antecedent: FlowNode, seen: Set<FlowNode>) {
|
||||
const target = buildGraphNode(antecedent, seen);
|
||||
const edge: FlowGraphEdge = { source, target };
|
||||
edges.push(edge);
|
||||
source.edges.push(edge);
|
||||
|
@ -353,8 +366,11 @@ namespace Debug {
|
|||
return getSourceTextOfNodeFromSourceFile(sourceFile, node, /*includeTrivia*/ false);
|
||||
}
|
||||
|
||||
function renderFlowNode(flowNode: FlowNode) {
|
||||
function renderFlowNode(flowNode: FlowNode, circular: boolean | "circularity") {
|
||||
let text = getHeader(flowNode.flags);
|
||||
if (circular) {
|
||||
text = `${text}#${getDebugFlowNodeId(flowNode)}`;
|
||||
}
|
||||
if (hasNode(flowNode)) {
|
||||
if (flowNode.node) {
|
||||
text += ` (${getNodeText(flowNode.node)})`;
|
||||
|
@ -373,7 +389,7 @@ namespace Debug {
|
|||
}
|
||||
text += ` (${clauses.join(", ")})`;
|
||||
}
|
||||
return text;
|
||||
return circular === "circularity" ? `Circular(${text})` : text;
|
||||
}
|
||||
|
||||
function renderGraph() {
|
||||
|
|
|
@ -9,7 +9,14 @@ x; // number
|
|||
x = true;
|
||||
(x = "", obj).foo = (x = x.length);
|
||||
x; // number
|
||||
|
||||
|
||||
// https://github.com/microsoft/TypeScript/issues/35484
|
||||
type D = { done: true, value: 1 } | { done: false, value: 2 };
|
||||
declare function fn(): D;
|
||||
let o: D;
|
||||
if ((o = fn()).done) {
|
||||
const y: 1 = o.value;
|
||||
}
|
||||
|
||||
//// [controlFlowAssignmentExpression.js]
|
||||
var x;
|
||||
|
@ -20,3 +27,7 @@ x; // number
|
|||
x = true;
|
||||
(x = "", obj).foo = (x = x.length);
|
||||
x; // number
|
||||
var o;
|
||||
if ((o = fn()).done) {
|
||||
var y = o.value;
|
||||
}
|
||||
|
|
|
@ -31,3 +31,31 @@ x = true;
|
|||
x; // number
|
||||
>x : Symbol(x, Decl(controlFlowAssignmentExpression.ts, 0, 3))
|
||||
|
||||
// https://github.com/microsoft/TypeScript/issues/35484
|
||||
type D = { done: true, value: 1 } | { done: false, value: 2 };
|
||||
>D : Symbol(D, Decl(controlFlowAssignmentExpression.ts, 9, 2))
|
||||
>done : Symbol(done, Decl(controlFlowAssignmentExpression.ts, 12, 10))
|
||||
>value : Symbol(value, Decl(controlFlowAssignmentExpression.ts, 12, 22))
|
||||
>done : Symbol(done, Decl(controlFlowAssignmentExpression.ts, 12, 37))
|
||||
>value : Symbol(value, Decl(controlFlowAssignmentExpression.ts, 12, 50))
|
||||
|
||||
declare function fn(): D;
|
||||
>fn : Symbol(fn, Decl(controlFlowAssignmentExpression.ts, 12, 62))
|
||||
>D : Symbol(D, Decl(controlFlowAssignmentExpression.ts, 9, 2))
|
||||
|
||||
let o: D;
|
||||
>o : Symbol(o, Decl(controlFlowAssignmentExpression.ts, 14, 3))
|
||||
>D : Symbol(D, Decl(controlFlowAssignmentExpression.ts, 9, 2))
|
||||
|
||||
if ((o = fn()).done) {
|
||||
>(o = fn()).done : Symbol(done, Decl(controlFlowAssignmentExpression.ts, 12, 10), Decl(controlFlowAssignmentExpression.ts, 12, 37))
|
||||
>o : Symbol(o, Decl(controlFlowAssignmentExpression.ts, 14, 3))
|
||||
>fn : Symbol(fn, Decl(controlFlowAssignmentExpression.ts, 12, 62))
|
||||
>done : Symbol(done, Decl(controlFlowAssignmentExpression.ts, 12, 10), Decl(controlFlowAssignmentExpression.ts, 12, 37))
|
||||
|
||||
const y: 1 = o.value;
|
||||
>y : Symbol(y, Decl(controlFlowAssignmentExpression.ts, 16, 9))
|
||||
>o.value : Symbol(value, Decl(controlFlowAssignmentExpression.ts, 12, 22))
|
||||
>o : Symbol(o, Decl(controlFlowAssignmentExpression.ts, 14, 3))
|
||||
>value : Symbol(value, Decl(controlFlowAssignmentExpression.ts, 12, 22))
|
||||
}
|
||||
|
|
|
@ -45,3 +45,34 @@ x = true;
|
|||
x; // number
|
||||
>x : number
|
||||
|
||||
// https://github.com/microsoft/TypeScript/issues/35484
|
||||
type D = { done: true, value: 1 } | { done: false, value: 2 };
|
||||
>D : D
|
||||
>done : true
|
||||
>true : true
|
||||
>value : 1
|
||||
>done : false
|
||||
>false : false
|
||||
>value : 2
|
||||
|
||||
declare function fn(): D;
|
||||
>fn : () => D
|
||||
|
||||
let o: D;
|
||||
>o : D
|
||||
|
||||
if ((o = fn()).done) {
|
||||
>(o = fn()).done : boolean
|
||||
>(o = fn()) : D
|
||||
>o = fn() : D
|
||||
>o : D
|
||||
>fn() : D
|
||||
>fn : () => D
|
||||
>done : boolean
|
||||
|
||||
const y: 1 = o.value;
|
||||
>y : 1
|
||||
>o.value : 1
|
||||
>o : { done: true; value: 1; }
|
||||
>value : 1
|
||||
}
|
||||
|
|
|
@ -8,3 +8,11 @@ x; // number
|
|||
x = true;
|
||||
(x = "", obj).foo = (x = x.length);
|
||||
x; // number
|
||||
|
||||
// https://github.com/microsoft/TypeScript/issues/35484
|
||||
type D = { done: true, value: 1 } | { done: false, value: 2 };
|
||||
declare function fn(): D;
|
||||
let o: D;
|
||||
if ((o = fn()).done) {
|
||||
const y: 1 = o.value;
|
||||
}
|
Loading…
Reference in a new issue