code:
/**
 * @fileoverview Rule to flag declared but unused private class members
 * @author Tim van der Lippe
 */

"use strict";

//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------

/** @type {import('../shared/types').Rule} */
module.exports = {
    meta: {
        type: "problem",

        docs: {
            description: "Disallow unused private class members",
            recommended: false,
            url: "https://eslint.org/docs/latest/rules/no-unused-private-class-members"
        },

        schema: [],

        messages: {
            unusedPrivateClassMember: "'{{classMemberName}}' is defined but never used."
        }
    },

    create(context) {
        const trackedClasses = [];

        /**
         * Check whether the current node is in a write only assignment.
         * @param {ASTNode} privateIdentifierNode Node referring to a private identifier
         * @returns {boolean} Whether the node is in a write only assignment
         * @private
         */
        function isWriteOnlyAssignment(privateIdentifierNode) {
            const parentStatement = privateIdentifierNode.parent.parent;
            const isAssignmentExpression = parentStatement.type === "AssignmentExpression";

            if (!isAssignmentExpression &&
                parentStatement.type !== "ForInStatement" &&
                parentStatement.type !== "ForOfStatement" &&
                parentStatement.type !== "AssignmentPattern") {
                return false;
            }

            // It is a write-only usage, since we still allow usages on the right for reads
            if (parentStatement.left !== privateIdentifierNode.parent) {
                return false;
            }

            // For any other operator (such as '+=') we still consider it a read operation
            if (isAssignmentExpression && parentStatement.operator !== "=") {

                /*
                 * However, if the read operation is "discarded" in an empty statement, then
                 * we consider it write only.
                 */
                return parentStatement.parent.type === "ExpressionStatement";
            }

            return true;
        }

        //--------------------------------------------------------------------------
        // Public
        //--------------------------------------------------------------------------

        return {

            // Collect all declared members up front and assume they are all unused
            ClassBody(classBodyNode) {
                const privateMembers = new Map();

                trackedClasses.unshift(privateMembers);
                for (const bodyMember of classBodyNode.body) {
                    if (bodyMember.type === "PropertyDefinition" || bodyMember.type === "MethodDefinition") {
                        if (bodyMember.key.type === "PrivateIdentifier") {
                            privateMembers.set(bodyMember.key.name, {
                                declaredNode: bodyMember,
                                isAccessor: bodyMember.type === "MethodDefinition" &&
                                    (bodyMember.kind === "set" || bodyMember.kind === "get")
                            });
                        }
                    }
                }
            },

            /*
             * Process all usages of the private identifier and remove a member from
             * `declaredAndUnusedPrivateMembers` if we deem it used.
             */
            PrivateIdentifier(privateIdentifierNode) {
                const classBody = trackedClasses.find(classProperties => classProperties.has(privateIdentifierNode.name));

                // Can't happen, as it is a parser to have a missing class body, but let's code defensively here.
                if (!classBody) {
                    return;
                }

                // In case any other usage was already detected, we can short circuit the logic here.
                const memberDefinition = classBody.get(privateIdentifierNode.name);

                if (memberDefinition.isUsed) {
                    return;
                }

                // The definition of the class member itself
                if (privateIdentifierNode.parent.type === "PropertyDefinition" ||
                    privateIdentifierNode.parent.type === "MethodDefinition") {
                    return;
                }

                /*
                 * Any usage of an accessor is considered a read, as the getter/setter can have
                 * side-effects in its definition.
                 */
                if (memberDefinition.isAccessor) {
                    memberDefinition.isUsed = true;
                    return;
                }

                // Any assignments to this member, except for assignments that also read
                if (isWriteOnlyAssignment(privateIdentifierNode)) {
                    return;
                }

                const wrappingExpressionType = privateIdentifierNode.parent.parent.type;
                const parentOfWrappingExpressionType = privateIdentifierNode.parent.parent.parent.type;

                // A statement which only increments (`this.#x++;`)
                if (wrappingExpressionType === "UpdateExpression" &&
                    parentOfWrappingExpressionType === "ExpressionStatement") {
                    return;
                }

                /*
                 * ({ x: this.#usedInDestructuring } = bar);
                 *
                 * But should treat the following as a read:
                 * ({ [this.#x]: a } = foo);
                 */
                if (wrappingExpressionType === "Property" &&
                    parentOfWrappingExpressionType === "ObjectPattern" &&
                    privateIdentifierNode.parent.parent.value === privateIdentifierNode.parent) {
                    return;
                }

                // [...this.#unusedInRestPattern] = bar;
                if (wrappingExpressionType === "RestElement") {
                    return;
                }

                // [this.#unusedInAssignmentPattern] = bar;
                if (wrappingExpressionType === "ArrayPattern") {
                    return;
                }

                /*
                 * We can't delete the memberDefinition, as we need to keep track of which member we are marking as used.
                 * In the case of nested classes, we only mark the first member we encounter as used. If you were to delete
                 * the member, then any subsequent usage could incorrectly mark the member of an encapsulating parent class
                 * as used, which is incorrect.
                 */
                memberDefinition.isUsed = true;
            },

            /*
             * Post-process the class members and report any remaining members.
             * Since private members can only be accessed in the current class context,
             * we can safely assume that all usages are within the current class body.
             */
            "ClassBody:exit"() {
                const unusedPrivateMembers = trackedClasses.shift();

                for (const [classMemberName, { declaredNode, isUsed }] of unusedPrivateMembers.entries()) {
                    if (isUsed) {
                        continue;
                    }
                    context.report({
                        node: declaredNode,
                        loc: declaredNode.key.loc,
                        messageId: "unusedPrivateClassMember",
                        data: {
                            classMemberName: `#${classMemberName}`
                        }
                    });
                }
            }
        };
    }
};


code:
/**
 * @fileoverview Rule to flag declared but unused variables
 * @author Ilya Volodin
 */

"use strict";

//------------------------------------------------------------------------------
// Requirements
//------------------------------------------------------------------------------

const astUtils = require("./utils/ast-utils");

//------------------------------------------------------------------------------
// Typedefs
//------------------------------------------------------------------------------

/**
 * Bag of data used for formatting the `unusedVar` lint message.
 * @typedef {Object} UnusedVarMessageData
 * @property {string} varName The name of the unused var.
 * @property {'defined'|'assigned a value'} action Description of the vars state.
 * @property {string} additional Any additional info to be appended at the end.
 */

//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------

