|
|
|
|
|
|
|
|
|
|
|
"use strict"; |
|
|
|
|
|
|
|
|
|
|
|
const { getStaticValue } = require("@eslint-community/eslint-utils"); |
|
|
|
|
|
|
|
|
|
|
|
|
|
module.exports = { |
|
meta: { |
|
type: "problem", |
|
|
|
docs: { |
|
description: "Enforce \"for\" loop update clause moving the counter in the right direction", |
|
recommended: true, |
|
url: "https://eslint.org/docs/latest/rules/for-direction" |
|
}, |
|
|
|
fixable: null, |
|
schema: [], |
|
|
|
messages: { |
|
incorrectDirection: "The update clause in this loop moves the variable in the wrong direction." |
|
} |
|
}, |
|
|
|
create(context) { |
|
const { sourceCode } = context; |
|
|
|
|
|
|
|
|
|
|
|
|
|
function report(node) { |
|
context.report({ |
|
node, |
|
messageId: "incorrectDirection" |
|
}); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function getRightDirection(update, dir) { |
|
const staticValue = getStaticValue(update.right, sourceCode.getScope(update)); |
|
|
|
if (staticValue && ["bigint", "boolean", "number"].includes(typeof staticValue.value)) { |
|
const sign = Math.sign(Number(staticValue.value)) || 0; |
|
|
|
return dir * sign; |
|
} |
|
return 0; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function getUpdateDirection(update, counter) { |
|
if (update.argument.type === "Identifier" && update.argument.name === counter) { |
|
if (update.operator === "++") { |
|
return 1; |
|
} |
|
if (update.operator === "--") { |
|
return -1; |
|
} |
|
} |
|
return 0; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function getAssignmentDirection(update, counter) { |
|
if (update.left.name === counter) { |
|
if (update.operator === "+=") { |
|
return getRightDirection(update, 1); |
|
} |
|
if (update.operator === "-=") { |
|
return getRightDirection(update, -1); |
|
} |
|
} |
|
return 0; |
|
} |
|
|
|
return { |
|
ForStatement(node) { |
|
|
|
if (node.test && node.test.type === "BinaryExpression" && node.update) { |
|
for (const counterPosition of ["left", "right"]) { |
|
if (node.test[counterPosition].type !== "Identifier") { |
|
continue; |
|
} |
|
|
|
const counter = node.test[counterPosition].name; |
|
const operator = node.test.operator; |
|
const update = node.update; |
|
|
|
let wrongDirection; |
|
|
|
if (operator === "<" || operator === "<=") { |
|
wrongDirection = counterPosition === "left" ? -1 : 1; |
|
} else if (operator === ">" || operator === ">=") { |
|
wrongDirection = counterPosition === "left" ? 1 : -1; |
|
} else { |
|
return; |
|
} |
|
|
|
if (update.type === "UpdateExpression") { |
|
if (getUpdateDirection(update, counter) === wrongDirection) { |
|
report(node); |
|
} |
|
} else if (update.type === "AssignmentExpression" && getAssignmentDirection(update, counter) === wrongDirection) { |
|
report(node); |
|
} |
|
} |
|
} |
|
} |
|
}; |
|
} |
|
}; |
|
|