Ohm-Management - Projektarbeit B-ME
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

no-constant-condition.js 7.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. /**
  2. * @fileoverview Rule to flag use constant conditions
  3. * @author Christian Schulz <http://rndm.de>
  4. */
  5. "use strict";
  6. //------------------------------------------------------------------------------
  7. // Rule Definition
  8. //------------------------------------------------------------------------------
  9. module.exports = {
  10. meta: {
  11. type: "problem",
  12. docs: {
  13. description: "disallow constant expressions in conditions",
  14. category: "Possible Errors",
  15. recommended: true,
  16. url: "https://eslint.org/docs/rules/no-constant-condition"
  17. },
  18. schema: [
  19. {
  20. type: "object",
  21. properties: {
  22. checkLoops: {
  23. type: "boolean"
  24. }
  25. },
  26. additionalProperties: false
  27. }
  28. ],
  29. messages: {
  30. unexpected: "Unexpected constant condition."
  31. }
  32. },
  33. create(context) {
  34. const options = context.options[0] || {},
  35. checkLoops = options.checkLoops !== false,
  36. loopSetStack = [];
  37. let loopsInCurrentScope = new Set();
  38. //--------------------------------------------------------------------------
  39. // Helpers
  40. //--------------------------------------------------------------------------
  41. /**
  42. * Checks if a branch node of LogicalExpression short circuits the whole condition
  43. * @param {ASTNode} node The branch of main condition which needs to be checked
  44. * @param {string} operator The operator of the main LogicalExpression.
  45. * @returns {boolean} true when condition short circuits whole condition
  46. */
  47. function isLogicalIdentity(node, operator) {
  48. switch (node.type) {
  49. case "Literal":
  50. return (operator === "||" && node.value === true) ||
  51. (operator === "&&" && node.value === false);
  52. case "UnaryExpression":
  53. return (operator === "&&" && node.operator === "void");
  54. case "LogicalExpression":
  55. return isLogicalIdentity(node.left, node.operator) ||
  56. isLogicalIdentity(node.right, node.operator);
  57. // no default
  58. }
  59. return false;
  60. }
  61. /**
  62. * Checks if a node has a constant truthiness value.
  63. * @param {ASTNode} node The AST node to check.
  64. * @param {boolean} inBooleanPosition `false` if checking branch of a condition.
  65. * `true` in all other cases
  66. * @returns {Bool} true when node's truthiness is constant
  67. * @private
  68. */
  69. function isConstant(node, inBooleanPosition) {
  70. switch (node.type) {
  71. case "Literal":
  72. case "ArrowFunctionExpression":
  73. case "FunctionExpression":
  74. case "ObjectExpression":
  75. case "ArrayExpression":
  76. return true;
  77. case "UnaryExpression":
  78. if (node.operator === "void") {
  79. return true;
  80. }
  81. return (node.operator === "typeof" && inBooleanPosition) ||
  82. isConstant(node.argument, true);
  83. case "BinaryExpression":
  84. return isConstant(node.left, false) &&
  85. isConstant(node.right, false) &&
  86. node.operator !== "in";
  87. case "LogicalExpression": {
  88. const isLeftConstant = isConstant(node.left, inBooleanPosition);
  89. const isRightConstant = isConstant(node.right, inBooleanPosition);
  90. const isLeftShortCircuit = (isLeftConstant && isLogicalIdentity(node.left, node.operator));
  91. const isRightShortCircuit = (isRightConstant && isLogicalIdentity(node.right, node.operator));
  92. return (isLeftConstant && isRightConstant) || isLeftShortCircuit || isRightShortCircuit;
  93. }
  94. case "AssignmentExpression":
  95. return (node.operator === "=") && isConstant(node.right, inBooleanPosition);
  96. case "SequenceExpression":
  97. return isConstant(node.expressions[node.expressions.length - 1], inBooleanPosition);
  98. // no default
  99. }
  100. return false;
  101. }
  102. /**
  103. * Tracks when the given node contains a constant condition.
  104. * @param {ASTNode} node The AST node to check.
  105. * @returns {void}
  106. * @private
  107. */
  108. function trackConstantConditionLoop(node) {
  109. if (node.test && isConstant(node.test, true)) {
  110. loopsInCurrentScope.add(node);
  111. }
  112. }
  113. /**
  114. * Reports when the set contains the given constant condition node
  115. * @param {ASTNode} node The AST node to check.
  116. * @returns {void}
  117. * @private
  118. */
  119. function checkConstantConditionLoopInSet(node) {
  120. if (loopsInCurrentScope.has(node)) {
  121. loopsInCurrentScope.delete(node);
  122. context.report({ node: node.test, messageId: "unexpected" });
  123. }
  124. }
  125. /**
  126. * Reports when the given node contains a constant condition.
  127. * @param {ASTNode} node The AST node to check.
  128. * @returns {void}
  129. * @private
  130. */
  131. function reportIfConstant(node) {
  132. if (node.test && isConstant(node.test, true)) {
  133. context.report({ node: node.test, messageId: "unexpected" });
  134. }
  135. }
  136. /**
  137. * Stores current set of constant loops in loopSetStack temporarily
  138. * and uses a new set to track constant loops
  139. * @returns {void}
  140. * @private
  141. */
  142. function enterFunction() {
  143. loopSetStack.push(loopsInCurrentScope);
  144. loopsInCurrentScope = new Set();
  145. }
  146. /**
  147. * Reports when the set still contains stored constant conditions
  148. * @param {ASTNode} node The AST node to check.
  149. * @returns {void}
  150. * @private
  151. */
  152. function exitFunction() {
  153. loopsInCurrentScope = loopSetStack.pop();
  154. }
  155. /**
  156. * Checks node when checkLoops option is enabled
  157. * @param {ASTNode} node The AST node to check.
  158. * @returns {void}
  159. * @private
  160. */
  161. function checkLoop(node) {
  162. if (checkLoops) {
  163. trackConstantConditionLoop(node);
  164. }
  165. }
  166. //--------------------------------------------------------------------------
  167. // Public
  168. //--------------------------------------------------------------------------
  169. return {
  170. ConditionalExpression: reportIfConstant,
  171. IfStatement: reportIfConstant,
  172. WhileStatement: checkLoop,
  173. "WhileStatement:exit": checkConstantConditionLoopInSet,
  174. DoWhileStatement: checkLoop,
  175. "DoWhileStatement:exit": checkConstantConditionLoopInSet,
  176. ForStatement: checkLoop,
  177. "ForStatement > .test": node => checkLoop(node.parent),
  178. "ForStatement:exit": checkConstantConditionLoopInSet,
  179. FunctionDeclaration: enterFunction,
  180. "FunctionDeclaration:exit": exitFunction,
  181. FunctionExpression: enterFunction,
  182. "FunctionExpression:exit": exitFunction,
  183. YieldExpression: () => loopsInCurrentScope.clear()
  184. };
  185. }
  186. };