/** @type {import('../shared/types').Rule} */
module.exports = {
    meta: {
        type: "problem",

        docs: {
            description: "Disallow unused variables",
            recommended: true,
            url: "https://eslint.org/docs/latest/rules/no-unused-vars"
        },

        schema: [
            {
                oneOf: [
                    {
                        enum: ["all", "local"]
                    },
                    {
                        type: "object",
                        properties: {
                            vars: {
                                enum: ["all", "local"]
                            },
                            varsIgnorePattern: {
                                type: "string"
                            },
                            args: {
                                enum: ["all", "after-used", "none"]
                            },
                            ignoreRestSiblings: {
                                type: "boolean"
                            },
                            argsIgnorePattern: {
                                type: "string"
                            },
                            caughtErrors: {
                                enum: ["all", "none"]
                            },
                            caughtErrorsIgnorePattern: {
                                type: "string"
                            },
                            destructuredArrayIgnorePattern: {
                                type: "string"
                            }
                        },
                        additionalProperties: false
                    }
                ]
            }
        ],

        messages: {
            unusedVar: "'{{varName}}' is {{action}} but never used{{additional}}."
        }
    },

    create(context) {
        const sourceCode = context.sourceCode;

        const REST_PROPERTY_TYPE = /^(?:RestElement|(?:Experimental)?RestProperty)$/u;

        const config = {
            vars: "all",
            args: "after-used",
            ignoreRestSiblings: false,
            caughtErrors: "none"
        };

        const firstOption = context.options[0];

        if (firstOption) {
            if (typeof firstOption === "string") {
                config.vars = firstOption;
            } else {
                config.vars = firstOption.vars || config.vars;
                config.args = firstOption.args || config.args;
                config.ignoreRestSiblings = firstOption.ignoreRestSiblings || config.ignoreRestSiblings;
                config.caughtErrors = firstOption.caughtErrors || config.caughtErrors;

                if (firstOption.varsIgnorePattern) {
                    config.varsIgnorePattern = new RegExp(firstOption.varsIgnorePattern, "u");
                }

                if (firstOption.argsIgnorePattern) {
                    config.argsIgnorePattern = new RegExp(firstOption.argsIgnorePattern, "u");
                }

                if (firstOption.caughtErrorsIgnorePattern) {
                    config.caughtErrorsIgnorePattern = new RegExp(firstOption.caughtErrorsIgnorePattern, "u");
                }

                if (firstOption.destructuredArrayIgnorePattern) {
                    config.destructuredArrayIgnorePattern = new RegExp(firstOption.destructuredArrayIgnorePattern, "u");
                }
            }
        }

        /**
         * Generates the message data about the variable being defined and unused,
         * including the ignore pattern if configured.
         * @param {Variable} unusedVar eslint-scope variable object.
         * @returns {UnusedVarMessageData} The message data to be used with this unused variable.
         */
        function getDefinedMessageData(unusedVar) {
            const defType = unusedVar.defs && unusedVar.defs[0] && unusedVar.defs[0].type;
            let type;
            let pattern;

            if (defType === "CatchClause" && config.caughtErrorsIgnorePattern) {
                type = "args";
                pattern = config.caughtErrorsIgnorePattern.toString();
            } else if (defType === "Parameter" && config.argsIgnorePattern) {
                type = "args";
                pattern = config.argsIgnorePattern.toString();
            } else if (defType !== "Parameter" && config.varsIgnorePattern) {
                type = "vars";
                pattern = config.varsIgnorePattern.toString();
            }

            const additional = type ? `. Allowed unused ${type} must match ${pattern}` : "";

            return {
                varName: unusedVar.name,
                action: "defined",
                additional
            };
        }

        /**
         * Generate the warning message about the variable being
         * assigned and unused, including the ignore pattern if configured.
         * @param {Variable} unusedVar eslint-scope variable object.
         * @returns {UnusedVarMessageData} The message data to be used with this unused variable.
         */
        function getAssignedMessageData(unusedVar) {
            const def = unusedVar.defs[0];
            let additional = "";

            if (config.destructuredArrayIgnorePattern && def && def.name.parent.type === "ArrayPattern") {
                additional = `. Allowed unused elements of array destructuring patterns must match ${config.destructuredArrayIgnorePattern.toString()}`;
            } else if (config.varsIgnorePattern) {
                additional = `. Allowed unused vars must match ${config.varsIgnorePattern.toString()}`;
            }

            return {
                varName: unusedVar.name,
                action: "assigned a value",
                additional
            };
        }

        //--------------------------------------------------------------------------
        // Helpers
        //--------------------------------------------------------------------------

        const STATEMENT_TYPE = /(?:Statement|Declaration)$/u;

        /**
         * Determines if a given variable is being exported from a module.
         * @param {Variable} variable eslint-scope variable object.
         * @returns {boolean} True if the variable is exported, false if not.
         * @private
         */
        function isExported(variable) {

            const definition = variable.defs[0];

            if (definition) {

                let node = definition.node;

                if (node.type === "VariableDeclarator") {
                    node = node.parent;
                } else if (definition.type === "Parameter") {
                    return false;
                }

                return node.parent.type.indexOf("Export") === 0;
            }
            return false;

        }

        /**
         * Checks whether a node is a sibling of the rest property or not.
         * @param {ASTNode} node a node to check
         * @returns {boolean} True if the node is a sibling of the rest property, otherwise false.
         */
        function hasRestSibling(node) {
            return node.type === "Property" &&
                node.parent.type === "ObjectPattern" &&
                REST_PROPERTY_TYPE.test(node.parent.properties[node.parent.properties.length - 1].type);
        }

        /**
         * Determines if a variable has a sibling rest property
         * @param {Variable} variable eslint-scope variable object.
         * @returns {boolean} True if the variable is exported, false if not.
         * @private
         */
        function hasRestSpreadSibling(variable) {
            if (config.ignoreRestSiblings) {
                const hasRestSiblingDefinition = variable.defs.some(def => hasRestSibling(def.name.parent));
                const hasRestSiblingReference = variable.references.some(ref => hasRestSibling(ref.identifier.parent));

                return hasRestSiblingDefinition || hasRestSiblingReference;
            }

            return false;
        }

        /**
         * Determines if a reference is a read operation.
         * @param {Reference} ref An eslint-scope Reference
         * @returns {boolean} whether the given reference represents a read operation
         * @private
         */
        function isReadRef(ref) {
            return ref.isRead();
        }

        /**
         * Determine if an identifier is referencing an enclosing function name.
         * @param {Reference} ref The reference to check.
         * @param {ASTNode[]} nodes The candidate function nodes.
         * @returns {boolean} True if it's a self-reference, false if not.
         * @private
         */
        function isSelfReference(ref, nodes) {
            let scope = ref.from;

            while (scope) {
                if (nodes.includes(scope.block)) {
                    return true;
                }

                scope = scope.upper;
            }

            return false;
        }

        /**
         * Gets a list of function definitions for a specified variable.
         * @param {Variable} variable eslint-scope variable object.
         * @returns {ASTNode[]} Function nodes.
         * @private
         */
        function getFunctionDefinitions(variable) {
            const functionDefinitions = [];

            variable.defs.forEach(def => {
                const { type, node } = def;

                // FunctionDeclarations
                if (type === "FunctionName") {
                    functionDefinitions.push(node);
                }

                // FunctionExpressions
                if (type === "Variable" && node.init &&
                    (node.init.type === "FunctionExpression" || node.init.type === "ArrowFunctionExpression")) {
                    functionDefinitions.push(node.init);
                }
            });
            return functionDefinitions;
        }

        /**
         * Checks the position of given nodes.
         * @param {ASTNode} inner A node which is expected as inside.
         * @param {ASTNode} outer A node which is expected as outside.
         * @returns {boolean} `true` if the `inner` node exists in the `outer` node.
         * @private
         */
        function isInside(inner, outer) {
            return (
                inner.range[0] >= outer.range[0] &&
                inner.range[1] <= outer.range[1]
            );
        }

        /**
         * Checks whether a given node is unused expression or not.
         * @param {ASTNode} node The node itself
         * @returns {boolean} The node is an unused expression.
         * @private
         */
        function isUnusedExpression(node) {
            const parent = node.parent;

            if (parent.type === "ExpressionStatement") {
                return true;
            }

            if (parent.type === "SequenceExpression") {
                const isLastExpression = parent.expressions[parent.expressions.length - 1] === node;

                if (!isLastExpression) {
                    return true;
                }
                return isUnusedExpression(parent);
            }

            return false;
        }

        /**
         * If a given reference is left-hand side of an assignment, this gets
         * the right-hand side node of the assignment.
         *
         * In the following cases, this returns null.
         *
         * - The reference is not the LHS of an assignment expression.
         * - The reference is inside of a loop.
         * - The reference is inside of a function scope which is different from
         *   the declaration.
         * @param {eslint-scope.Reference} ref A reference to check.
         * @param {ASTNode} prevRhsNode The previous RHS node.
         * @returns {ASTNode|null} The RHS node or null.
         * @private
         */
        function getRhsNode(ref, prevRhsNode) {
            const id = ref.identifier;
            const parent = id.parent;
            const refScope = ref.from.variableScope;
            const varScope = ref.resolved.scope.variableScope;
            const canBeUsedLater = refScope !== varScope || astUtils.isInLoop(id);

            /*
             * Inherits the previous node if this reference is in the node.
             * This is for `a = a + a`-like code.
             */
            if (prevRhsNode && isInside(id, prevRhsNode)) {
                return prevRhsNode;
            }

            if (parent.type === "AssignmentExpression" &&
                isUnusedExpression(parent) &&
                id === parent.left &&
                !canBeUsedLater
            ) {
                return parent.right;
            }
            return null;
        }

        /**
         * Checks whether a given function node is stored to somewhere or not.
         * If the function node is stored, the function can be used later.
         * @param {ASTNode} funcNode A function node to check.
         * @param {ASTNode} rhsNode The RHS node of the previous assignment.
         * @returns {boolean} `true` if under the following conditions:
         *      - the funcNode is assigned to a variable.
         *      - the funcNode is bound as an argument of a function call.
         *      - the function is bound to a property and the object satisfies above conditions.
         * @private
         */
        function isStorableFunction(funcNode, rhsNode) {
            let node = funcNode;
            let parent = funcNode.parent;

            while (parent && isInside(parent, rhsNode)) {
                switch (parent.type) {
                    case "SequenceExpression":
                        if (parent.expressions[parent.expressions.length - 1] !== node) {
                            return false;
                        }
                        break;

                    case "CallExpression":
                    case "NewExpression":
                        return parent.callee !== node;

                    case "AssignmentExpression":
                    case "TaggedTemplateExpression":
                    case "YieldExpression":
                        return true;

                    default:
                        if (STATEMENT_TYPE.test(parent.type)) {

                            /*
                             * If it encountered statements, this is a complex pattern.
                             * Since analyzing complex patterns is hard, this returns `true` to avoid false positive.
                             */
                            return true;
                        }
                }

                node = parent;
                parent = parent.parent;
            }

            return false;
        }

        /**
         * Checks whether a given Identifier node exists inside of a function node which can be used later.
         *
         * "can be used later" means:
         * - the function is assigned to a variable.
         * - the function is bound to a property and the object can be used later.
         * - the function is bound as an argument of a function call.
         *
         * If a reference exists in a function which can be used later, the reference is read when the function is called.
         * @param {ASTNode} id An Identifier node to check.
         * @param {ASTNode} rhsNode The RHS node of the previous assignment.
         * @returns {boolean} `true` if the `id` node exists inside of a function node which can be used later.
         * @private
         */
        function isInsideOfStorableFunction(id, rhsNode) {
            const funcNode = astUtils.getUpperFunction(id);

            return (
                funcNode &&
                isInside(funcNode, rhsNode) &&
                isStorableFunction(funcNode, rhsNode)
            );
        }

        /**
         * Checks whether a given reference is a read to update itself or not.
         * @param {eslint-scope.Reference} ref A reference to check.
         * @param {ASTNode} rhsNode The RHS node of the previous assignment.
         * @returns {boolean} The reference is a read to update itself.
         * @private
         */
        function isReadForItself(ref, rhsNode) {
            const id = ref.identifier;
            const parent = id.parent;

            return ref.isRead() && (

                // self update. e.g. `a += 1`, `a++`
                (
                    (
                        parent.type === "AssignmentExpression" &&
                        parent.left === id &&
                        isUnusedExpression(parent) &&
                        !astUtils.isLogicalAssignmentOperator(parent.operator)
                    ) ||
                    (
                        parent.type === "UpdateExpression" &&
                        isUnusedExpression(parent)
                    )
                ) ||

                // in RHS of an assignment for itself. e.g. `a = a + 1`
                (
                    rhsNode &&
                    isInside(id, rhsNode) &&
                    !isInsideOfStorableFunction(id, rhsNode)
                )
            );
        }

        /**
         * Determine if an identifier is used either in for-in or for-of loops.
         * @param {Reference} ref The reference to check.
         * @returns {boolean} whether reference is used in the for-in loops
         * @private
         */
        function isForInOfRef(ref) {
            let target = ref.identifier.parent;

            // "for (var ...) { return; }"
            if (target.type === "VariableDeclarator") {
                target = target.parent.parent;
            }

            if (target.type !== "ForInStatement" && target.type !== "ForOfStatement") {
                return false;
            }

            // "for (...) { return; }"
            if (target.body.type === "BlockStatement") {
                target = target.body.body[0];

            // "for (...) return;"
            } else {
                target = target.body;
            }

            // For empty loop body
            if (!target) {
                return false;
            }

            return target.type === "ReturnStatement";
        }

        /**
         * Determines if the variable is used.
         * @param {Variable} variable The variable to check.
         * @returns {boolean} True if the variable is used
         * @private
         */
        function isUsedVariable(variable) {
            const functionNodes = getFunctionDefinitions(variable),
                isFunctionDefinition = functionNodes.length > 0;
            let rhsNode = null;

            return variable.references.some(ref => {
                if (isForInOfRef(ref)) {
                    return true;
                }

                const forItself = isReadForItself(ref, rhsNode);

                rhsNode = getRhsNode(ref, rhsNode);

                return (
                    isReadRef(ref) &&
                    !forItself &&
                    !(isFunctionDefinition && isSelfReference(ref, functionNodes))
                );
            });
        }

        /**
         * Checks whether the given variable is after the last used parameter.
         * @param {eslint-scope.Variable} variable The variable to check.
         * @returns {boolean} `true` if the variable is defined after the last
         * used parameter.
         */
        function isAfterLastUsedArg(variable) {
            const def = variable.defs[0];
            const params = sourceCode.getDeclaredVariables(def.node);
            const posteriorParams = params.slice(params.indexOf(variable) + 1);

            // If any used parameters occur after this parameter, do not report.
            return !posteriorParams.some(v => v.references.length > 0 || v.eslintUsed);
        }

        /**
         * Gets an array of variables without read references.
         * @param {Scope} scope an eslint-scope Scope object.
         * @param {Variable[]} unusedVars an array that saving result.
         * @returns {Variable[]} unused variables of the scope and descendant scopes.
         * @private
         */
        function collectUnusedVariables(scope, unusedVars) {
            const variables = scope.variables;
            const childScopes = scope.childScopes;
            let i, l;

            if (scope.type !== "global" || config.vars === "all") {
                for (i = 0, l = variables.length; i < l; ++i) {
                    const variable = variables[i];

                    // skip a variable of class itself name in the class scope
                    if (scope.type === "class" && scope.block.id === variable.identifiers[0]) {
                        continue;
                    }

                    // skip function expression names and variables marked with markVariableAsUsed()
                    if (scope.functionExpressionScope || variable.eslintUsed) {
                        continue;
                    }

                    // skip implicit "arguments" variable
                    if (scope.type === "function" && variable.name === "arguments" && variable.identifiers.length === 0) {
                        continue;
                    }

                    // explicit global variables don't have definitions.
                    const def = variable.defs[0];

                    if (def) {
                        const type = def.type;
                        const refUsedInArrayPatterns = variable.references.some(ref => ref.identifier.parent.type === "ArrayPattern");

                        // skip elements of array destructuring patterns
                        if (
                            (
                                def.name.parent.type === "ArrayPattern" ||
                                refUsedInArrayPatterns
                            ) &&
                            config.destructuredArrayIgnorePattern &&
                            config.destructuredArrayIgnorePattern.test(def.name.name)
                        ) {
                            continue;
                        }

                        // skip catch variables
                        if (type === "CatchClause") {
                            if (config.caughtErrors === "none") {
                                continue;
                            }

                            // skip ignored parameters
                            if (config.caughtErrorsIgnorePattern && config.caughtErrorsIgnorePattern.test(def.name.name)) {
                                continue;
                            }
                        }

                        if (type === "Parameter") {

                            // skip any setter argument
                            if ((def.node.parent.type === "Property" || def.node.parent.type === "MethodDefinition") && def.node.parent.kind === "set") {
                                continue;
                            }

                            // if "args" option is "none", skip any parameter
                            if (config.args === "none") {
                                continue;
                            }

                            // skip ignored parameters
                            if (config.argsIgnorePattern && config.argsIgnorePattern.test(def.name.name)) {
                                continue;
                            }

                            // if "args" option is "after-used", skip used variables
                            if (config.args === "after-used" && astUtils.isFunction(def.name.parent) && !isAfterLastUsedArg(variable)) {
                                continue;
                            }
                        } else {

                            // skip ignored variables
                            if (config.varsIgnorePattern && config.varsIgnorePattern.test(def.name.name)) {
                                continue;
                            }
                        }
                    }

                    if (!isUsedVariable(variable) && !isExported(variable) && !hasRestSpreadSibling(variable)) {
                        unusedVars.push(variable);
                    }
                }
            }

            for (i = 0, l = childScopes.length; i < l; ++i) {
                collectUnusedVariables(childScopes[i], unusedVars);
            }

            return unusedVars;
        }

        //--------------------------------------------------------------------------
        // Public
        //--------------------------------------------------------------------------

        return {
            "Program:exit"(programNode) {
                const unusedVars = collectUnusedVariables(sourceCode.getScope(programNode), []);

                for (let i = 0, l = unusedVars.length; i < l; ++i) {
                    const unusedVar = unusedVars[i];

                    // Report the first declaration.
                    if (unusedVar.defs.length > 0) {

                        // report last write reference, https://github.com/eslint/eslint/issues/14324
                        const writeReferences = unusedVar.references.filter(ref => ref.isWrite() && ref.from.variableScope === unusedVar.scope.variableScope);

                        let referenceToReport;

                        if (writeReferences.length > 0) {
                            referenceToReport = writeReferences[writeReferences.length - 1];
                        }

                        context.report({
                            node: referenceToReport ? referenceToReport.identifier : unusedVar.identifiers[0],
                            messageId: "unusedVar",
                            data: unusedVar.references.some(ref => ref.isWrite())
                                ? getAssignedMessageData(unusedVar)
                                : getDefinedMessageData(unusedVar)
                        });

                    // If there are no regular declaration, report the first `/*globals*/` comment directive.
                    } else if (unusedVar.eslintExplicitGlobalComments) {
                        const directiveComment = unusedVar.eslintExplicitGlobalComments[0];

                        context.report({
                            node: programNode,
                            loc: astUtils.getNameLocationInGlobalDirectiveComment(sourceCode, directiveComment, unusedVar.name),
                            messageId: "unusedVar",
                            data: getDefinedMessageData(unusedVar)
                        });
                    }
                }
            }
        };

    }
};

