mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-05-24 11:43:18 -05:00
No implicit returns following exhaustive switch statements
This commit is contained in:
@@ -650,6 +650,11 @@ namespace ts {
|
||||
return isNarrowableReference(expr);
|
||||
}
|
||||
|
||||
function isNarrowingSwitchStatement(switchStatement: SwitchStatement) {
|
||||
const expr = switchStatement.expression;
|
||||
return expr.kind === SyntaxKind.PropertyAccessExpression && isNarrowableReference((<PropertyAccessExpression>expr).expression);
|
||||
}
|
||||
|
||||
function createBranchLabel(): FlowLabel {
|
||||
return {
|
||||
flags: FlowFlags.BranchLabel,
|
||||
@@ -699,8 +704,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
function createFlowSwitchClause(antecedent: FlowNode, switchStatement: SwitchStatement, clauseStart: number, clauseEnd: number): FlowNode {
|
||||
const expr = switchStatement.expression;
|
||||
if (expr.kind !== SyntaxKind.PropertyAccessExpression || !isNarrowableReference((<PropertyAccessExpression>expr).expression)) {
|
||||
if (!isNarrowingSwitchStatement(switchStatement)) {
|
||||
return antecedent;
|
||||
}
|
||||
setFlowNodeReferenced(antecedent);
|
||||
@@ -939,6 +943,9 @@ namespace ts {
|
||||
bind(node.caseBlock);
|
||||
addAntecedent(postSwitchLabel, currentFlow);
|
||||
const hasDefault = forEach(node.caseBlock.clauses, c => c.kind === SyntaxKind.DefaultClause);
|
||||
// We mark a switch statement as possibly exhaustive if it has no default clause and if all
|
||||
// case clauses have unreachable end points (e.g. they all return).
|
||||
node.possiblyExhaustive = !hasDefault && !postSwitchLabel.antecedents;
|
||||
if (!hasDefault) {
|
||||
addAntecedent(postSwitchLabel, createFlowSwitchClause(preSwitchCaseFlow, node, 0, 0));
|
||||
}
|
||||
|
||||
@@ -11808,10 +11808,42 @@ namespace ts {
|
||||
return aggregatedTypes;
|
||||
}
|
||||
|
||||
function isExhaustiveSwitchStatement(node: SwitchStatement): boolean {
|
||||
const expr = node.expression;
|
||||
if (!node.possiblyExhaustive || expr.kind !== SyntaxKind.PropertyAccessExpression) {
|
||||
return false;
|
||||
}
|
||||
const type = checkExpression((<PropertyAccessExpression>expr).expression);
|
||||
if (!(type.flags & TypeFlags.Union)) {
|
||||
return false;
|
||||
}
|
||||
const propName = (<PropertyAccessExpression>expr).name.text;
|
||||
const propType = getTypeOfPropertyOfType(type, propName);
|
||||
if (!propType || !isStringLiteralUnionType(propType)) {
|
||||
return false;
|
||||
}
|
||||
const switchTypes = getSwitchClauseTypes(node);
|
||||
if (!switchTypes.length) {
|
||||
return false;
|
||||
}
|
||||
return eachTypeContainedIn(propType, switchTypes);
|
||||
}
|
||||
|
||||
function functionHasImplicitReturn(func: FunctionLikeDeclaration) {
|
||||
if (!(func.flags & NodeFlags.HasImplicitReturn)) {
|
||||
return false;
|
||||
}
|
||||
const lastStatement = lastOrUndefined((<Block>func.body).statements);
|
||||
if (lastStatement && lastStatement.kind === SyntaxKind.SwitchStatement && isExhaustiveSwitchStatement(<SwitchStatement>lastStatement)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function checkAndAggregateReturnExpressionTypes(func: FunctionLikeDeclaration, contextualMapper: TypeMapper): Type[] {
|
||||
const isAsync = isAsyncFunctionLike(func);
|
||||
const aggregatedTypes: Type[] = [];
|
||||
let hasReturnWithNoExpression = !!(func.flags & NodeFlags.HasImplicitReturn);
|
||||
let hasReturnWithNoExpression = functionHasImplicitReturn(func);
|
||||
let hasReturnOfTypeNever = false;
|
||||
forEachReturnStatement(<Block>func.body, returnStatement => {
|
||||
const expr = returnStatement.expression;
|
||||
@@ -11868,7 +11900,7 @@ namespace ts {
|
||||
|
||||
// If all we have is a function signature, or an arrow function with an expression body, then there is nothing to check.
|
||||
// also if HasImplicitReturn flag is not set this means that all codepaths in function body end with return or throw
|
||||
if (nodeIsMissing(func.body) || func.body.kind !== SyntaxKind.Block || !(func.flags & NodeFlags.HasImplicitReturn)) {
|
||||
if (nodeIsMissing(func.body) || func.body.kind !== SyntaxKind.Block || !functionHasImplicitReturn(func)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -1188,6 +1188,7 @@ namespace ts {
|
||||
export interface SwitchStatement extends Statement {
|
||||
expression: Expression;
|
||||
caseBlock: CaseBlock;
|
||||
possiblyExhaustive?: boolean;
|
||||
}
|
||||
|
||||
// @kind(SyntaxKind.CaseBlock)
|
||||
|
||||
Reference in New Issue
Block a user