]>
git.proxmox.com Git - pve-eslint.git/blob - eslint/lib/rules/no-unreachable.js
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 //------------------------------------------------------------------------------
113 description
: "disallow unreachable code after `return`, `throw`, `continue`, and `break` statements",
115 url
: "https://eslint.org/docs/rules/no-unreachable"
121 unreachableCode
: "Unreachable code."
126 let currentCodePath
= null;
128 /** @type {ConstructorInfo | null} */
129 let constructorInfo
= null;
131 /** @type {ConsecutiveRange} */
132 const range
= new ConsecutiveRange(context
.getSourceCode());
135 * Reports a given node if it's unreachable.
136 * @param {ASTNode} node A statement node to report.
139 function reportIfUnreachable(node
) {
142 if (node
&& (node
.type
=== "PropertyDefinition" || currentCodePath
.currentSegments
.every(isUnreachable
))) {
144 // Store this statement to distinguish consecutive statements.
150 // Skip if this statement is inside of the current range.
151 if (range
.contains(node
)) {
155 // Merge if this statement is consecutive to the current range.
156 if (range
.isConsecutive(node
)) {
165 * Report the current range since this statement is reachable or is
166 * not consecutive to the current range.
168 if (!range
.isEmpty
) {
170 messageId
: "unreachableCode",
172 node
: range
.startNode
176 // Update the current range.
177 range
.reset(nextNode
);
182 // Manages the current code path.
183 onCodePathStart(codePath
) {
184 currentCodePath
= codePath
;
188 currentCodePath
= currentCodePath
.upper
;
191 // Registers for all statement nodes (excludes FunctionDeclaration).
192 BlockStatement
: reportIfUnreachable
,
193 BreakStatement
: reportIfUnreachable
,
194 ClassDeclaration
: reportIfUnreachable
,
195 ContinueStatement
: reportIfUnreachable
,
196 DebuggerStatement
: reportIfUnreachable
,
197 DoWhileStatement
: reportIfUnreachable
,
198 ExpressionStatement
: reportIfUnreachable
,
199 ForInStatement
: reportIfUnreachable
,
200 ForOfStatement
: reportIfUnreachable
,
201 ForStatement
: reportIfUnreachable
,
202 IfStatement
: reportIfUnreachable
,
203 ImportDeclaration
: reportIfUnreachable
,
204 LabeledStatement
: reportIfUnreachable
,
205 ReturnStatement
: reportIfUnreachable
,
206 SwitchStatement
: reportIfUnreachable
,
207 ThrowStatement
: reportIfUnreachable
,
208 TryStatement
: reportIfUnreachable
,
210 VariableDeclaration(node
) {
211 if (node
.kind
!== "var" || node
.declarations
.some(isInitialized
)) {
212 reportIfUnreachable(node
);
216 WhileStatement
: reportIfUnreachable
,
217 WithStatement
: reportIfUnreachable
,
218 ExportNamedDeclaration
: reportIfUnreachable
,
219 ExportDefaultDeclaration
: reportIfUnreachable
,
220 ExportAllDeclaration
: reportIfUnreachable
,
223 reportIfUnreachable();
227 * Instance fields defined in a subclass are never created if the constructor of the subclass
228 * doesn't call `super()`, so their definitions are unreachable code.
230 "MethodDefinition[kind='constructor']"() {
232 upper
: constructorInfo
,
236 "MethodDefinition[kind='constructor']:exit"(node
) {
237 const { hasSuperCall
} = constructorInfo
;
239 constructorInfo
= constructorInfo
.upper
;
241 // skip typescript constructors without the body
242 if (!node
.value
.body
) {
246 const classDefinition
= node
.parent
.parent
;
248 if (classDefinition
.superClass
&& !hasSuperCall
) {
249 for (const element
of classDefinition
.body
.body
) {
250 if (element
.type
=== "PropertyDefinition" && !element
.static) {
251 reportIfUnreachable(element
);
256 "CallExpression > Super.callee"() {
257 if (constructorInfo
) {
258 constructorInfo
.hasSuperCall
= true;