license: mit


code:
/**
 * @fileoverview Rule to flag use of variables before they are defined
 * @author Ilya Volodin
 */

"use strict";

//------------------------------------------------------------------------------
// Helpers
//------------------------------------------------------------------------------

const SENTINEL_TYPE = /^(?:(?:Function|Class)(?:Declaration|Expression)|ArrowFunctionExpression|CatchClause|ImportDeclaration|ExportNamedDeclaration)$/u;
const FOR_IN_OF_TYPE = /^For(?:In|Of)Statement$/u;

/**
 * Parses a given value as options.
 * @param {any} options A value to parse.
 * @returns {Object} The parsed options.
 */
function parseOptions(options) {
    let functions = true;
    let classes = true;
    let variables = true;
    let allowNamedExports = false;

    if (typeof options === "string") {
        functions = (options !== "nofunc");
    } else if (typeof options === "object" && options !== null) {
        functions = options.functions !== false;
        classes = options.classes !== false;
        variables = options.variables !== false;
        allowNamedExports = !!options.allowNamedExports;
    }

    return { functions, classes, variables, allowNamedExports };
}

/**
 * Checks whether or not a given location is inside of the range of a given node.
 * @param {ASTNode} node An node to check.
 * @param {number} location A location to check.
 * @returns {boolean} `true` if the location is inside of the range of the node.
 */
