mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-12 03:51:47 -06:00
Treat exhaustive switch statements like non-returning functions in CFA
This commit is contained in:
parent
3a89c8cc5c
commit
cc6e4938ae
@ -569,7 +569,7 @@ namespace ts {
|
||||
}
|
||||
// We create a return control flow graph for IIFEs and constructors. For constructors
|
||||
// we use the return control flow graph in strict property initialization checks.
|
||||
currentReturnTarget = containerFlags & ContainerFlags.IsFunctionLike && nodeIsPresent((<FunctionLikeDeclaration>node).body) ? createBranchLabel() : undefined;
|
||||
currentReturnTarget = isIIFE || node.kind === SyntaxKind.Constructor ? createBranchLabel() : undefined;
|
||||
currentBreakTarget = undefined;
|
||||
currentContinueTarget = undefined;
|
||||
activeLabels = undefined;
|
||||
@ -581,6 +581,7 @@ namespace ts {
|
||||
if (!(currentFlow.flags & FlowFlags.Unreachable) && containerFlags & ContainerFlags.IsFunctionLike && nodeIsPresent((<FunctionLikeDeclaration>node).body)) {
|
||||
node.flags |= NodeFlags.HasImplicitReturn;
|
||||
if (hasExplicitReturn) node.flags |= NodeFlags.HasExplicitReturn;
|
||||
(<FunctionLikeDeclaration>node).endFlowNode = currentFlow;
|
||||
}
|
||||
if (node.kind === SyntaxKind.SourceFile) {
|
||||
node.flags |= emitFlags;
|
||||
@ -589,7 +590,9 @@ namespace ts {
|
||||
if (currentReturnTarget) {
|
||||
addAntecedent(currentReturnTarget, currentFlow);
|
||||
currentFlow = finishFlowLabel(currentReturnTarget);
|
||||
(<FunctionLikeDeclaration>node).returnFlowNode = currentFlow;
|
||||
if (node.kind === SyntaxKind.Constructor) {
|
||||
(<ConstructorDeclaration>node).returnFlowNode = currentFlow;
|
||||
}
|
||||
}
|
||||
if (!isIIFE) {
|
||||
currentFlow = saveCurrentFlow;
|
||||
|
||||
@ -16996,16 +16996,19 @@ namespace ts {
|
||||
return isReachableFlowNodeWorker(flow, /*skipCacheCheck*/ false);
|
||||
}
|
||||
|
||||
function isReachableFlowNodeWorker(flow: FlowNode, skipCacheCheck: boolean): boolean {
|
||||
function isReachableFlowNodeWorker(flow: FlowNode, noCacheCheck: boolean): boolean {
|
||||
while (true) {
|
||||
const flags = flow.flags;
|
||||
if (flags & FlowFlags.Shared && !skipCacheCheck) {
|
||||
const id = getFlowNodeId(flow);
|
||||
const reachable = flowNodeReachable[id];
|
||||
return reachable !== undefined ? reachable : (flowNodeReachable[id] = isReachableFlowNodeWorker(flow, /*skipCacheCheck*/ true));
|
||||
if (flags & FlowFlags.Shared | flags & FlowFlags.SwitchClause) {
|
||||
if (!noCacheCheck) {
|
||||
const id = getFlowNodeId(flow);
|
||||
const reachable = flowNodeReachable[id];
|
||||
return reachable !== undefined ? reachable : (flowNodeReachable[id] = isReachableFlowNodeWorker(flow, /*skipCacheCheck*/ true));
|
||||
}
|
||||
noCacheCheck = false;
|
||||
}
|
||||
if (flags & (FlowFlags.Assignment | FlowFlags.Condition | FlowFlags.SwitchClause | FlowFlags.ArrayMutation | FlowFlags.PreFinally | FlowFlags.AfterFinally)) {
|
||||
flow = (<FlowAssignment | FlowCondition | FlowSwitchClause | FlowArrayMutation | PreFinallyFlow | AfterFinallyFlow>flow).antecedent;
|
||||
if (flags & (FlowFlags.Assignment | FlowFlags.Condition | FlowFlags.ArrayMutation | FlowFlags.PreFinally | FlowFlags.AfterFinally)) {
|
||||
flow = (<FlowAssignment | FlowCondition | FlowArrayMutation | PreFinallyFlow | AfterFinallyFlow>flow).antecedent;
|
||||
}
|
||||
else if (flags & FlowFlags.Call) {
|
||||
const signature = getEffectsSignature((<FlowCall>flow).node);
|
||||
@ -17014,14 +17017,20 @@ namespace ts {
|
||||
}
|
||||
flow = (<FlowCall>flow).antecedent;
|
||||
}
|
||||
else if (flags & FlowFlags.BranchLabel) {
|
||||
return some((<FlowLabel>flow).antecedents!, isReachableFlowNode);
|
||||
}
|
||||
else if (flags & FlowFlags.LoopLabel) {
|
||||
flow = (<FlowLabel>flow).antecedents![0];
|
||||
}
|
||||
else if (flags & FlowFlags.BranchLabel) {
|
||||
return every((<FlowLabel>flow).antecedents!, isReachableFlowNode);
|
||||
else if (flags & FlowFlags.SwitchClause) {
|
||||
if ((<FlowSwitchClause>flow).clauseStart === (<FlowSwitchClause>flow).clauseEnd && isExhaustiveSwitchStatement((<FlowSwitchClause>flow).switchStatement)) {
|
||||
return false;
|
||||
}
|
||||
flow = (<FlowSwitchClause>flow).antecedent;
|
||||
}
|
||||
else {
|
||||
return true;
|
||||
return !(flags & FlowFlags.Unreachable);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -17044,7 +17053,11 @@ namespace ts {
|
||||
// on empty arrays are possible without implicit any errors and new element types can be inferred without
|
||||
// type mismatch errors.
|
||||
const resultType = getObjectFlags(evolvedType) & ObjectFlags.EvolvingArray && isEvolvingArrayOperationTarget(reference) ? autoArrayType : finalizeEvolvingArrayType(evolvedType);
|
||||
if (resultType === unreachableNeverType || reference.parent && reference.parent.kind === SyntaxKind.NonNullExpression && getTypeWithFacts(resultType, TypeFacts.NEUndefinedOrNull).flags & TypeFlags.Never) {
|
||||
if (resultType === unreachableNeverType) {
|
||||
error(reference, Diagnostics.Unreachable_code_detected);
|
||||
return declaredType;
|
||||
}
|
||||
if (reference.parent && reference.parent.kind === SyntaxKind.NonNullExpression && getTypeWithFacts(resultType, TypeFacts.NEUndefinedOrNull).flags & TypeFlags.Never) {
|
||||
return declaredType;
|
||||
}
|
||||
return resultType;
|
||||
@ -23804,9 +23817,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
function functionHasImplicitReturn(func: FunctionLikeDeclaration) {
|
||||
return !!(func.flags & NodeFlags.HasImplicitReturn &&
|
||||
!some((<Block>func.body).statements, s => s.kind === SyntaxKind.SwitchStatement && isExhaustiveSwitchStatement(<SwitchStatement>s)) &&
|
||||
!(func.returnFlowNode && !isReachableFlowNode(func.returnFlowNode)));
|
||||
return func.endFlowNode && isReachableFlowNode(func.endFlowNode);
|
||||
}
|
||||
|
||||
/** NOTE: Return value of `[]` means a different thing than `undefined`. `[]` means func returns `void`, `undefined` means it returns `never`. */
|
||||
|
||||
@ -1040,7 +1040,7 @@ namespace ts {
|
||||
questionToken?: QuestionToken;
|
||||
exclamationToken?: ExclamationToken;
|
||||
body?: Block | Expression;
|
||||
/* @internal */ returnFlowNode?: FlowNode;
|
||||
/* @internal */ endFlowNode?: FlowNode;
|
||||
}
|
||||
|
||||
export type FunctionLikeDeclaration =
|
||||
@ -1086,6 +1086,7 @@ namespace ts {
|
||||
kind: SyntaxKind.Constructor;
|
||||
parent: ClassLikeDeclaration;
|
||||
body?: FunctionBody;
|
||||
/* @internal */ returnFlowNode?: FlowNode;
|
||||
}
|
||||
|
||||
/** For when we encounter a semicolon in a class declaration. ES6 allows these as class elements. */
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user