lib/rules/no-self-assign.js
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 rightElement = right.elements[i];

            eachSelfAssignment(left.elements[i], 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" &&
        !left.computed &&
        !right.computed &&
        right.kind === "init" &&
        !right.method &&
        left.key.name === right.key.name
    ) {
        eachSelfAssignment(left.value, right.value, props, report);
    } else if (
        props &&
        left.type === "MemberExpression" &&
        right.type === "MemberExpression" &&
        isSameMember(left, right)
    ) {
        report(right);
    }
}
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 rightElement = right.elements[i];

            eachSelfAssignment(left.elements[i], 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" &&
        !left.computed &&
        !right.computed &&
        right.kind === "init" &&
        !right.method &&
        left.key.name === right.key.name
    ) {
        eachSelfAssignment(left.value, right.value, props, report);
    } else if (
        props &&
        left.type === "MemberExpression" &&
        right.type === "MemberExpression" &&
        isSameMember(left, right)
    ) {
        report(right);
    }
}
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 rightElement = right . elements [ i ] ; eachSelfAssignment ( left . elements [ i ] , 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" && ! left . computed && ! right . computed && right . kind === "init" && ! right . method && left . key . name === right . key . name ) { eachSelfAssignment ( left . value , right . value , props , report ) ; } else if ( props && left . type === "MemberExpression" && right . type === "MemberExpression" && isSameMember ( left , right ) ) { report ( right ) ; } }
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}
Traverses 2 Pattern nodes in parallel then reports self - assignments .