7e83773f05c319dc61147566f107b26d9881623b
[dotfiles/.git] / control-flow.js
1 "use strict";
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;
7 }
8 exports.endsControlFlow = endsControlFlow;
9 const defaultControlFlowEnd = { statements: [], end: false };
10 function getControlFlowEnd(statement) {
11     return node_1.isBlockLike(statement) ? handleBlock(statement) : getControlFlowEndWorker(statement);
12 }
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);
41         default:
42             return defaultControlFlowEnd;
43     }
44 }
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);
50         if (current.end) {
51             result.end = true;
52             break;
53         }
54     }
55     return result;
56 }
57 function handleForInOrOfStatement(statement) {
58     const end = matchBreakOrContinue(getControlFlowEndWorker(statement.statement), node_1.isBreakOrContinueStatement);
59     end.end = false;
60     return end;
61 }
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)
70         end.end = false;
71     return end;
72 }
73 function getConstantCondition(node) {
74     switch (node.kind) {
75         case ts.SyntaxKind.TrueKeyword:
76             return true;
77         case ts.SyntaxKind.FalseKeyword:
78             return false;
79         default:
80             return;
81     }
82 }
83 function handleIfStatement(node) {
84     switch (getConstantCondition(node.expression)) {
85         case true:
86             return getControlFlowEndWorker(node.thenStatement);
87         case false:
88             return node.elseStatement === undefined
89                 ? defaultControlFlowEnd
90                 : getControlFlowEndWorker(node.elseStatement);
91     }
92     const then = getControlFlowEndWorker(node.thenStatement);
93     if (node.elseStatement === undefined)
94         return {
95             statements: then.statements,
96             end: false,
97         };
98     const elze = getControlFlowEndWorker(node.elseStatement);
99     return {
100         statements: [...then.statements, ...elze.statements],
101         end: then.end && elze.end,
102     };
103 }
104 function handleSwitchStatement(node) {
105     let hasDefault = false;
106     const result = {
107         statements: [],
108         end: false,
109     };
110     for (const clause of node.caseBlock.clauses) {
111         if (clause.kind === ts.SyntaxKind.DefaultClause)
112             hasDefault = true;
113         const current = handleBlock(clause);
114         result.end = current.end;
115         result.statements.push(...current.statements);
116     }
117     if (!hasDefault)
118         result.end = false;
119     return result;
120 }
121 function handleTryStatement(node) {
122     let finallyResult;
123     if (node.finallyBlock !== undefined) {
124         finallyResult = handleBlock(node.finallyBlock);
125         if (finallyResult.end)
126             return finallyResult;
127     }
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);
132     return {
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,
137     };
138 }
139 function matchBreakOrContinue(current, pred) {
140     const result = {
141         statements: [],
142         end: current.end,
143     };
144     for (const statement of current.statements) {
145         if (pred(statement) && statement.label === undefined) {
146             result.end = false;
147             continue;
148         }
149         result.statements.push(statement);
150     }
151     return result;
152 }
153 function matchLabel(current, label) {
154     const result = {
155         statements: [],
156         end: current.end,
157     };
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) {
164                     result.end = false;
165                     continue;
166                 }
167         }
168         result.statements.push(statement);
169     }
170     return result;
171 }