123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218 |
- /**
- * @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: {
- type: "problem",
-
- 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: [],
-
- messages: {
- unreachableCode: "Unreachable code."
- }
- },
-
- 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({
- messageId: "unreachableCode",
- 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,
- 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();
- }
- };
- }
- };
|