function isInRange(node, location) {
    return node && node.range[0] <= location && location <= node.range[1];
}

/**
 * Checks whether or not a given location is inside of the range of a class static initializer.
 * Static initializers are static blocks and initializers of static fields.
 * @param {ASTNode} node `ClassBody` node to check static initializers.
 * @param {number} location A location to check.
 * @returns {boolean} `true` if the location is inside of a class static initializer.
 */
function isInClassStaticInitializerRange(node, location) {
    return node.body.some(classMember => (
        (
            classMember.type === "StaticBlock" &&
            isInRange(classMember, location)
        ) ||
        (
            classMember.type === "PropertyDefinition" &&
            classMember.static &&
            classMember.value &&
            isInRange(classMember.value, location)
        )
    ));
}

/**
 * Checks whether a given scope is the scope of a class static initializer.
 * Static initializers are static blocks and initializers of static fields.
 * @param {eslint-scope.Scope} scope A scope to check.
 * @returns {boolean} `true` if the scope is a class static initializer scope.
 */
function isClassStaticInitializerScope(scope) {
    if (scope.type === "class-static-block") {
        return true;
    }

    if (scope.type === "class-field-initializer") {

        // `scope.block` is PropertyDefinition#value node
        const propertyDefinition = scope.block.parent;

        return propertyDefinition.static;
    }

    return false;
}

/**
 * Checks whether a given reference is evaluated in an execution context
 * that isn't the one where the variable it refers to is defined.
 * Execution contexts are:
 * - top-level
 * - functions
 * - class field initializers (implicit functions)
 * - class static blocks (implicit functions)
 * Static class field initializers and class static blocks are automatically run during the class definition evaluation,
 * and therefore we'll consider them as a part of the parent execution context.
 * Example:
 *
 *   const x = 1;
 *
 *   x; // returns `false`
 *   () => x; // returns `true`
 *
 *   class C {
 *       field = x; // returns `true`
 *       static field = x; // returns `false`
 *
 *       method() {
 *           x; // returns `true`
 *       }
 *
 *       static method() {
 *           x; // returns `true`
 *       }
 *
 *       static {
 *           x; // returns `false`
 *       }
 *   }
 * @param {eslint-scope.Reference} reference A reference to check.
 * @returns {boolean} `true` if the reference is from a separate execution context.
 */
function isFromSeparateExecutionContext(reference) {
    const variable = reference.resolved;
    let scope = reference.from;

    // Scope#variableScope represents execution context
    while (variable.scope.variableScope !== scope.variableScope) {
        if (isClassStaticInitializerScope(scope.variableScope)) {
            scope = scope.variableScope.upper;
        } else {
            return true;
        }
    }

    return false;
}

/**
 * Checks whether or not a given reference is evaluated during the initialization of its variable.
 *
 * This returns `true` in the following cases:
 *
 *     var a = a
 *     var [a = a] = list
 *     var {a = a} = obj
 *     for (var a in a) {}
 *     for (var a of a) {}
 *     var C = class { [C]; };
 *     var C = class { static foo = C; };
 *     var C = class { static { foo = C; } };
 *     class C extends C {}
 *     class C extends (class { static foo = C; }) {}
 *     class C { [C]; }
 * @param {Reference} reference A reference to check.
 * @returns {boolean} `true` if the reference is evaluated during the initialization.
 */
function isEvaluatedDuringInitialization(reference) {
    if (isFromSeparateExecutionContext(reference)) {

        /*
         * Even if the reference appears in the initializer, it isn't evaluated during the initialization.
         * For example, `const x = () => x;` is valid.
         */
        return false;
    }

    const location = reference.identifier.range[1];
    const definition = reference.resolved.defs[0];

    if (definition.type === "ClassName") {

        // `ClassDeclaration` or `ClassExpression`
        const classDefinition = definition.node;

        return (
            isInRange(classDefinition, location) &&

            /*
             * Class binding is initialized before running static initializers.
             * For example, `class C { static foo = C; static { bar = C; } }` is valid.
             */
            !isInClassStaticInitializerRange(classDefinition.body, location)
        );
    }

    let node = definition.name.parent;

    while (node) {
        if (node.type === "VariableDeclarator") {
            if (isInRange(node.init, location)) {
                return true;
            }
            if (FOR_IN_OF_TYPE.test(node.parent.parent.type) &&
                isInRange(node.parent.parent.right, location)
            ) {
                return true;
            }
            break;
        } else if (node.type === "AssignmentPattern") {
            if (isInRange(node.right, location)) {
                return true;
            }
        } else if (SENTINEL_TYPE.test(node.type)) {
            break;
        }

        node = node.parent;
    }

    return false;
}

//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------

/** @type {import('../shared/types').Rule} */
module.exports = {
    meta: {
        type: "problem",

        docs: {
            description: "Disallow the use of variables before they are defined",
            recommended: false,
            url: "https://eslint.org/docs/latest/rules/no-use-before-define"
        },

        schema: [
            {
                oneOf: [
                    {
                        enum: ["nofunc"]
                    },
                    {
                        type: "object",
                        properties: {
                            functions: { type: "boolean" },
                            classes: { type: "boolean" },
                            variables: { type: "boolean" },
                            allowNamedExports: { type: "boolean" }
                        },
                        additionalProperties: false
                    }
                ]
            }
        ],

        messages: {
            usedBeforeDefined: "'{{name}}' was used before it was defined."
        }
    },

    create(context) {
        const options = parseOptions(context.options[0]);
        const sourceCode = context.sourceCode;

        /**
         * Determines whether a given reference should be checked.
         *
         * Returns `false` if the reference is:
         * - initialization's (e.g., `let a = 1`).
         * - referring to an undefined variable (i.e., if it's an unresolved reference).
         * - referring to a variable that is defined, but not in the given source code
         *   (e.g., global environment variable or `arguments` in functions).
         * - allowed by options.
         * @param {eslint-scope.Reference} reference The reference
         * @returns {boolean} `true` if the reference should be checked
         */
        function shouldCheck(reference) {
            if (reference.init) {
                return false;
            }

            const { identifier } = reference;

            if (
                options.allowNamedExports &&
                identifier.parent.type === "ExportSpecifier" &&
                identifier.parent.local === identifier
            ) {
                return false;
            }

            const variable = reference.resolved;

            if (!variable || variable.defs.length === 0) {
                return false;
            }

            const definitionType = variable.defs[0].type;

            if (!options.functions && definitionType === "FunctionName") {
                return false;
            }

            if (
                (
                    !options.variables && definitionType === "Variable" ||
                    !options.classes && definitionType === "ClassName"
                ) &&

                // don't skip checking the reference if it's in the same execution context, because of TDZ
                isFromSeparateExecutionContext(reference)
            ) {
                return false;
            }

            return true;
        }

        /**
         * Finds and validates all references in a given scope and its child scopes.
         * @param {eslint-scope.Scope} scope The scope object.
         * @returns {void}
         */
        function checkReferencesInScope(scope) {
            scope.references.filter(shouldCheck).forEach(reference => {
                const variable = reference.resolved;
                const definitionIdentifier = variable.defs[0].name;

                if (
                    reference.identifier.range[1] < definitionIdentifier.range[1] ||
                    isEvaluatedDuringInitialization(reference)
                ) {
                    context.report({
                        node: reference.identifier,
                        messageId: "usedBeforeDefined",
                        data: reference.identifier
                    });
                }
            });

            scope.childScopes.forEach(checkReferencesInScope);
        }

        return {
            Program(node) {
                checkReferencesInScope(sourceCode.getScope(node));
            }
        };
    }
};


code:
/**
 * @fileoverview Rule to disallow useless backreferences in regular expressions
 * @author Milos Djermanovic
 */

"use strict";

//------------------------------------------------------------------------------
// Requirements
//------------------------------------------------------------------------------

const { CALL, CONSTRUCT, ReferenceTracker, getStringIfConstant } = require("@eslint-community/eslint-utils");
const { RegExpParser, visitRegExpAST } = require("@eslint-community/regexpp");

//------------------------------------------------------------------------------
// Helpers
//------------------------------------------------------------------------------

const parser = new RegExpParser();

/**
 * Finds the path from the given `regexpp` AST node to the root node.
 * @param {regexpp.Node} node Node.
 * @returns {regexpp.Node[]} Array that starts with the given node and ends with the root node.
 */
function getPathToRoot(node) {
    const path = [];
    let current = node;

    do {
        path.push(current);
        current = current.parent;
    } while (current);

    return path;
}

/**
 * Determines whether the given `regexpp` AST node is a lookaround node.
 * @param {regexpp.Node} node Node.
 * @returns {boolean} `true` if it is a lookaround node.
 */
function isLookaround(node) {
    return node.type === "Assertion" &&
        (node.kind === "lookahead" || node.kind === "lookbehind");
}

/**
 * Determines whether the given `regexpp` AST node is a negative lookaround node.
 * @param {regexpp.Node} node Node.
 * @returns {boolean} `true` if it is a negative lookaround node.
 */
