]>
git.proxmox.com Git - pve-eslint.git/blob - eslint/lib/rules/no-unreachable.js
dea86815aac5b98180b4f95dc76a6d5732f9658f
2 * @fileoverview Checks for unreachable code due to return, throws, break, and continue.
3 * @author Joel Feenstra
7 //------------------------------------------------------------------------------
9 //------------------------------------------------------------------------------
12 * @typedef {Object} ConstructorInfo
13 * @property {ConstructorInfo | null} upper Info about the constructor that encloses this constructor.
14 * @property {boolean} hasSuperCall The flag about having `super()` expressions.
18 * Checks whether or not a given variable declarator has the initializer.
19 * @param {ASTNode} node A VariableDeclarator node to check.
20 * @returns {boolean} `true` if the node has the initializer.
22 function isInitialized(node
) {
23 return Boolean(node
.init
);
27 * Checks whether or not a given code path segment is unreachable.
28 * @param {CodePathSegment} segment A CodePathSegment to check.
29 * @returns {boolean} `true` if the segment is unreachable.
31 function isUnreachable(segment
) {
32 return !segment
.reachable
;
36 * The class to distinguish consecutive unreachable statements.
38 class ConsecutiveRange
{
39 constructor(sourceCode
) {
40 this.sourceCode
= sourceCode
;
41 this.startNode
= null;
46 * The location object of this range.
51 start
: this.startNode
.loc
.start
,
52 end
: this.endNode
.loc
.end
57 * `true` if this range is empty.
61 return !(this.startNode
&& this.endNode
);
65 * Checks whether the given node is inside of this range.
66 * @param {ASTNode|Token} node The node to check.
67 * @returns {boolean} `true` if the node is inside of this range.
71 node
.range
[0] >= this.startNode
.range
[0] &&
72 node
.range
[1] <= this.endNode
.range
[1]
77 * Checks whether the given node is consecutive to this range.
78 * @param {ASTNode} node The node to check.
79 * @returns {boolean} `true` if the node is consecutive to this range.
82 return this.contains(this.sourceCode
.getTokenBefore(node
));
86 * Merges the given node to this range.
87 * @param {ASTNode} node The node to merge.
95 * Resets this range by the given node or null.
96 * @param {ASTNode|null} node The node to reset, or null.
100 this.startNode
= this.endNode
= node
;
104 //------------------------------------------------------------------------------
106 //------------------------------------------------------------------------------
108 /** @type {import('../shared/types').Rule} */
114 description
: "Disallow unreachable code after `return`, `throw`, `continue`, and `break` statements",
116 url
: "https://eslint.org/docs/rules/no-unreachable"
122 unreachableCode
: "Unreachable code."
127 let currentCodePath
= null;
129 /** @type {ConstructorInfo | null} */
130 let constructorInfo
= null;
132 /** @type {ConsecutiveRange} */
133 const range
= new ConsecutiveRange(context
.getSourceCode());
136 * Reports a given node if it's unreachable.
137 * @param {ASTNode} node A statement node to report.
140 function reportIfUnreachable(node
) {
143 if (node
&& (node
.type
=== "PropertyDefinition" || currentCodePath
.currentSegments
.every(isUnreachable
))) {
145 // Store this statement to distinguish consecutive statements.
151 // Skip if this statement is inside of the current range.
152 if (range
.contains(node
)) {
156 // Merge if this statement is consecutive to the current range.
157 if (range
.isConsecutive(node
)) {
166 * Report the current range since this statement is reachable or is
167 * not consecutive to the current range.
169 if (!range
.isEmpty
) {
171 messageId
: "unreachableCode",
173 node
: range
.startNode
177 // Update the current range.
178 range
.reset(nextNode
);
183 // Manages the current code path.
184 onCodePathStart(codePath
) {
185 currentCodePath
= codePath
;
189 currentCodePath
= currentCodePath
.upper
;
192 // Registers for all statement nodes (excludes FunctionDeclaration).
193 BlockStatement
: reportIfUnreachable
,
194 BreakStatement
: reportIfUnreachable
,
195 ClassDeclaration
: reportIfUnreachable
,
196 ContinueStatement
: reportIfUnreachable
,
197 DebuggerStatement
: reportIfUnreachable
,
198 DoWhileStatement
: reportIfUnreachable
,
199 ExpressionStatement
: reportIfUnreachable
,
200 ForInStatement
: reportIfUnreachable
,
201 ForOfStatement
: reportIfUnreachable
,
202 ForStatement
: reportIfUnreachable
,
203 IfStatement
: reportIfUnreachable
,
204 ImportDeclaration
: reportIfUnreachable
,
205 LabeledStatement
: reportIfUnreachable
,
206 ReturnStatement
: reportIfUnreachable
,
207 SwitchStatement
: reportIfUnreachable
,
208 ThrowStatement
: reportIfUnreachable
,
209 TryStatement
: reportIfUnreachable
,
211 VariableDeclaration(node
) {
212 if (node
.kind
!== "var" || node
.declarations
.some(isInitialized
)) {
213 reportIfUnreachable(node
);
217 WhileStatement
: reportIfUnreachable
,
218 WithStatement
: reportIfUnreachable
,
219 ExportNamedDeclaration
: reportIfUnreachable
,
220 ExportDefaultDeclaration
: reportIfUnreachable
,
221 ExportAllDeclaration
: reportIfUnreachable
,
224 reportIfUnreachable();
228 * Instance fields defined in a subclass are never created if the constructor of the subclass
229 * doesn't call `super()`, so their definitions are unreachable code.
231 "MethodDefinition[kind='constructor']"() {
233 upper
: constructorInfo
,
237 "MethodDefinition[kind='constructor']:exit"(node
) {
238 const { hasSuperCall
} = constructorInfo
;
240 constructorInfo
= constructorInfo
.upper
;
242 // skip typescript constructors without the body
243 if (!node
.value
.body
) {
247 const classDefinition
= node
.parent
.parent
;
249 if (classDefinition
.superClass
&& !hasSuperCall
) {
250 for (const element
of classDefinition
.body
.body
) {
251 if (element
.type
=== "PropertyDefinition" && !element
.static) {
252 reportIfUnreachable(element
);
257 "CallExpression > Super.callee"() {
258 if (constructorInfo
) {
259 constructorInfo
.hasSuperCall
= true;