no-constant-condition.js 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. /**
  2. * @fileoverview Rule to flag use constant conditions
  3. * @author Christian Schulz <http://rndm.de>
  4. */
  5. "use strict";
  6. const { isConstant } = require("./utils/ast-utils");
  7. //------------------------------------------------------------------------------
  8. // Helpers
  9. //------------------------------------------------------------------------------
  10. //------------------------------------------------------------------------------
  11. // Rule Definition
  12. //------------------------------------------------------------------------------
  13. /** @type {import('../shared/types').Rule} */
  14. module.exports = {
  15. meta: {
  16. type: "problem",
  17. docs: {
  18. description: "Disallow constant expressions in conditions",
  19. recommended: true,
  20. url: "https://eslint.org/docs/rules/no-constant-condition"
  21. },
  22. schema: [
  23. {
  24. type: "object",
  25. properties: {
  26. checkLoops: {
  27. type: "boolean",
  28. default: true
  29. }
  30. },
  31. additionalProperties: false
  32. }
  33. ],
  34. messages: {
  35. unexpected: "Unexpected constant condition."
  36. }
  37. },
  38. create(context) {
  39. const options = context.options[0] || {},
  40. checkLoops = options.checkLoops !== false,
  41. loopSetStack = [];
  42. let loopsInCurrentScope = new Set();
  43. //--------------------------------------------------------------------------
  44. // Helpers
  45. //--------------------------------------------------------------------------
  46. /**
  47. * Tracks when the given node contains a constant condition.
  48. * @param {ASTNode} node The AST node to check.
  49. * @returns {void}
  50. * @private
  51. */
  52. function trackConstantConditionLoop(node) {
  53. if (node.test && isConstant(context.getScope(), node.test, true)) {
  54. loopsInCurrentScope.add(node);
  55. }
  56. }
  57. /**
  58. * Reports when the set contains the given constant condition node
  59. * @param {ASTNode} node The AST node to check.
  60. * @returns {void}
  61. * @private
  62. */
  63. function checkConstantConditionLoopInSet(node) {
  64. if (loopsInCurrentScope.has(node)) {
  65. loopsInCurrentScope.delete(node);
  66. context.report({ node: node.test, messageId: "unexpected" });
  67. }
  68. }
  69. /**
  70. * Reports when the given node contains a constant condition.
  71. * @param {ASTNode} node The AST node to check.
  72. * @returns {void}
  73. * @private
  74. */
  75. function reportIfConstant(node) {
  76. if (node.test && isConstant(context.getScope(), node.test, true)) {
  77. context.report({ node: node.test, messageId: "unexpected" });
  78. }
  79. }
  80. /**
  81. * Stores current set of constant loops in loopSetStack temporarily
  82. * and uses a new set to track constant loops
  83. * @returns {void}
  84. * @private
  85. */
  86. function enterFunction() {
  87. loopSetStack.push(loopsInCurrentScope);
  88. loopsInCurrentScope = new Set();
  89. }
  90. /**
  91. * Reports when the set still contains stored constant conditions
  92. * @returns {void}
  93. * @private
  94. */
  95. function exitFunction() {
  96. loopsInCurrentScope = loopSetStack.pop();
  97. }
  98. /**
  99. * Checks node when checkLoops option is enabled
  100. * @param {ASTNode} node The AST node to check.
  101. * @returns {void}
  102. * @private
  103. */
  104. function checkLoop(node) {
  105. if (checkLoops) {
  106. trackConstantConditionLoop(node);
  107. }
  108. }
  109. //--------------------------------------------------------------------------
  110. // Public
  111. //--------------------------------------------------------------------------
  112. return {
  113. ConditionalExpression: reportIfConstant,
  114. IfStatement: reportIfConstant,
  115. WhileStatement: checkLoop,
  116. "WhileStatement:exit": checkConstantConditionLoopInSet,
  117. DoWhileStatement: checkLoop,
  118. "DoWhileStatement:exit": checkConstantConditionLoopInSet,
  119. ForStatement: checkLoop,
  120. "ForStatement > .test": node => checkLoop(node.parent),
  121. "ForStatement:exit": checkConstantConditionLoopInSet,
  122. FunctionDeclaration: enterFunction,
  123. "FunctionDeclaration:exit": exitFunction,
  124. FunctionExpression: enterFunction,
  125. "FunctionExpression:exit": exitFunction,
  126. YieldExpression: () => loopsInCurrentScope.clear()
  127. };
  128. }
  129. };