function isNegativeLookaround(node) {
    return isLookaround(node) && node.negate;
}

//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------

/** @type {import('../shared/types').Rule} */
module.exports = {
    meta: {
        type: "problem",

        docs: {
            description: "Disallow useless backreferences in regular expressions",
            recommended: true,
            url: "https://eslint.org/docs/latest/rules/no-useless-backreference"
        },

        schema: [],

        messages: {
            nested: "Backreference '{{ bref }}' will be ignored. It references group '{{ group }}' from within that group.",
            forward: "Backreference '{{ bref }}' will be ignored. It references group '{{ group }}' which appears later in the pattern.",
            backward: "Backreference '{{ bref }}' will be ignored. It references group '{{ group }}' which appears before in the same lookbehind.",
            disjunctive: "Backreference '{{ bref }}' will be ignored. It references group '{{ group }}' which is in another alternative.",
            intoNegativeLookaround: "Backreference '{{ bref }}' will be ignored. It references group '{{ group }}' which is in a negative lookaround."
        }
    },

    create(context) {

        const sourceCode = context.sourceCode;

        /**
         * Checks and reports useless backreferences in the given regular expression.
         * @param {ASTNode} node Node that represents regular expression. A regex literal or RegExp constructor call.
         * @param {string} pattern Regular expression pattern.
         * @param {string} flags Regular expression flags.
         * @returns {void}
         */
        function checkRegex(node, pattern, flags) {
            let regExpAST;

            try {
                regExpAST = parser.parsePattern(pattern, 0, pattern.length, { unicode: flags.includes("u"), unicodeSets: flags.includes("v") });
            } catch {

                // Ignore regular expressions with syntax errors
                return;
            }

            visitRegExpAST(regExpAST, {
                onBackreferenceEnter(bref) {
                    const group = bref.resolved,
                        brefPath = getPathToRoot(bref),
                        groupPath = getPathToRoot(group);
                    let messageId = null;

                    if (brefPath.includes(group)) {

                        // group is bref's ancestor => bref is nested ('nested reference') => group hasn't matched yet when bref starts to match.
                        messageId = "nested";
                    } else {

                        // Start from the root to find the lowest common ancestor.
                        let i = brefPath.length - 1,
                            j = groupPath.length - 1;

                        do {
                            i--;
                            j--;
                        } while (brefPath[i] === groupPath[j]);

                        const indexOfLowestCommonAncestor = j + 1,
                            groupCut = groupPath.slice(0, indexOfLowestCommonAncestor),
                            commonPath = groupPath.slice(indexOfLowestCommonAncestor),
                            lowestCommonLookaround = commonPath.find(isLookaround),
                            isMatchingBackward = lowestCommonLookaround && lowestCommonLookaround.kind === "lookbehind";

                        if (!isMatchingBackward && bref.end <= group.start) {

                            // bref is left, group is right ('forward reference') => group hasn't matched yet when bref starts to match.
                            messageId = "forward";
                        } else if (isMatchingBackward && group.end <= bref.start) {

                            // the opposite of the previous when the regex is matching backward in a lookbehind context.
                            messageId = "backward";
                        } else if (groupCut[groupCut.length - 1].type === "Alternative") {

                            // group's and bref's ancestor nodes below the lowest common ancestor are sibling alternatives => they're disjunctive.
                            messageId = "disjunctive";
                        } else if (groupCut.some(isNegativeLookaround)) {

                            // group is in a negative lookaround which isn't bref's ancestor => group has already failed when bref starts to match.
                            messageId = "intoNegativeLookaround";
                        }
                    }

                    if (messageId) {
                        context.report({
                            node,
                            messageId,
                            data: {
                                bref: bref.raw,
                                group: group.raw
                            }
                        });
                    }
                }
            });
        }

        return {
            "Literal[regex]"(node) {
                const { pattern, flags } = node.regex;

                checkRegex(node, pattern, flags);
            },
            Program(node) {
                const scope = sourceCode.getScope(node),
                    tracker = new ReferenceTracker(scope),
                    traceMap = {
                        RegExp: {
                            [CALL]: true,
                            [CONSTRUCT]: true
                        }
                    };

                for (const { node: refNode } of tracker.iterateGlobalReferences(traceMap)) {
                    const [patternNode, flagsNode] = refNode.arguments,
                        pattern = getStringIfConstant(patternNode, scope),
                        flags = getStringIfConstant(flagsNode, scope);

                    if (typeof pattern === "string") {
                        checkRegex(refNode, pattern, flags || "");
                    }
                }
            }
        };
    }
};


code:
/**
 * @fileoverview A rule to disallow unnecessary `.call()` and `.apply()`.
 * @author Toru Nagashima
 */

"use strict";

const astUtils = require("./utils/ast-utils");

//------------------------------------------------------------------------------
// Helpers
//------------------------------------------------------------------------------

/**
 * Checks whether or not a node is a `.call()`/`.apply()`.
 * @param {ASTNode} node A CallExpression node to check.
 * @returns {boolean} Whether or not the node is a `.call()`/`.apply()`.
 */
function isCallOrNonVariadicApply(node) {
    const callee = astUtils.skipChainExpression(node.callee);

    return (
        callee.type === "MemberExpression" &&
        callee.property.type === "Identifier" &&
        callee.computed === false &&
        (
            (callee.property.name === "call" && node.arguments.length >= 1) ||
            (callee.property.name === "apply" && node.arguments.length === 2 && node.arguments[1].type === "ArrayExpression")
        )
    );
}

/**
 * Checks whether or not `thisArg` is not changed by `.call()`/`.apply()`.
 * @param {ASTNode|null} expectedThis The node that is the owner of the applied function.
 * @param {ASTNode} thisArg The node that is given to the first argument of the `.call()`/`.apply()`.
 * @param {SourceCode} sourceCode The ESLint source code object.
 * @returns {boolean} Whether or not `thisArg` is not changed by `.call()`/`.apply()`.
 */
function isValidThisArg(expectedThis, thisArg, sourceCode) {
    if (!expectedThis) {
        return astUtils.isNullOrUndefined(thisArg);
    }
    return astUtils.equalTokens(expectedThis, thisArg, sourceCode);
}

//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------

/** @type {import('../shared/types').Rule} */
module.exports = {
    meta: {
        type: "suggestion",

        docs: {
            description: "Disallow unnecessary calls to `.call()` and `.apply()`",
            recommended: false,
            url: "https://eslint.org/docs/latest/rules/no-useless-call"
        },

        schema: [],

        messages: {
            unnecessaryCall: "Unnecessary '.{{name}}()'."
        }
    },

    create(context) {
        const sourceCode = context.sourceCode;

        return {
            CallExpression(node) {
                if (!isCallOrNonVariadicApply(node)) {
                    return;
                }

                const callee = astUtils.skipChainExpression(node.callee);
                const applied = astUtils.skipChainExpression(callee.object);
                const expectedThis = (applied.type === "MemberExpression") ? applied.object : null;
                const thisArg = node.arguments[0];

                if (isValidThisArg(expectedThis, thisArg, sourceCode)) {
                    context.report({ node, messageId: "unnecessaryCall", data: { name: callee.property.name } });
                }
            }
        };
    }
};


code:
/**
 * @fileoverview Reports useless `catch` clauses that just rethrow their error.
 * @author Teddy Katz
 */

"use strict";

//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------

/** @type {import('../shared/types').Rule} */
module.exports = {
    meta: {
        type: "suggestion",

        docs: {
            description: "Disallow unnecessary `catch` clauses",
            recommended: true,
            url: "https://eslint.org/docs/latest/rules/no-useless-catch"
        },

        schema: [],

        messages: {
            unnecessaryCatchClause: "Unnecessary catch clause.",
            unnecessaryCatch: "Unnecessary try/catch wrapper."
        }
    },

    create(context) {
        return {
            CatchClause(node) {
                if (
                    node.param &&
                    node.param.type === "Identifier" &&
                    node.body.body.length &&
                    node.body.body[0].type === "ThrowStatement" &&
                    node.body.body[0].argument.type === "Identifier" &&
                    node.body.body[0].argument.name === node.param.name
                ) {
                    if (node.parent.finalizer) {
                        context.report({
                            node,
                            messageId: "unnecessaryCatchClause"
                        });
                    } else {
                        context.report({
                            node: node.parent,
                            messageId: "unnecessaryCatch"
                        });
                    }
                }
            }
        };
    }
};


code:
/**
 * @fileoverview Rule to disallow unnecessary computed property keys in object literals
 * @author Burak Yigit Kaya
 */
"use strict";

//------------------------------------------------------------------------------
// Requirements
//------------------------------------------------------------------------------

const astUtils = require("./utils/ast-utils");

//------------------------------------------------------------------------------
// Helpers
//------------------------------------------------------------------------------

