--- /dev/null
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+const ts = require("typescript");
+const node_1 = require("../typeguard/node");
+function endsControlFlow(statement) {
+ return getControlFlowEnd(statement).end;
+}
+exports.endsControlFlow = endsControlFlow;
+const defaultControlFlowEnd = { statements: [], end: false };
+function getControlFlowEnd(statement) {
+ return node_1.isBlockLike(statement) ? handleBlock(statement) : getControlFlowEndWorker(statement);
+}
+exports.getControlFlowEnd = getControlFlowEnd;
+function getControlFlowEndWorker(statement) {
+ switch (statement.kind) {
+ case ts.SyntaxKind.ReturnStatement:
+ case ts.SyntaxKind.ThrowStatement:
+ case ts.SyntaxKind.ContinueStatement:
+ case ts.SyntaxKind.BreakStatement:
+ return { statements: [statement], end: true };
+ case ts.SyntaxKind.Block:
+ return handleBlock(statement);
+ case ts.SyntaxKind.ForStatement:
+ case ts.SyntaxKind.WhileStatement:
+ return handleForAndWhileStatement(statement);
+ case ts.SyntaxKind.ForOfStatement:
+ case ts.SyntaxKind.ForInStatement:
+ return handleForInOrOfStatement(statement);
+ case ts.SyntaxKind.DoStatement:
+ return matchBreakOrContinue(getControlFlowEndWorker(statement.statement), node_1.isBreakOrContinueStatement);
+ case ts.SyntaxKind.IfStatement:
+ return handleIfStatement(statement);
+ case ts.SyntaxKind.SwitchStatement:
+ return matchBreakOrContinue(handleSwitchStatement(statement), node_1.isBreakStatement);
+ case ts.SyntaxKind.TryStatement:
+ return handleTryStatement(statement);
+ case ts.SyntaxKind.LabeledStatement:
+ return matchLabel(getControlFlowEndWorker(statement.statement), statement.label);
+ case ts.SyntaxKind.WithStatement:
+ return getControlFlowEndWorker(statement.statement);
+ default:
+ return defaultControlFlowEnd;
+ }
+}
+function handleBlock(statement) {
+ const result = { statements: [], end: false };
+ for (const s of statement.statements) {
+ const current = getControlFlowEndWorker(s);
+ result.statements.push(...current.statements);
+ if (current.end) {
+ result.end = true;
+ break;
+ }
+ }
+ return result;
+}
+function handleForInOrOfStatement(statement) {
+ const end = matchBreakOrContinue(getControlFlowEndWorker(statement.statement), node_1.isBreakOrContinueStatement);
+ end.end = false;
+ return end;
+}
+function handleForAndWhileStatement(statement) {
+ const constantCondition = statement.kind === ts.SyntaxKind.WhileStatement
+ ? getConstantCondition(statement.expression)
+ : statement.condition === undefined || getConstantCondition(statement.condition);
+ if (constantCondition === false)
+ return defaultControlFlowEnd;
+ const end = matchBreakOrContinue(getControlFlowEndWorker(statement.statement), node_1.isBreakOrContinueStatement);
+ if (constantCondition === undefined)
+ end.end = false;
+ return end;
+}
+function getConstantCondition(node) {
+ switch (node.kind) {
+ case ts.SyntaxKind.TrueKeyword:
+ return true;
+ case ts.SyntaxKind.FalseKeyword:
+ return false;
+ default:
+ return;
+ }
+}
+function handleIfStatement(node) {
+ switch (getConstantCondition(node.expression)) {
+ case true:
+ return getControlFlowEndWorker(node.thenStatement);
+ case false:
+ return node.elseStatement === undefined
+ ? defaultControlFlowEnd
+ : getControlFlowEndWorker(node.elseStatement);
+ }
+ const then = getControlFlowEndWorker(node.thenStatement);
+ if (node.elseStatement === undefined)
+ return {
+ statements: then.statements,
+ end: false,
+ };
+ const elze = getControlFlowEndWorker(node.elseStatement);
+ return {
+ statements: [...then.statements, ...elze.statements],
+ end: then.end && elze.end,
+ };
+}
+function handleSwitchStatement(node) {
+ let hasDefault = false;
+ const result = {
+ statements: [],
+ end: false,
+ };
+ for (const clause of node.caseBlock.clauses) {
+ if (clause.kind === ts.SyntaxKind.DefaultClause)
+ hasDefault = true;
+ const current = handleBlock(clause);
+ result.end = current.end;
+ result.statements.push(...current.statements);
+ }
+ if (!hasDefault)
+ result.end = false;
+ return result;
+}
+function handleTryStatement(node) {
+ let finallyResult;
+ if (node.finallyBlock !== undefined) {
+ finallyResult = handleBlock(node.finallyBlock);
+ if (finallyResult.end)
+ return finallyResult;
+ }
+ const tryResult = handleBlock(node.tryBlock);
+ if (node.catchClause === undefined)
+ return { statements: finallyResult.statements.concat(tryResult.statements), end: tryResult.end };
+ const catchResult = handleBlock(node.catchClause.block);
+ return {
+ statements: tryResult.statements
+ .filter((s) => s.kind !== ts.SyntaxKind.ThrowStatement)
+ .concat(catchResult.statements, finallyResult === undefined ? [] : finallyResult.statements),
+ end: tryResult.end && catchResult.end,
+ };
+}
+function matchBreakOrContinue(current, pred) {
+ const result = {
+ statements: [],
+ end: current.end,
+ };
+ for (const statement of current.statements) {
+ if (pred(statement) && statement.label === undefined) {
+ result.end = false;
+ continue;
+ }
+ result.statements.push(statement);
+ }
+ return result;
+}
+function matchLabel(current, label) {
+ const result = {
+ statements: [],
+ end: current.end,
+ };
+ const labelText = label.text;
+ for (const statement of current.statements) {
+ switch (statement.kind) {
+ case ts.SyntaxKind.BreakStatement:
+ case ts.SyntaxKind.ContinueStatement:
+ if (statement.label !== undefined && statement.label.text === labelText) {
+ result.end = false;
+ continue;
+ }
+ }
+ result.statements.push(statement);
+ }
+ return result;
+}