2 Object.defineProperty(exports, "__esModule", { value: true });
3 const ts = require("typescript");
4 const node_1 = require("../typeguard/node");
5 function endsControlFlow(statement) {
6 return getControlFlowEnd(statement).end;
8 exports.endsControlFlow = endsControlFlow;
9 const defaultControlFlowEnd = { statements: [], end: false };
10 function getControlFlowEnd(statement) {
11 return node_1.isBlockLike(statement) ? handleBlock(statement) : getControlFlowEndWorker(statement);
13 exports.getControlFlowEnd = getControlFlowEnd;
14 function getControlFlowEndWorker(statement) {
15 switch (statement.kind) {
16 case ts.SyntaxKind.ReturnStatement:
17 case ts.SyntaxKind.ThrowStatement:
18 case ts.SyntaxKind.ContinueStatement:
19 case ts.SyntaxKind.BreakStatement:
20 return { statements: [statement], end: true };
21 case ts.SyntaxKind.Block:
22 return handleBlock(statement);
23 case ts.SyntaxKind.ForStatement:
24 case ts.SyntaxKind.WhileStatement:
25 return handleForAndWhileStatement(statement);
26 case ts.SyntaxKind.ForOfStatement:
27 case ts.SyntaxKind.ForInStatement:
28 return handleForInOrOfStatement(statement);
29 case ts.SyntaxKind.DoStatement:
30 return matchBreakOrContinue(getControlFlowEndWorker(statement.statement), node_1.isBreakOrContinueStatement);
31 case ts.SyntaxKind.IfStatement:
32 return handleIfStatement(statement);
33 case ts.SyntaxKind.SwitchStatement:
34 return matchBreakOrContinue(handleSwitchStatement(statement), node_1.isBreakStatement);
35 case ts.SyntaxKind.TryStatement:
36 return handleTryStatement(statement);
37 case ts.SyntaxKind.LabeledStatement:
38 return matchLabel(getControlFlowEndWorker(statement.statement), statement.label);
39 case ts.SyntaxKind.WithStatement:
40 return getControlFlowEndWorker(statement.statement);
42 return defaultControlFlowEnd;
45 function handleBlock(statement) {
46 const result = { statements: [], end: false };
47 for (const s of statement.statements) {
48 const current = getControlFlowEndWorker(s);
49 result.statements.push(...current.statements);
57 function handleForInOrOfStatement(statement) {
58 const end = matchBreakOrContinue(getControlFlowEndWorker(statement.statement), node_1.isBreakOrContinueStatement);
62 function handleForAndWhileStatement(statement) {
63 const constantCondition = statement.kind === ts.SyntaxKind.WhileStatement
64 ? getConstantCondition(statement.expression)
65 : statement.condition === undefined || getConstantCondition(statement.condition);
66 if (constantCondition === false)
67 return defaultControlFlowEnd;
68 const end = matchBreakOrContinue(getControlFlowEndWorker(statement.statement), node_1.isBreakOrContinueStatement);
69 if (constantCondition === undefined)
73 function getConstantCondition(node) {
75 case ts.SyntaxKind.TrueKeyword:
77 case ts.SyntaxKind.FalseKeyword:
83 function handleIfStatement(node) {
84 switch (getConstantCondition(node.expression)) {
86 return getControlFlowEndWorker(node.thenStatement);
88 return node.elseStatement === undefined
89 ? defaultControlFlowEnd
90 : getControlFlowEndWorker(node.elseStatement);
92 const then = getControlFlowEndWorker(node.thenStatement);
93 if (node.elseStatement === undefined)
95 statements: then.statements,
98 const elze = getControlFlowEndWorker(node.elseStatement);
100 statements: [...then.statements, ...elze.statements],
101 end: then.end && elze.end,
104 function handleSwitchStatement(node) {
105 let hasDefault = false;
110 for (const clause of node.caseBlock.clauses) {
111 if (clause.kind === ts.SyntaxKind.DefaultClause)
113 const current = handleBlock(clause);
114 result.end = current.end;
115 result.statements.push(...current.statements);
121 function handleTryStatement(node) {
123 if (node.finallyBlock !== undefined) {
124 finallyResult = handleBlock(node.finallyBlock);
125 if (finallyResult.end)
126 return finallyResult;
128 const tryResult = handleBlock(node.tryBlock);
129 if (node.catchClause === undefined)
130 return { statements: finallyResult.statements.concat(tryResult.statements), end: tryResult.end };
131 const catchResult = handleBlock(node.catchClause.block);
133 statements: tryResult.statements
134 .filter((s) => s.kind !== ts.SyntaxKind.ThrowStatement)
135 .concat(catchResult.statements, finallyResult === undefined ? [] : finallyResult.statements),
136 end: tryResult.end && catchResult.end,
139 function matchBreakOrContinue(current, pred) {
144 for (const statement of current.statements) {
145 if (pred(statement) && statement.label === undefined) {
149 result.statements.push(statement);
153 function matchLabel(current, label) {
158 const labelText = label.text;
159 for (const statement of current.statements) {
160 switch (statement.kind) {
161 case ts.SyntaxKind.BreakStatement:
162 case ts.SyntaxKind.ContinueStatement:
163 if (statement.label !== undefined && statement.label.text === labelText) {
168 result.statements.push(statement);