/**
 * Determines whether the computed key syntax is unnecessarily used for the given node.
 * In particular, it determines whether removing the square brackets and using the content between them
 * directly as the key (e.g. ['foo'] -> 'foo') would produce valid syntax and preserve the same behavior.
 * Valid non-computed keys are only: identifiers, number literals and string literals.
 * Only literals can preserve the same behavior, with a few exceptions for specific node types:
 * Property
 *   - { ["__proto__"]: foo } defines a property named "__proto__"
 *     { "__proto__": foo } defines object's prototype
 * PropertyDefinition
 *   - class C { ["constructor"]; } defines an instance field named "constructor"
 *     class C { "constructor"; } produces a parsing error
 *   - class C { static ["constructor"]; } defines a static field named "constructor"
 *     class C { static "constructor"; } produces a parsing error
 *   - class C { static ["prototype"]; } produces a runtime error (doesn't break the whole script)
 *     class C { static "prototype"; } produces a parsing error (breaks the whole script)
 * MethodDefinition
 *   - class C { ["constructor"]() {} } defines a prototype method named "constructor"
 *     class C { "constructor"() {} } defines the constructor
 *   - class C { static ["prototype"]() {} } produces a runtime error (doesn't break the whole script)
 *     class C { static "prototype"() {} } produces a parsing error (breaks the whole script)
 * @param {ASTNode} node The node to check. It can be `Property`, `PropertyDefinition` or `MethodDefinition`.
 * @throws {Error} (Unreachable.)
 * @returns {void} `true` if the node has useless computed key.
 */
function hasUselessComputedKey(node) {
    if (!node.computed) {
        return false;
    }

    const { key } = node;

    if (key.type !== "Literal") {
        return false;
    }

    const { value } = key;

    if (typeof value !== "number" && typeof value !== "string") {
        return false;
    }

    switch (node.type) {
        case "Property":
            return value !== "__proto__";

        case "PropertyDefinition":
            if (node.static) {
                return value !== "constructor" && value !== "prototype";
            }

            return value !== "constructor";

        case "MethodDefinition":
            if (node.static) {
                return value !== "prototype";
            }

            return value !== "constructor";

        /* c8 ignore next */
        default:
            throw new Error(`Unexpected node type: ${node.type}`);
    }

}

//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------

/** @type {import('../shared/types').Rule} */
module.exports = {
    meta: {
        type: "suggestion",

        docs: {
            description: "Disallow unnecessary computed property keys in objects and classes",
            recommended: false,
            url: "https://eslint.org/docs/latest/rules/no-useless-computed-key"
        },

        schema: [{
            type: "object",
            properties: {
                enforceForClassMembers: {
                    type: "boolean",
                    default: false
                }
            },
            additionalProperties: false
        }],
        fixable: "code",

        messages: {
            unnecessarilyComputedProperty: "Unnecessarily computed property [{{property}}] found."
        }
    },
    create(context) {
        const sourceCode = context.sourceCode;
        const enforceForClassMembers = context.options[0] && context.options[0].enforceForClassMembers;

        /**
         * Reports a given node if it violated this rule.
         * @param {ASTNode} node The node to check.
         * @returns {void}
         */
        function check(node) {
            if (hasUselessComputedKey(node)) {
                const { key } = node;

                context.report({
                    node,
                    messageId: "unnecessarilyComputedProperty",
                    data: { property: sourceCode.getText(key) },
                    fix(fixer) {
                        const leftSquareBracket = sourceCode.getTokenBefore(key, astUtils.isOpeningBracketToken);
                        const rightSquareBracket = sourceCode.getTokenAfter(key, astUtils.isClosingBracketToken);

                        // If there are comments between the brackets and the property name, don't do a fix.
                        if (sourceCode.commentsExistBetween(leftSquareBracket, rightSquareBracket)) {
                            return null;
                        }

                        const tokenBeforeLeftBracket = sourceCode.getTokenBefore(leftSquareBracket);

                        // Insert a space before the key to avoid changing identifiers, e.g. ({ get[2]() {} }) to ({ get2() {} })
                        const needsSpaceBeforeKey = tokenBeforeLeftBracket.range[1] === leftSquareBracket.range[0] &&
                            !astUtils.canTokensBeAdjacent(tokenBeforeLeftBracket, sourceCode.getFirstToken(key));

                        const replacementKey = (needsSpaceBeforeKey ? " " : "") + key.raw;

                        return fixer.replaceTextRange([leftSquareBracket.range[0], rightSquareBracket.range[1]], replacementKey);
                    }
                });
            }
        }

        /**
         * A no-op function to act as placeholder for checking a node when the `enforceForClassMembers` option is `false`.
         * @returns {void}
         * @private
         */
        function noop() {}

        return {
            Property: check,
            MethodDefinition: enforceForClassMembers ? check : noop,
            PropertyDefinition: enforceForClassMembers ? check : noop
        };
    }
};


code:
/**
 * @fileoverview disallow unnecessary concatenation of template strings
 * @author Henry Zhu
 */
"use strict";

//------------------------------------------------------------------------------
// Requirements
//------------------------------------------------------------------------------

const astUtils = require("./utils/ast-utils");

//------------------------------------------------------------------------------
// Helpers
//------------------------------------------------------------------------------

/**
 * Checks whether or not a given node is a concatenation.
 * @param {ASTNode} node A node to check.
 * @returns {boolean} `true` if the node is a concatenation.
 */
function isConcatenation(node) {
    return node.type === "BinaryExpression" && node.operator === "+";
}

/**
 * Checks if the given token is a `+` token or not.
 * @param {Token} token The token to check.
 * @returns {boolean} `true` if the token is a `+` token.
 */
function isConcatOperatorToken(token) {
    return token.value === "+" && token.type === "Punctuator";
}

/**
 * Get's the right most node on the left side of a BinaryExpression with + operator.
 * @param {ASTNode} node A BinaryExpression node to check.
 * @returns {ASTNode} node
 */
function getLeft(node) {
    let left = node.left;

    while (isConcatenation(left)) {
        left = left.right;
    }
    return left;
}

/**
 * Get's the left most node on the right side of a BinaryExpression with + operator.
 * @param {ASTNode} node A BinaryExpression node to check.
 * @returns {ASTNode} node
 */
function getRight(node) {
    let right = node.right;

    while (isConcatenation(right)) {
        right = right.left;
    }
    return right;
}

//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------

/** @type {import('../shared/types').Rule} */
module.exports = {
    meta: {
        type: "suggestion",

        docs: {
            description: "Disallow unnecessary concatenation of literals or template literals",
            recommended: false,
            url: "https://eslint.org/docs/latest/rules/no-useless-concat"
        },

        schema: [],

        messages: {
            unexpectedConcat: "Unexpected string concatenation of literals."
        }
    },

    create(context) {
        const sourceCode = context.sourceCode;

        return {
            BinaryExpression(node) {

                // check if not concatenation
                if (node.operator !== "+") {
                    return;
                }

                // account for the `foo + "a" + "b"` case
                const left = getLeft(node);
                const right = getRight(node);

                if (astUtils.isStringLiteral(left) &&
                    astUtils.isStringLiteral(right) &&
                    astUtils.isTokenOnSameLine(left, right)
                ) {
                    const operatorToken = sourceCode.getFirstTokenBetween(left, right, isConcatOperatorToken);

                    context.report({
                        node,
                        loc: operatorToken.loc,
                        messageId: "unexpectedConcat"
                    });
                }
            }
        };
    }
};


code:
/**
 * @fileoverview Rule to flag the use of redundant constructors in classes.
 * @author Alberto Rodríguez
 */
"use strict";

//------------------------------------------------------------------------------
// Helpers
//------------------------------------------------------------------------------

/**
 * Checks whether a given array of statements is a single call of `super`.
 * @param {ASTNode[]} body An array of statements to check.
 * @returns {boolean} `true` if the body is a single call of `super`.
 */
function isSingleSuperCall(body) {
    return (
        body.length === 1 &&
        body[0].type === "ExpressionStatement" &&
        body[0].expression.type === "CallExpression" &&
        body[0].expression.callee.type === "Super"
    );
}

/**
 * Checks whether a given node is a pattern which doesn't have any side effects.
 * Default parameters and Destructuring parameters can have side effects.
 * @param {ASTNode} node A pattern node.
 * @returns {boolean} `true` if the node doesn't have any side effects.
 */
function isSimple(node) {
    return node.type === "Identifier" || node.type === "RestElement";
}

/**
 * Checks whether a given array of expressions is `...arguments` or not.
 * `super(...arguments)` passes all arguments through.
 * @param {ASTNode[]} superArgs An array of expressions to check.
 * @returns {boolean} `true` if the superArgs is `...arguments`.
 */
function isSpreadArguments(superArgs) {
    return (
        superArgs.length === 1 &&
        superArgs[0].type === "SpreadElement" &&
        superArgs[0].argument.type === "Identifier" &&
        superArgs[0].argument.name === "arguments"
    );
}

/**
 * Checks whether given 2 nodes are identifiers which have the same name or not.
 * @param {ASTNode} ctorParam A node to check.
 * @param {ASTNode} superArg A node to check.
 * @returns {boolean} `true` if the nodes are identifiers which have the same
 *      name.
 */
function isValidIdentifierPair(ctorParam, superArg) {
    return (
        ctorParam.type === "Identifier" &&
        superArg.type === "Identifier" &&
        ctorParam.name === superArg.name
    );
}

