1/** 2 * @fileoverview enforce "for" loop update clause moving the counter in the right direction.(for-direction) 3 * @author Aladdin-ADD<hh_2013@foxmail.com> 4 */ 5 6"use strict"; 7 8//------------------------------------------------------------------------------ 9// Rule Definition 10//------------------------------------------------------------------------------ 11 12module.exports = { 13 meta: { 14 type: "problem", 15 16 docs: { 17 description: "enforce \"for\" loop update clause moving the counter in the right direction.", 18 category: "Possible Errors", 19 recommended: true, 20 url: "https://eslint.org/docs/rules/for-direction" 21 }, 22 23 fixable: null, 24 schema: [], 25 26 messages: { 27 incorrectDirection: "The update clause in this loop moves the variable in the wrong direction." 28 } 29 }, 30 31 create(context) { 32 33 /** 34 * report an error. 35 * @param {ASTNode} node the node to report. 36 * @returns {void} 37 */ 38 function report(node) { 39 context.report({ 40 node, 41 messageId: "incorrectDirection" 42 }); 43 } 44 45 /** 46 * check the right side of the assignment 47 * @param {ASTNode} update UpdateExpression to check 48 * @param {int} dir expected direction that could either be turned around or invalidated 49 * @returns {int} return dir, the negated dir or zero if it's not clear for identifiers 50 */ 51 function getRightDirection(update, dir) { 52 if (update.right.type === "UnaryExpression") { 53 if (update.right.operator === "-") { 54 return -dir; 55 } 56 } else if (update.right.type === "Identifier") { 57 return 0; 58 } 59 return dir; 60 } 61 62 /** 63 * check UpdateExpression add/sub the counter 64 * @param {ASTNode} update UpdateExpression to check 65 * @param {string} counter variable name to check 66 * @returns {int} if add return 1, if sub return -1, if nochange, return 0 67 */ 68 function getUpdateDirection(update, counter) { 69 if (update.argument.type === "Identifier" && update.argument.name === counter) { 70 if (update.operator === "++") { 71 return 1; 72 } 73 if (update.operator === "--") { 74 return -1; 75 } 76 } 77 return 0; 78 } 79 80 /** 81 * check AssignmentExpression add/sub the counter 82 * @param {ASTNode} update AssignmentExpression to check 83 * @param {string} counter variable name to check 84 * @returns {int} if add return 1, if sub return -1, if nochange, return 0 85 */ 86 function getAssignmentDirection(update, counter) { 87 if (update.left.name === counter) { 88 if (update.operator === "+=") { 89 return getRightDirection(update, 1); 90 } 91 if (update.operator === "-=") { 92 return getRightDirection(update, -1); 93 } 94 } 95 return 0; 96 } 97 return { 98 ForStatement(node) { 99 100 if (node.test && node.test.type === "BinaryExpression" && node.test.left.type === "Identifier" && node.update) { 101 const counter = node.test.left.name; 102 const operator = node.test.operator; 103 const update = node.update; 104 105 let wrongDirection; 106 107 if (operator === "<" || operator === "<=") { 108 wrongDirection = -1; 109 } else if (operator === ">" || operator === ">=") { 110 wrongDirection = 1; 111 } else { 112 return; 113 } 114 115 if (update.type === "UpdateExpression") { 116 if (getUpdateDirection(update, counter) === wrongDirection) { 117 report(node); 118 } 119 } else if (update.type === "AssignmentExpression" && getAssignmentDirection(update, counter) === wrongDirection) { 120 report(node); 121 } 122 } 123 } 124 }; 125 } 126}; 127