control-flow.js 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  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. }