| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213 | /** * @fileoverview Checks for unreachable code due to return, throws, break, and continue. * @author Joel Feenstra */"use strict";//------------------------------------------------------------------------------// Helpers//------------------------------------------------------------------------------/** * 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 whether or not a given code path segment is unreachable. * @param {CodePathSegment} segment - A CodePathSegment to check. * @returns {boolean} `true` if the segment is unreachable. */function isUnreachable(segment) {    return !segment.reachable;}/** * 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//------------------------------------------------------------------------------module.exports = {    meta: {        docs: {            description: "disallow unreachable code after `return`, `throw`, `continue`, and `break` statements",            category: "Possible Errors",            recommended: true,            url: "https://eslint.org/docs/rules/no-unreachable"        },        schema: []    },    create(context) {        let currentCodePath = null;        const range = new ConsecutiveRange(context.getSourceCode());        /**         * 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 && currentCodePath.currentSegments.every(isUnreachable)) {                // 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({                    message: "Unreachable code.",                    loc: range.location,                    node: range.startNode                });            }            // Update the current range.            range.reset(nextNode);        }        return {            // Manages the current code path.            onCodePathStart(codePath) {                currentCodePath = codePath;            },            onCodePathEnd() {                currentCodePath = currentCodePath.upper;            },            // Registers for all statement nodes (excludes FunctionDeclaration).            BlockStatement: reportIfUnreachable,            BreakStatement: reportIfUnreachable,            ClassDeclaration: reportIfUnreachable,            ContinueStatement: reportIfUnreachable,            DebuggerStatement: reportIfUnreachable,            DoWhileStatement: reportIfUnreachable,            EmptyStatement: 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();            }        };    }};
 |