/**
 * Checks whether given 2 nodes are a rest/spread pair which has the same values.
 * @param {ASTNode} ctorParam A node to check.
 * @param {ASTNode} superArg A node to check.
 * @returns {boolean} `true` if the nodes are a rest/spread pair which has the
 *      same values.
 */
function isValidRestSpreadPair(ctorParam, superArg) {
    return (
        ctorParam.type === "RestElement" &&
        superArg.type === "SpreadElement" &&
        isValidIdentifierPair(ctorParam.argument, superArg.argument)
    );
}

/**
 * Checks whether given 2 nodes have the same value or not.
 * @param {ASTNode} ctorParam A node to check.
 * @param {ASTNode} superArg A node to check.
 * @returns {boolean} `true` if the nodes have the same value or not.
 */
function isValidPair(ctorParam, superArg) {
    return (
        isValidIdentifierPair(ctorParam, superArg) ||
        isValidRestSpreadPair(ctorParam, superArg)
    );
}

/**
 * Checks whether the parameters of a constructor and the arguments of `super()`
 * have the same values or not.
 * @param {ASTNode} ctorParams The parameters of a constructor to check.
 * @param {ASTNode} superArgs The arguments of `super()` to check.
 * @returns {boolean} `true` if those have the same values.
 */
function isPassingThrough(ctorParams, superArgs) {
    if (ctorParams.length !== superArgs.length) {
        return false;
    }

    for (let i = 0; i < ctorParams.length; ++i) {
        if (!isValidPair(ctorParams[i], superArgs[i])) {
            return false;
        }
    }

    return true;
}

/**
 * Checks whether the constructor body is a redundant super call.
 * @param {Array} body constructor body content.
 * @param {Array} ctorParams The params to check against super call.
 * @returns {boolean} true if the constructor body is redundant
 */
function isRedundantSuperCall(body, ctorParams) {
    return (
        isSingleSuperCall(body) &&
        ctorParams.every(isSimple) &&
        (
            isSpreadArguments(body[0].expression.arguments) ||
            isPassingThrough(ctorParams, body[0].expression.arguments)
        )
    );
}

//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------

/** @type {import('../shared/types').Rule} */
module.exports = {
    meta: {
        type: "suggestion",

        docs: {
            description: "Disallow unnecessary constructors",
            recommended: false,
            url: "https://eslint.org/docs/latest/rules/no-useless-constructor"
        },

        schema: [],

        messages: {
            noUselessConstructor: "Useless constructor."
        }
    },

    create(context) {

        /**
         * Checks whether a node is a redundant constructor
         * @param {ASTNode} node node to check
         * @returns {void}
         */
        function checkForConstructor(node) {
            if (node.kind !== "constructor") {
                return;
            }

            /*
             * Prevent crashing on parsers which do not require class constructor
             * to have a body, e.g. typescript and flow
             */
            if (!node.value.body) {
                return;
            }

            const body = node.value.body.body;
            const ctorParams = node.value.params;
            const superClass = node.parent.parent.superClass;

            if (superClass ? isRedundantSuperCall(body, ctorParams) : (body.length === 0)) {
                context.report({
                    node,
                    messageId: "noUselessConstructor"
                });
            }
        }

        return {
            MethodDefinition: checkForConstructor
        };
    }
};


code:
/**
 * @fileoverview Look for useless escapes in strings and regexes
 * @author Onur Temizkan
 */

"use strict";

const astUtils = require("./utils/ast-utils");
const { RegExpParser, visitRegExpAST } = require("@eslint-community/regexpp");

/**
 * @typedef {import('@eslint-community/regexpp').AST.CharacterClass} CharacterClass
 * @typedef {import('@eslint-community/regexpp').AST.ExpressionCharacterClass} ExpressionCharacterClass
 */
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------

/**
 * Returns the union of two sets.
 * @param {Set} setA The first set
 * @param {Set} setB The second set
 * @returns {Set} The union of the two sets
 */
function union(setA, setB) {
    return new Set(function *() {
        yield* setA;
        yield* setB;
    }());
}

const VALID_STRING_ESCAPES = union(new Set("\\nrvtbfux"), astUtils.LINEBREAKS);
const REGEX_GENERAL_ESCAPES = new Set("\\bcdDfnpPrsStvwWxu0123456789]");
const REGEX_NON_CHARCLASS_ESCAPES = union(REGEX_GENERAL_ESCAPES, new Set("^/.$*+?[{}|()Bk"));

/*
 * Set of characters that require escaping in character classes in `unicodeSets` mode.
 * ( ) [ ] { } / - \ | are ClassSetSyntaxCharacter
 */
const REGEX_CLASSSET_CHARACTER_ESCAPES = union(REGEX_GENERAL_ESCAPES, new Set("q/[{}|()-"));

/*
 * A single character set of ClassSetReservedDoublePunctuator.
 * && !! ## $$ %% ** ++ ,, .. :: ;; << == >> ?? @@ ^^ `` ~~ are ClassSetReservedDoublePunctuator
 */
const REGEX_CLASS_SET_RESERVED_DOUBLE_PUNCTUATOR = new Set("!#$%&*+,.:;<=>?@^`~");

