source: imaps-frontend/node_modules/eslint/lib/rules/no-unreachable.js

main
Last change on this file was d565449, checked in by stefan toskovski <stefantoska84@…>, 4 weeks ago

Update repo after prototype presentation

  • Property mode set to 100644
File size: 8.9 KB
Line 
1/**
2 * @fileoverview Checks for unreachable code due to return, throws, break, and continue.
3 * @author Joel Feenstra
4 */
5"use strict";
6
7//------------------------------------------------------------------------------
8// Helpers
9//------------------------------------------------------------------------------
10
11/**
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.
15 */
16
17/**
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.
21 */
22function isInitialized(node) {
23 return Boolean(node.init);
24}
25
26/**
27 * Checks all segments in a set and returns true if all are unreachable.
28 * @param {Set<CodePathSegment>} segments The segments to check.
29 * @returns {boolean} True if all segments are unreachable; false otherwise.
30 */
31function areAllSegmentsUnreachable(segments) {
32
33 for (const segment of segments) {
34 if (segment.reachable) {
35 return false;
36 }
37 }
38
39 return true;
40}
41
42/**
43 * The class to distinguish consecutive unreachable statements.
44 */
45class ConsecutiveRange {
46 constructor(sourceCode) {
47 this.sourceCode = sourceCode;
48 this.startNode = null;
49 this.endNode = null;
50 }
51
52 /**
53 * The location object of this range.
54 * @type {Object}
55 */
56 get location() {
57 return {
58 start: this.startNode.loc.start,
59 end: this.endNode.loc.end
60 };
61 }
62
63 /**
64 * `true` if this range is empty.
65 * @type {boolean}
66 */
67 get isEmpty() {
68 return !(this.startNode && this.endNode);
69 }
70
71 /**
72 * Checks whether the given node is inside of this range.
73 * @param {ASTNode|Token} node The node to check.
74 * @returns {boolean} `true` if the node is inside of this range.
75 */
76 contains(node) {
77 return (
78 node.range[0] >= this.startNode.range[0] &&
79 node.range[1] <= this.endNode.range[1]
80 );
81 }
82
83 /**
84 * Checks whether the given node is consecutive to this range.
85 * @param {ASTNode} node The node to check.
86 * @returns {boolean} `true` if the node is consecutive to this range.
87 */
88 isConsecutive(node) {
89 return this.contains(this.sourceCode.getTokenBefore(node));
90 }
91
92 /**
93 * Merges the given node to this range.
94 * @param {ASTNode} node The node to merge.
95 * @returns {void}
96 */
97 merge(node) {
98 this.endNode = node;
99 }
100
101 /**
102 * Resets this range by the given node or null.
103 * @param {ASTNode|null} node The node to reset, or null.
104 * @returns {void}
105 */
106 reset(node) {
107 this.startNode = this.endNode = node;
108 }
109}
110
111//------------------------------------------------------------------------------
112// Rule Definition
113//------------------------------------------------------------------------------
114
115/** @type {import('../shared/types').Rule} */
116module.exports = {
117 meta: {
118 type: "problem",
119
120 docs: {
121 description: "Disallow unreachable code after `return`, `throw`, `continue`, and `break` statements",
122 recommended: true,
123 url: "https://eslint.org/docs/latest/rules/no-unreachable"
124 },
125
126 schema: [],
127
128 messages: {
129 unreachableCode: "Unreachable code."
130 }
131 },
132
133 create(context) {
134
135 /** @type {ConstructorInfo | null} */
136 let constructorInfo = null;
137
138 /** @type {ConsecutiveRange} */
139 const range = new ConsecutiveRange(context.sourceCode);
140
141 /** @type {Array<Set<CodePathSegment>>} */
142 const codePathSegments = [];
143
144 /** @type {Set<CodePathSegment>} */
145 let currentCodePathSegments = new Set();
146
147 /**
148 * Reports a given node if it's unreachable.
149 * @param {ASTNode} node A statement node to report.
150 * @returns {void}
151 */
152 function reportIfUnreachable(node) {
153 let nextNode = null;
154
155 if (node && (node.type === "PropertyDefinition" || areAllSegmentsUnreachable(currentCodePathSegments))) {
156
157 // Store this statement to distinguish consecutive statements.
158 if (range.isEmpty) {
159 range.reset(node);
160 return;
161 }
162
163 // Skip if this statement is inside of the current range.
164 if (range.contains(node)) {
165 return;
166 }
167
168 // Merge if this statement is consecutive to the current range.
169 if (range.isConsecutive(node)) {
170 range.merge(node);
171 return;
172 }
173
174 nextNode = node;
175 }
176
177 /*
178 * Report the current range since this statement is reachable or is
179 * not consecutive to the current range.
180 */
181 if (!range.isEmpty) {
182 context.report({
183 messageId: "unreachableCode",
184 loc: range.location,
185 node: range.startNode
186 });
187 }
188
189 // Update the current range.
190 range.reset(nextNode);
191 }
192
193 return {
194
195 // Manages the current code path.
196 onCodePathStart() {
197 codePathSegments.push(currentCodePathSegments);
198 currentCodePathSegments = new Set();
199 },
200
201 onCodePathEnd() {
202 currentCodePathSegments = codePathSegments.pop();
203 },
204
205 onUnreachableCodePathSegmentStart(segment) {
206 currentCodePathSegments.add(segment);
207 },
208
209 onUnreachableCodePathSegmentEnd(segment) {
210 currentCodePathSegments.delete(segment);
211 },
212
213 onCodePathSegmentEnd(segment) {
214 currentCodePathSegments.delete(segment);
215 },
216
217 onCodePathSegmentStart(segment) {
218 currentCodePathSegments.add(segment);
219 },
220
221 // Registers for all statement nodes (excludes FunctionDeclaration).
222 BlockStatement: reportIfUnreachable,
223 BreakStatement: reportIfUnreachable,
224 ClassDeclaration: reportIfUnreachable,
225 ContinueStatement: reportIfUnreachable,
226 DebuggerStatement: reportIfUnreachable,
227 DoWhileStatement: reportIfUnreachable,
228 ExpressionStatement: reportIfUnreachable,
229 ForInStatement: reportIfUnreachable,
230 ForOfStatement: reportIfUnreachable,
231 ForStatement: reportIfUnreachable,
232 IfStatement: reportIfUnreachable,
233 ImportDeclaration: reportIfUnreachable,
234 LabeledStatement: reportIfUnreachable,
235 ReturnStatement: reportIfUnreachable,
236 SwitchStatement: reportIfUnreachable,
237 ThrowStatement: reportIfUnreachable,
238 TryStatement: reportIfUnreachable,
239
240 VariableDeclaration(node) {
241 if (node.kind !== "var" || node.declarations.some(isInitialized)) {
242 reportIfUnreachable(node);
243 }
244 },
245
246 WhileStatement: reportIfUnreachable,
247 WithStatement: reportIfUnreachable,
248 ExportNamedDeclaration: reportIfUnreachable,
249 ExportDefaultDeclaration: reportIfUnreachable,
250 ExportAllDeclaration: reportIfUnreachable,
251
252 "Program:exit"() {
253 reportIfUnreachable();
254 },
255
256 /*
257 * Instance fields defined in a subclass are never created if the constructor of the subclass
258 * doesn't call `super()`, so their definitions are unreachable code.
259 */
260 "MethodDefinition[kind='constructor']"() {
261 constructorInfo = {
262 upper: constructorInfo,
263 hasSuperCall: false
264 };
265 },
266 "MethodDefinition[kind='constructor']:exit"(node) {
267 const { hasSuperCall } = constructorInfo;
268
269 constructorInfo = constructorInfo.upper;
270
271 // skip typescript constructors without the body
272 if (!node.value.body) {
273 return;
274 }
275
276 const classDefinition = node.parent.parent;
277
278 if (classDefinition.superClass && !hasSuperCall) {
279 for (const element of classDefinition.body.body) {
280 if (element.type === "PropertyDefinition" && !element.static) {
281 reportIfUnreachable(element);
282 }
283 }
284 }
285 },
286 "CallExpression > Super.callee"() {
287 if (constructorInfo) {
288 constructorInfo.hasSuperCall = true;
289 }
290 }
291 };
292 }
293};
Note: See TracBrowser for help on using the repository browser.