Skip to content

Commit 3749de6

Browse files
committed
Dedicated isReachableFlowNode function to determine reachability
1 parent 971b0df commit 3749de6

File tree

1 file changed

+41
-7
lines changed

1 file changed

+41
-7
lines changed

src/compiler/checker.ts

Lines changed: 41 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -842,6 +842,7 @@ namespace ts {
842842
const flowLoopTypes: Type[][] = [];
843843
const sharedFlowNodes: FlowNode[] = [];
844844
const sharedFlowTypes: FlowType[] = [];
845+
const flowNodeReachable: (boolean | undefined)[] = [];
845846
const potentialThisCollisions: Node[] = [];
846847
const potentialNewTargetCollisions: Node[] = [];
847848
const awaitedTypeStack: number[] = [];
@@ -16991,6 +16992,40 @@ namespace ts {
1699116992
diagnostics.add(createFileDiagnostic(sourceFile, span.start, span.length, Diagnostics.The_containing_function_or_module_body_is_too_large_for_control_flow_analysis));
1699216993
}
1699316994

16995+
function isReachableFlowNode(flow: FlowNode) {
16996+
return isReachableFlowNodeWorker(flow, /*skipCacheCheck*/ false);
16997+
}
16998+
16999+
function isReachableFlowNodeWorker(flow: FlowNode, skipCacheCheck: boolean): boolean {
17000+
while (true) {
17001+
const flags = flow.flags;
17002+
if (flags & FlowFlags.Shared && !skipCacheCheck) {
17003+
const id = getFlowNodeId(flow);
17004+
const reachable = flowNodeReachable[id];
17005+
return reachable !== undefined ? reachable : (flowNodeReachable[id] = isReachableFlowNodeWorker(flow, /*skipCacheCheck*/ true));
17006+
}
17007+
if (flags & (FlowFlags.Assignment | FlowFlags.Condition | FlowFlags.SwitchClause | FlowFlags.ArrayMutation | FlowFlags.PreFinally | FlowFlags.AfterFinally)) {
17008+
flow = (<FlowAssignment | FlowCondition | FlowSwitchClause | FlowArrayMutation | PreFinallyFlow | AfterFinallyFlow>flow).antecedent;
17009+
}
17010+
else if (flags & FlowFlags.Call) {
17011+
const signature = getEffectsSignature((<FlowCall>flow).node);
17012+
if (signature && getReturnTypeOfSignature(signature).flags & TypeFlags.Never) {
17013+
return false;
17014+
}
17015+
flow = (<FlowCall>flow).antecedent;
17016+
}
17017+
else if (flags & FlowFlags.LoopLabel) {
17018+
flow = (<FlowLabel>flow).antecedents![0];
17019+
}
17020+
else if (flags & FlowFlags.BranchLabel) {
17021+
return every((<FlowLabel>flow).antecedents!, isReachableFlowNode);
17022+
}
17023+
else {
17024+
return true;
17025+
}
17026+
}
17027+
}
17028+
1699417029
function getFlowTypeOfReference(reference: Node, declaredType: Type, initialType = declaredType, flowContainer?: Node, couldBeUninitialized?: boolean) {
1699517030
let key: string | undefined;
1699617031
let keySet = false;
@@ -17146,11 +17181,11 @@ namespace ts {
1714617181
// Assignments only narrow the computed type if the declared type is a union type. Thus, we
1714717182
// only need to evaluate the assigned type if the declared type is a union type.
1714817183
if (isMatchingReference(reference, node)) {
17149-
const flowType = getTypeAtFlowNode(flow.antecedent);
17150-
if (flowType === unreachableNeverType) {
17151-
return flowType;
17184+
if (!isReachableFlowNode(flow)) {
17185+
return unreachableNeverType;
1715217186
}
1715317187
if (getAssignmentTargetKind(node) === AssignmentKind.Compound) {
17188+
const flowType = getTypeAtFlowNode(flow.antecedent);
1715417189
return createFlowType(getBaseTypeOfLiteralType(getTypeFromFlowType(flowType)), isIncomplete(flowType));
1715517190
}
1715617191
if (declaredType === autoType || declaredType === autoArrayType) {
@@ -17170,16 +17205,15 @@ namespace ts {
1717017205
// reference 'x.y.z', we may be at an assignment to 'x.y' or 'x'. In that case,
1717117206
// return the declared type.
1717217207
if (containsMatchingReference(reference, node)) {
17173-
const flowType = getTypeAtFlowNode(flow.antecedent);
17174-
if (flowType === unreachableNeverType) {
17175-
return flowType;
17208+
if (!isReachableFlowNode(flow)) {
17209+
return unreachableNeverType;
1717617210
}
1717717211
// A matching dotted name might also be an expando property on a function *expression*,
1717817212
// in which case we continue control flow analysis back to the function's declaration
1717917213
if (isVariableDeclaration(node) && (isInJSFile(node) || isVarConst(node))) {
1718017214
const init = getDeclaredExpandoInitializer(node);
1718117215
if (init && (init.kind === SyntaxKind.FunctionExpression || init.kind === SyntaxKind.ArrowFunction)) {
17182-
return flowType;
17216+
return getTypeAtFlowNode(flow.antecedent);
1718317217
}
1718417218
}
1718517219
return declaredType;

0 commit comments

Comments
 (0)