/** @type {import('../shared/types').Rule} */
module.exports = {
    meta: {
        type: "suggestion",

        docs: {
            description: "Disallow unnecessary escape characters",
            recommended: true,
            url: "https://eslint.org/docs/latest/rules/no-useless-escape"
        },

        hasSuggestions: true,

        messages: {
            unnecessaryEscape: "Unnecessary escape character: \\{{character}}.",
            removeEscape: "Remove the `\\`. This maintains the current functionality.",
            removeEscapeDoNotKeepSemantics: "Remove the `\\` if it was inserted by mistake.",
            escapeBackslash: "Replace the `\\` with `\\\\` to include the actual backslash character."
        },

        schema: []
    },

    create(context) {
        const sourceCode = context.sourceCode;
        const parser = new RegExpParser();

        /**
         * Reports a node
         * @param {ASTNode} node The node to report
         * @param {number} startOffset The backslash's offset from the start of the node
         * @param {string} character The uselessly escaped character (not including the backslash)
         * @param {boolean} [disableEscapeBackslashSuggest] `true` if escapeBackslash suggestion should be turned off.
         * @returns {void}
         */
        function report(node, startOffset, character, disableEscapeBackslashSuggest) {
            const rangeStart = node.range[0] + startOffset;
            const range = [rangeStart, rangeStart + 1];
            const start = sourceCode.getLocFromIndex(rangeStart);

            context.report({
                node,
                loc: {
                    start,
                    end: { line: start.line, column: start.column + 1 }
                },
                messageId: "unnecessaryEscape",
                data: { character },
                suggest: [
                    {

                        // Removing unnecessary `\` characters in a directive is not guaranteed to maintain functionality.
                        messageId: astUtils.isDirective(node.parent)
                            ? "removeEscapeDoNotKeepSemantics" : "removeEscape",
                        fix(fixer) {
                            return fixer.removeRange(range);
                        }
                    },
                    ...disableEscapeBackslashSuggest
                        ? []
                        : [
                            {
                                messageId: "escapeBackslash",
                                fix(fixer) {
                                    return fixer.insertTextBeforeRange(range, "\\");
                                }
                            }
                        ]
                ]
            });
        }

        /**
         * Checks if the escape character in given string slice is unnecessary.
         * @private
         * @param {ASTNode} node node to validate.
         * @param {string} match string slice to validate.
         * @returns {void}
         */
        function validateString(node, match) {
            const isTemplateElement = node.type === "TemplateElement";
            const escapedChar = match[0][1];
            let isUnnecessaryEscape = !VALID_STRING_ESCAPES.has(escapedChar);
            let isQuoteEscape;

            if (isTemplateElement) {
                isQuoteEscape = escapedChar === "`";

                if (escapedChar === "$") {

                    // Warn if `\$` is not followed by `{`
                    isUnnecessaryEscape = match.input[match.index + 2] !== "{";
                } else if (escapedChar === "{") {

                    /*
                     * Warn if `\{` is not preceded by `$`. If preceded by `$`, escaping
                     * is necessary and the rule should not warn. If preceded by `/$`, the rule
                     * will warn for the `/$` instead, as it is the first unnecessarily escaped character.
                     */
                    isUnnecessaryEscape = match.input[match.index - 1] !== "$";
                }
            } else {
                isQuoteEscape = escapedChar === node.raw[0];
            }

            if (isUnnecessaryEscape && !isQuoteEscape) {
                report(node, match.index, match[0].slice(1));
            }
        }

        /**
         * Checks if the escape character in given regexp is unnecessary.
         * @private
         * @param {ASTNode} node node to validate.
         * @returns {void}
         */
        function validateRegExp(node) {
            const { pattern, flags } = node.regex;
            let patternNode;
            const unicode = flags.includes("u");
            const unicodeSets = flags.includes("v");

            try {
                patternNode = parser.parsePattern(pattern, 0, pattern.length, { unicode, unicodeSets });
            } catch {

                // Ignore regular expressions with syntax errors
                return;
            }

            /** @type {(CharacterClass | ExpressionCharacterClass)[]} */
            const characterClassStack = [];

            visitRegExpAST(patternNode, {
                onCharacterClassEnter: characterClassNode => characterClassStack.unshift(characterClassNode),
                onCharacterClassLeave: () => characterClassStack.shift(),
                onExpressionCharacterClassEnter: characterClassNode => characterClassStack.unshift(characterClassNode),
                onExpressionCharacterClassLeave: () => characterClassStack.shift(),
                onCharacterEnter(characterNode) {
                    if (!characterNode.raw.startsWith("\\")) {

                        // It's not an escaped character.
                        return;
                    }

                    const escapedChar = characterNode.raw.slice(1);

                    if (escapedChar !== String.fromCodePoint(characterNode.value)) {

                        // It's a valid escape.
                        return;
                    }
                    let allowedEscapes;

                    if (characterClassStack.length) {
                        allowedEscapes = unicodeSets ? REGEX_CLASSSET_CHARACTER_ESCAPES : REGEX_GENERAL_ESCAPES;
                    } else {
                        allowedEscapes = REGEX_NON_CHARCLASS_ESCAPES;
                    }
                    if (allowedEscapes.has(escapedChar)) {
                        return;
                    }

                    const reportedIndex = characterNode.start + 1;
                    let disableEscapeBackslashSuggest = false;

                    if (characterClassStack.length) {
                        const characterClassNode = characterClassStack[0];

                        if (escapedChar === "^") {

                            /*
                             * The '^' character is also a special case; it must always be escaped outside of character classes, but
                             * it only needs to be escaped in character classes if it's at the beginning of the character class. To
                             * account for this, consider it to be a valid escape character outside of character classes, and filter
                             * out '^' characters that appear at the start of a character class.
                             */
                            if (characterClassNode.start + 1 === characterNode.start) {

                                return;
                            }
                        }
                        if (!unicodeSets) {
                            if (escapedChar === "-") {

                                /*
                                 * The '-' character is a special case, because it's only valid to escape it if it's in a character
                                 * class, and is not at either edge of the character class. To account for this, don't consider '-'
                                 * characters to be valid in general, and filter out '-' characters that appear in the middle of a
                                 * character class.
                                 */
                                if (characterClassNode.start + 1 !== characterNode.start && characterNode.end !== characterClassNode.end - 1) {

                                    return;
                                }
                            }
                        } else { // unicodeSets mode
                            if (REGEX_CLASS_SET_RESERVED_DOUBLE_PUNCTUATOR.has(escapedChar)) {

                                // Escaping is valid if it is a ClassSetReservedDoublePunctuator.
                                if (pattern[characterNode.end] === escapedChar) {
                                    return;
                                }
                                if (pattern[characterNode.start - 1] === escapedChar) {
                                    if (escapedChar !== "^") {
                                        return;
                                    }

                                    // If the previous character is a `negate` caret(`^`), escape to caret is unnecessary.

                                    if (!characterClassNode.negate) {
                                        return;
                                    }
                                    const negateCaretIndex = characterClassNode.start + 1;

                                    if (negateCaretIndex < characterNode.start - 1) {
                                        return;
                                    }
                                }
                            }

                            if (characterNode.parent.type === "ClassIntersection" || characterNode.parent.type === "ClassSubtraction") {
                                disableEscapeBackslashSuggest = true;
                            }
                        }
                    }

                    report(
                        node,
                        reportedIndex,
                        escapedChar,
                        disableEscapeBackslashSuggest
                    );
                }
            });
        }

        /**
         * Checks if a node has an escape.
         * @param {ASTNode} node node to check.
         * @returns {void}
         */
        function check(node) {
            const isTemplateElement = node.type === "TemplateElement";

            if (
                isTemplateElement &&
                node.parent &&
                node.parent.parent &&
                node.parent.parent.type === "TaggedTemplateExpression" &&
                node.parent === node.parent.parent.quasi
            ) {

                // Don't report tagged template literals, because the backslash character is accessible to the tag function.
                return;
            }

            if (typeof node.value === "string" || isTemplateElement) {

                /*
                 * JSXAttribute doesn't have any escape sequence: https://facebook.github.io/jsx/.
                 * In addition, backticks are not supported by JSX yet: https://github.com/facebook/jsx/issues/25.
                 */
                if (node.parent.type === "JSXAttribute" || node.parent.type === "JSXElement" || node.parent.type === "JSXFragment") {
                    return;
                }

                const value = isTemplateElement ? sourceCode.getText(node) : node.raw;
                const pattern = /\\[^\d]/gu;
                let match;

                while ((match = pattern.exec(value))) {
                    validateString(node, match);
                }
            } else if (node.regex) {
                validateRegExp(node);
            }

        }

        return {
            Literal: check,
            TemplateElement: check
        };
    }
};


code:
/**
 * @fileoverview Disallow renaming import, export, and destructured assignments to the same name.
 * @author Kai Cataldo
 */

"use strict";

//------------------------------------------------------------------------------
// Requirements
//------------------------------------------------------------------------------

const astUtils = require("./utils/ast-utils");

//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------

/** @type {import('../shared/types').Rule} */
module.exports = {
    meta: {
        type: "suggestion",

        docs: {
            description: "Disallow renaming import, export, and destructured assignments to the same name",
            recommended: false,
            url: "https://eslint.org/docs/latest/rules/no-useless-rename"
        },

        fixable: "code",

        schema: [
            {
                type: "object",
                properties: {
                    ignoreDestructuring: { type: "boolean", default: false },
                    ignoreImport: { type: "boolean", default: false },
                    ignoreExport: { type: "boolean", default: false }
                },
                additionalProperties: false
            }
        ],

        messages: {
            unnecessarilyRenamed: "{{type}} {{name}} unnecessarily renamed."
        }
    },

    create(context) {
        const sourceCode = context.sourceCode,
            options = context.options[0] || {},
            ignoreDestructuring = options.ignoreDestructuring === true,
            ignoreImport = options.ignoreImport === true,
            ignoreExport = options.ignoreExport === true;

        //--------------------------------------------------------------------------
        // Helpers
        //--------------------------------------------------------------------------

        /**
         * Reports error for unnecessarily renamed assignments
         * @param {ASTNode} node node to report
         * @param {ASTNode} initial node with initial name value
         * @param {string} type the type of the offending node
         * @returns {void}
         */
        function reportError(node, initial, type) {
            const name = initial.type === "Identifier" ? initial.name : initial.value;

            return context.report({
                node,
                messageId: "unnecessarilyRenamed",
                data: {
                    name,
                    type
                },
                fix(fixer) {
                    const replacementNode = node.type === "Property" ? node.value : node.local;

                    if (sourceCode.getCommentsInside(node).length > sourceCode.getCommentsInside(replacementNode).length) {
                        return null;
                    }

                    // Don't autofix code such as `({foo: (foo) = a} = obj);`, parens are not allowed in shorthand properties.
                    if (
                        replacementNode.type === "AssignmentPattern" &&
                        astUtils.isParenthesised(sourceCode, replacementNode.left)
                    ) {
                        return null;
                    }

                    return fixer.replaceText(node, sourceCode.getText(replacementNode));
                }
            });
        }

        /**
         * Checks whether a destructured assignment is unnecessarily renamed
         * @param {ASTNode} node node to check
         * @returns {void}
         */
        function checkDestructured(node) {
            if (ignoreDestructuring) {
                return;
            }

            for (const property of node.properties) {

                /**
                 * Properties using shorthand syntax and rest elements can not be renamed.
                 * If the property is computed, we have no idea if a rename is useless or not.
                 */
                if (property.type !== "Property" || property.shorthand || property.computed) {
                    continue;
                }

                const key = (property.key.type === "Identifier" && property.key.name) || (property.key.type === "Literal" && property.key.value);
                const renamedKey = property.value.type === "AssignmentPattern" ? property.value.left.name : property.value.name;

                if (key === renamedKey) {
                    reportError(property, property.key, "Destructuring assignment");
                }
            }
        }

        /**
         * Checks whether an import is unnecessarily renamed
         * @param {ASTNode} node node to check
         * @returns {void}
         */
        function checkImport(node) {
            if (ignoreImport) {
                return;
            }

            if (
                node.imported.range[0] !== node.local.range[0] &&
                astUtils.getModuleExportName(node.imported) === node.local.name
            ) {
                reportError(node, node.imported, "Import");
            }
        }

        /**
         * Checks whether an export is unnecessarily renamed
         * @param {ASTNode} node node to check
         * @returns {void}
         */
        function checkExport(node) {
            if (ignoreExport) {
                return;
            }

            if (
                node.local.range[0] !== node.exported.range[0] &&
                astUtils.getModuleExportName(node.local) === astUtils.getModuleExportName(node.exported)
            ) {
                reportError(node, node.local, "Export");
            }

        }

        //--------------------------------------------------------------------------
        // Public
        //--------------------------------------------------------------------------

        return {
            ObjectPattern: checkDestructured,
            ImportSpecifier: checkImport,
            ExportSpecifier: checkExport
        };
    }
};