Skip to content
Permalink
9bfb9ba527
Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
Go to file
 
 
Cannot retrieve contributors at this time
128 lines (110 sloc) 4.63 KB
/**
* @fileoverview Rule to disallow negating the left operand of relational operators
* @author Toru Nagashima
*/
"use strict";
//------------------------------------------------------------------------------
// Requirements
//------------------------------------------------------------------------------
const astUtils = require("./utils/ast-utils");
//------------------------------------------------------------------------------
// Helpers
//------------------------------------------------------------------------------
/**
* Checks whether the given operator is `in` or `instanceof`
* @param {string} op The operator type to check.
* @returns {boolean} `true` if the operator is `in` or `instanceof`
*/
function isInOrInstanceOfOperator(op) {
return op === "in" || op === "instanceof";
}
/**
* Checks whether the given operator is an ordering relational operator or not.
* @param {string} op The operator type to check.
* @returns {boolean} `true` if the operator is an ordering relational operator.
*/
function isOrderingRelationalOperator(op) {
return op === "<" || op === ">" || op === ">=" || op === "<=";
}
/**
* Checks whether the given node is a logical negation expression or not.
* @param {ASTNode} node The node to check.
* @returns {boolean} `true` if the node is a logical negation expression.
*/
function isNegation(node) {
return node.type === "UnaryExpression" && node.operator === "!";
}
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
/** @type {import('../shared/types').Rule} */
module.exports = {
meta: {
type: "problem",
docs: {
description: "Disallow negating the left operand of relational operators",
recommended: true,
url: "https://eslint.org/docs/latest/rules/no-unsafe-negation"
},
hasSuggestions: true,
schema: [
{
type: "object",
properties: {
enforceForOrderingRelations: {
type: "boolean",
default: false
}
},
additionalProperties: false
}
],
fixable: null,
messages: {
unexpected: "Unexpected negating the left operand of '{{operator}}' operator.",
suggestNegatedExpression: "Negate '{{operator}}' expression instead of its left operand. This changes the current behavior.",
suggestParenthesisedNegation: "Wrap negation in '()' to make the intention explicit. This preserves the current behavior."
}
},
create(context) {
const sourceCode = context.sourceCode;
const options = context.options[0] || {};
const enforceForOrderingRelations = options.enforceForOrderingRelations === true;
return {
BinaryExpression(node) {
const operator = node.operator;
const orderingRelationRuleApplies = enforceForOrderingRelations && isOrderingRelationalOperator(operator);
if (
(isInOrInstanceOfOperator(operator) || orderingRelationRuleApplies) &&
isNegation(node.left) &&
!astUtils.isParenthesised(sourceCode, node.left)
) {
context.report({
node,
loc: node.left.loc,
messageId: "unexpected",
data: { operator },
suggest: [
{
messageId: "suggestNegatedExpression",
data: { operator },
fix(fixer) {
const negationToken = sourceCode.getFirstToken(node.left);
const fixRange = [negationToken.range[1], node.range[1]];
const text = sourceCode.text.slice(fixRange[0], fixRange[1]);
return fixer.replaceTextRange(fixRange, `(${text})`);
}
},
{
messageId: "suggestParenthesisedNegation",
fix(fixer) {
return fixer.replaceText(node.left, `(${sourceCode.getText(node.left)})`);
}
}
]
});
}
}
};
}
};