/**
							 | 
						|
								 * @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;
							 | 
						|
								                }
							 | 
						|
								            }
							 | 
						|
								        };
							 | 
						|
								    }
							 | 
						|
								};
							 |