| /** |
| * @fileoverview Rule to flag use constant conditions |
| * @author Christian Schulz <http://rndm.de> |
| */ |
| |
| "use strict"; |
| |
| const { isConstant } = require("./utils/ast-utils"); |
| |
| //------------------------------------------------------------------------------ |
| // Helpers |
| //------------------------------------------------------------------------------ |
| |
| //------------------------------------------------------------------------------ |
| // Rule Definition |
| //------------------------------------------------------------------------------ |
| |
| /** @type {import('../shared/types').Rule} */ |
| module.exports = { |
| meta: { |
| type: "problem", |
| |
| docs: { |
| description: "Disallow constant expressions in conditions", |
| recommended: true, |
| url: "https://eslint.org/docs/rules/no-constant-condition" |
| }, |
| |
| schema: [ |
| { |
| type: "object", |
| properties: { |
| checkLoops: { |
| type: "boolean", |
| default: true |
| } |
| }, |
| additionalProperties: false |
| } |
| ], |
| |
| messages: { |
| unexpected: "Unexpected constant condition." |
| } |
| }, |
| |
| create(context) { |
| const options = context.options[0] || {}, |
| checkLoops = options.checkLoops !== false, |
| loopSetStack = []; |
| |
| let loopsInCurrentScope = new Set(); |
| |
| //-------------------------------------------------------------------------- |
| // Helpers |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * Tracks when the given node contains a constant condition. |
| * @param {ASTNode} node The AST node to check. |
| * @returns {void} |
| * @private |
| */ |
| function trackConstantConditionLoop(node) { |
| if (node.test && isConstant(context.getScope(), node.test, true)) { |
| loopsInCurrentScope.add(node); |
| } |
| } |
| |
| /** |
| * Reports when the set contains the given constant condition node |
| * @param {ASTNode} node The AST node to check. |
| * @returns {void} |
| * @private |
| */ |
| function checkConstantConditionLoopInSet(node) { |
| if (loopsInCurrentScope.has(node)) { |
| loopsInCurrentScope.delete(node); |
| context.report({ node: node.test, messageId: "unexpected" }); |
| } |
| } |
| |
| /** |
| * Reports when the given node contains a constant condition. |
| * @param {ASTNode} node The AST node to check. |
| * @returns {void} |
| * @private |
| */ |
| function reportIfConstant(node) { |
| if (node.test && isConstant(context.getScope(), node.test, true)) { |
| context.report({ node: node.test, messageId: "unexpected" }); |
| } |
| } |
| |
| /** |
| * Stores current set of constant loops in loopSetStack temporarily |
| * and uses a new set to track constant loops |
| * @returns {void} |
| * @private |
| */ |
| function enterFunction() { |
| loopSetStack.push(loopsInCurrentScope); |
| loopsInCurrentScope = new Set(); |
| } |
| |
| /** |
| * Reports when the set still contains stored constant conditions |
| * @returns {void} |
| * @private |
| */ |
| function exitFunction() { |
| loopsInCurrentScope = loopSetStack.pop(); |
| } |
| |
| /** |
| * Checks node when checkLoops option is enabled |
| * @param {ASTNode} node The AST node to check. |
| * @returns {void} |
| * @private |
| */ |
| function checkLoop(node) { |
| if (checkLoops) { |
| trackConstantConditionLoop(node); |
| } |
| } |
| |
| //-------------------------------------------------------------------------- |
| // Public |
| //-------------------------------------------------------------------------- |
| |
| return { |
| ConditionalExpression: reportIfConstant, |
| IfStatement: reportIfConstant, |
| WhileStatement: checkLoop, |
| "WhileStatement:exit": checkConstantConditionLoopInSet, |
| DoWhileStatement: checkLoop, |
| "DoWhileStatement:exit": checkConstantConditionLoopInSet, |
| ForStatement: checkLoop, |
| "ForStatement > .test": node => checkLoop(node.parent), |
| "ForStatement:exit": checkConstantConditionLoopInSet, |
| FunctionDeclaration: enterFunction, |
| "FunctionDeclaration:exit": exitFunction, |
| FunctionExpression: enterFunction, |
| "FunctionExpression:exit": exitFunction, |
| YieldExpression: () => loopsInCurrentScope.clear() |
| }; |
| |
| } |
| }; |