|                                                                                                                                                                                                                                                                                                     |  | /** * @fileoverview Checks for unreachable code due to return, throws, break, and continue. * @author Joel Feenstra */"use strict";
//------------------------------------------------------------------------------
// Helpers
//------------------------------------------------------------------------------
/** * @typedef {Object} ConstructorInfo * @property {ConstructorInfo | null} upper Info about the constructor that encloses this constructor. * @property {boolean} hasSuperCall The flag about having `super()` expressions. */
/** * Checks whether or not a given variable declarator has the initializer. * @param {ASTNode} node A VariableDeclarator node to check. * @returns {boolean} `true` if the node has the initializer. */function isInitialized(node) {    return Boolean(node.init);}
/** * Checks all segments in a set and returns true if all are unreachable. * @param {Set<CodePathSegment>} segments The segments to check. * @returns {boolean} True if all segments are unreachable; false otherwise. */function areAllSegmentsUnreachable(segments) {
    for (const segment of segments) {        if (segment.reachable) {            return false;        }    }
    return true;}
/** * The class to distinguish consecutive unreachable statements. */class ConsecutiveRange {    constructor(sourceCode) {        this.sourceCode = sourceCode;        this.startNode = null;        this.endNode = null;    }
    /**     * The location object of this range.     * @type {Object}     */    get location() {        return {            start: this.startNode.loc.start,            end: this.endNode.loc.end        };    }
    /**     * `true` if this range is empty.     * @type {boolean}     */    get isEmpty() {        return !(this.startNode && this.endNode);    }
    /**     * Checks whether the given node is inside of this range.     * @param {ASTNode|Token} node The node to check.     * @returns {boolean} `true` if the node is inside of this range.     */    contains(node) {        return (            node.range[0] >= this.startNode.range[0] &&            node.range[1] <= this.endNode.range[1]        );    }
    /**     * Checks whether the given node is consecutive to this range.     * @param {ASTNode} node The node to check.     * @returns {boolean} `true` if the node is consecutive to this range.     */    isConsecutive(node) {        return this.contains(this.sourceCode.getTokenBefore(node));    }
    /**     * Merges the given node to this range.     * @param {ASTNode} node The node to merge.     * @returns {void}     */    merge(node) {        this.endNode = node;    }
    /**     * Resets this range by the given node or null.     * @param {ASTNode|null} node The node to reset, or null.     * @returns {void}     */    reset(node) {        this.startNode = this.endNode = node;    }}
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
/** @type {import('../shared/types').Rule} */module.exports = {    meta: {        type: "problem",
        docs: {            description: "Disallow unreachable code after `return`, `throw`, `continue`, and `break` statements",            recommended: true,            url: "https://eslint.org/docs/latest/rules/no-unreachable"        },
        schema: [],
        messages: {            unreachableCode: "Unreachable code."        }    },
    create(context) {
        /** @type {ConstructorInfo | null} */        let constructorInfo = null;
        /** @type {ConsecutiveRange} */        const range = new ConsecutiveRange(context.sourceCode);
        /** @type {Array<Set<CodePathSegment>>} */        const codePathSegments = [];
        /** @type {Set<CodePathSegment>} */        let currentCodePathSegments = new Set();
        /**         * Reports a given node if it's unreachable.         * @param {ASTNode} node A statement node to report.         * @returns {void}         */        function reportIfUnreachable(node) {            let nextNode = null;
            if (node && (node.type === "PropertyDefinition" || areAllSegmentsUnreachable(currentCodePathSegments))) {
                // Store this statement to distinguish consecutive statements.
                if (range.isEmpty) {                    range.reset(node);                    return;                }
                // Skip if this statement is inside of the current range.
                if (range.contains(node)) {                    return;                }
                // Merge if this statement is consecutive to the current range.
                if (range.isConsecutive(node)) {                    range.merge(node);                    return;                }
                nextNode = node;            }
            /*             * Report the current range since this statement is reachable or is             * not consecutive to the current range.             */            if (!range.isEmpty) {                context.report({                    messageId: "unreachableCode",                    loc: range.location,                    node: range.startNode                });            }
            // Update the current range.
            range.reset(nextNode);        }
        return {
            // Manages the current code path.
            onCodePathStart() {                codePathSegments.push(currentCodePathSegments);                currentCodePathSegments = new Set();            },
            onCodePathEnd() {                currentCodePathSegments = codePathSegments.pop();            },
            onUnreachableCodePathSegmentStart(segment) {                currentCodePathSegments.add(segment);            },
            onUnreachableCodePathSegmentEnd(segment) {                currentCodePathSegments.delete(segment);            },
            onCodePathSegmentEnd(segment) {                currentCodePathSegments.delete(segment);            },
            onCodePathSegmentStart(segment) {                currentCodePathSegments.add(segment);            },
            // Registers for all statement nodes (excludes FunctionDeclaration).
            BlockStatement: reportIfUnreachable,            BreakStatement: reportIfUnreachable,            ClassDeclaration: reportIfUnreachable,            ContinueStatement: reportIfUnreachable,            DebuggerStatement: reportIfUnreachable,            DoWhileStatement: reportIfUnreachable,            ExpressionStatement: reportIfUnreachable,            ForInStatement: reportIfUnreachable,            ForOfStatement: reportIfUnreachable,            ForStatement: reportIfUnreachable,            IfStatement: reportIfUnreachable,            ImportDeclaration: reportIfUnreachable,            LabeledStatement: reportIfUnreachable,            ReturnStatement: reportIfUnreachable,            SwitchStatement: reportIfUnreachable,            ThrowStatement: reportIfUnreachable,            TryStatement: reportIfUnreachable,
            VariableDeclaration(node) {                if (node.kind !== "var" || node.declarations.some(isInitialized)) {                    reportIfUnreachable(node);                }            },
            WhileStatement: reportIfUnreachable,            WithStatement: reportIfUnreachable,            ExportNamedDeclaration: reportIfUnreachable,            ExportDefaultDeclaration: reportIfUnreachable,            ExportAllDeclaration: reportIfUnreachable,
            "Program:exit"() {                reportIfUnreachable();            },
            /*             * Instance fields defined in a subclass are never created if the constructor of the subclass             * doesn't call `super()`, so their definitions are unreachable code.             */            "MethodDefinition[kind='constructor']"() {                constructorInfo = {                    upper: constructorInfo,                    hasSuperCall: false                };            },            "MethodDefinition[kind='constructor']:exit"(node) {                const { hasSuperCall } = constructorInfo;
                constructorInfo = constructorInfo.upper;
                // skip typescript constructors without the body
                if (!node.value.body) {                    return;                }
                const classDefinition = node.parent.parent;
                if (classDefinition.superClass && !hasSuperCall) {                    for (const element of classDefinition.body.body) {                        if (element.type === "PropertyDefinition" && !element.static) {                            reportIfUnreachable(element);                        }                    }                }            },            "CallExpression > Super.callee"() {                if (constructorInfo) {                    constructorInfo.hasSuperCall = true;                }            }        };    }};
 |