|                                                                                                                                                                                       |  | /** * @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/latest/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.sourceCode;        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);                }            }        };    }};
 |