1/** 2 * @fileoverview Rule to flag use of unary increment and decrement operators. 3 * @author Ian Christian Myers 4 * @author Brody McKee (github.com/mrmckeb) 5 */ 6 7"use strict"; 8 9//------------------------------------------------------------------------------ 10// Helpers 11//------------------------------------------------------------------------------ 12 13/** 14 * Determines whether the given node is the update node of a `ForStatement`. 15 * @param {ASTNode} node The node to check. 16 * @returns {boolean} `true` if the node is `ForStatement` update. 17 */ 18function isForStatementUpdate(node) { 19 const parent = node.parent; 20 21 return parent.type === "ForStatement" && parent.update === node; 22} 23 24/** 25 * Determines whether the given node is considered to be a for loop "afterthought" by the logic of this rule. 26 * In particular, it returns `true` if the given node is either: 27 * - The update node of a `ForStatement`: for (;; i++) {} 28 * - An operand of a sequence expression that is the update node: for (;; foo(), i++) {} 29 * - An operand of a sequence expression that is child of another sequence expression, etc., 30 * up to the sequence expression that is the update node: for (;; foo(), (bar(), (baz(), i++))) {} 31 * @param {ASTNode} node The node to check. 32 * @returns {boolean} `true` if the node is a for loop afterthought. 33 */ 34function isForLoopAfterthought(node) { 35 const parent = node.parent; 36 37 if (parent.type === "SequenceExpression") { 38 return isForLoopAfterthought(parent); 39 } 40 41 return isForStatementUpdate(node); 42} 43 44//------------------------------------------------------------------------------ 45// Rule Definition 46//------------------------------------------------------------------------------ 47 48module.exports = { 49 meta: { 50 type: "suggestion", 51 52 docs: { 53 description: "disallow the unary operators `++` and `--`", 54 category: "Stylistic Issues", 55 recommended: false, 56 url: "https://eslint.org/docs/rules/no-plusplus" 57 }, 58 59 schema: [ 60 { 61 type: "object", 62 properties: { 63 allowForLoopAfterthoughts: { 64 type: "boolean", 65 default: false 66 } 67 }, 68 additionalProperties: false 69 } 70 ], 71 72 messages: { 73 unexpectedUnaryOp: "Unary operator '{{operator}}' used." 74 } 75 }, 76 77 create(context) { 78 79 const config = context.options[0]; 80 let allowForLoopAfterthoughts = false; 81 82 if (typeof config === "object") { 83 allowForLoopAfterthoughts = config.allowForLoopAfterthoughts === true; 84 } 85 86 return { 87 88 UpdateExpression(node) { 89 if (allowForLoopAfterthoughts && isForLoopAfterthought(node)) { 90 return; 91 } 92 93 context.report({ 94 node, 95 messageId: "unexpectedUnaryOp", 96 data: { 97 operator: node.operator 98 } 99 }); 100 } 101 102 }; 103 104 } 105}; 106