| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128 | /** * @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/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.getSourceCode();        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)})`);                                }                            }                        ]                    });                }            }        };    }};
 |