| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183 | /** * @fileoverview Rule to disallow assignments where both sides are exactly the same * @author Toru Nagashima */"use strict";//------------------------------------------------------------------------------// Requirements//------------------------------------------------------------------------------const astUtils = require("./utils/ast-utils");//------------------------------------------------------------------------------// Helpers//------------------------------------------------------------------------------const SPACES = /\s+/gu;/** * Traverses 2 Pattern nodes in parallel, then reports self-assignments. * @param {ASTNode|null} left A left node to traverse. This is a Pattern or *      a Property. * @param {ASTNode|null} right A right node to traverse. This is a Pattern or *      a Property. * @param {boolean} props The flag to check member expressions as well. * @param {Function} report A callback function to report. * @returns {void} */function eachSelfAssignment(left, right, props, report) {    if (!left || !right) {        // do nothing    } else if (        left.type === "Identifier" &&        right.type === "Identifier" &&        left.name === right.name    ) {        report(right);    } else if (        left.type === "ArrayPattern" &&        right.type === "ArrayExpression"    ) {        const end = Math.min(left.elements.length, right.elements.length);        for (let i = 0; i < end; ++i) {            const leftElement = left.elements[i];            const rightElement = right.elements[i];            // Avoid cases such as [...a] = [...a, 1]            if (                leftElement &&                leftElement.type === "RestElement" &&                i < right.elements.length - 1            ) {                break;            }            eachSelfAssignment(leftElement, rightElement, props, report);            // After a spread element, those indices are unknown.            if (rightElement && rightElement.type === "SpreadElement") {                break;            }        }    } else if (        left.type === "RestElement" &&        right.type === "SpreadElement"    ) {        eachSelfAssignment(left.argument, right.argument, props, report);    } else if (        left.type === "ObjectPattern" &&        right.type === "ObjectExpression" &&        right.properties.length >= 1    ) {        /*         * Gets the index of the last spread property.         * It's possible to overwrite properties followed by it.         */        let startJ = 0;        for (let i = right.properties.length - 1; i >= 0; --i) {            const propType = right.properties[i].type;            if (propType === "SpreadElement" || propType === "ExperimentalSpreadProperty") {                startJ = i + 1;                break;            }        }        for (let i = 0; i < left.properties.length; ++i) {            for (let j = startJ; j < right.properties.length; ++j) {                eachSelfAssignment(                    left.properties[i],                    right.properties[j],                    props,                    report                );            }        }    } else if (        left.type === "Property" &&        right.type === "Property" &&        right.kind === "init" &&        !right.method    ) {        const leftName = astUtils.getStaticPropertyName(left);        if (leftName !== null && leftName === astUtils.getStaticPropertyName(right)) {            eachSelfAssignment(left.value, right.value, props, report);        }    } else if (        props &&        astUtils.skipChainExpression(left).type === "MemberExpression" &&        astUtils.skipChainExpression(right).type === "MemberExpression" &&        astUtils.isSameReference(left, right)    ) {        report(right);    }}//------------------------------------------------------------------------------// Rule Definition//------------------------------------------------------------------------------/** @type {import('../shared/types').Rule} */module.exports = {    meta: {        type: "problem",        docs: {            description: "Disallow assignments where both sides are exactly the same",            recommended: true,            url: "https://eslint.org/docs/rules/no-self-assign"        },        schema: [            {                type: "object",                properties: {                    props: {                        type: "boolean",                        default: true                    }                },                additionalProperties: false            }        ],        messages: {            selfAssignment: "'{{name}}' is assigned to itself."        }    },    create(context) {        const sourceCode = context.getSourceCode();        const [{ props = true } = {}] = context.options;        /**         * Reports a given node as self assignments.         * @param {ASTNode} node A node to report. This is an Identifier node.         * @returns {void}         */        function report(node) {            context.report({                node,                messageId: "selfAssignment",                data: {                    name: sourceCode.getText(node).replace(SPACES, "")                }            });        }        return {            AssignmentExpression(node) {                if (["=", "&&=", "||=", "??="].includes(node.operator)) {                    eachSelfAssignment(node.left, node.right, props, report);                }            }        };    }};
 |