source: imaps-frontend/node_modules/eslint/lib/rules/no-this-before-super.js@ d565449

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

Update repo after prototype presentation

  • Property mode set to 100644
File size: 10.9 KB
Line 
1/**
2 * @fileoverview A rule to disallow using `this`/`super` before `super()`.
3 * @author Toru Nagashima
4 */
5
6"use strict";
7
8//------------------------------------------------------------------------------
9// Requirements
10//------------------------------------------------------------------------------
11
12const astUtils = require("./utils/ast-utils");
13
14//------------------------------------------------------------------------------
15// Helpers
16//------------------------------------------------------------------------------
17
18/**
19 * Checks whether or not a given node is a constructor.
20 * @param {ASTNode} node A node to check. This node type is one of
21 * `Program`, `FunctionDeclaration`, `FunctionExpression`, and
22 * `ArrowFunctionExpression`.
23 * @returns {boolean} `true` if the node is a constructor.
24 */
25function isConstructorFunction(node) {
26 return (
27 node.type === "FunctionExpression" &&
28 node.parent.type === "MethodDefinition" &&
29 node.parent.kind === "constructor"
30 );
31}
32
33//------------------------------------------------------------------------------
34// Rule Definition
35//------------------------------------------------------------------------------
36
37/** @type {import('../shared/types').Rule} */
38module.exports = {
39 meta: {
40 type: "problem",
41
42 docs: {
43 description: "Disallow `this`/`super` before calling `super()` in constructors",
44 recommended: true,
45 url: "https://eslint.org/docs/latest/rules/no-this-before-super"
46 },
47
48 schema: [],
49
50 messages: {
51 noBeforeSuper: "'{{kind}}' is not allowed before 'super()'."
52 }
53 },
54
55 create(context) {
56
57 /*
58 * Information for each constructor.
59 * - upper: Information of the upper constructor.
60 * - hasExtends: A flag which shows whether the owner class has a valid
61 * `extends` part.
62 * - scope: The scope of the owner class.
63 * - codePath: The code path of this constructor.
64 */
65 let funcInfo = null;
66
67 /*
68 * Information for each code path segment.
69 * Each key is the id of a code path segment.
70 * Each value is an object:
71 * - superCalled: The flag which shows `super()` called in all code paths.
72 * - invalidNodes: The array of invalid ThisExpression and Super nodes.
73 */
74 let segInfoMap = Object.create(null);
75
76 /**
77 * Gets whether or not `super()` is called in a given code path segment.
78 * @param {CodePathSegment} segment A code path segment to get.
79 * @returns {boolean} `true` if `super()` is called.
80 */
81 function isCalled(segment) {
82 return !segment.reachable || segInfoMap[segment.id].superCalled;
83 }
84
85 /**
86 * Checks whether or not this is in a constructor.
87 * @returns {boolean} `true` if this is in a constructor.
88 */
89 function isInConstructorOfDerivedClass() {
90 return Boolean(funcInfo && funcInfo.isConstructor && funcInfo.hasExtends);
91 }
92
93 /**
94 * Determines if every segment in a set has been called.
95 * @param {Set<CodePathSegment>} segments The segments to search.
96 * @returns {boolean} True if every segment has been called; false otherwise.
97 */
98 function isEverySegmentCalled(segments) {
99 for (const segment of segments) {
100 if (!isCalled(segment)) {
101 return false;
102 }
103 }
104
105 return true;
106 }
107
108 /**
109 * Checks whether or not this is before `super()` is called.
110 * @returns {boolean} `true` if this is before `super()` is called.
111 */
112 function isBeforeCallOfSuper() {
113 return (
114 isInConstructorOfDerivedClass() &&
115 !isEverySegmentCalled(funcInfo.currentSegments)
116 );
117 }
118
119 /**
120 * Sets a given node as invalid.
121 * @param {ASTNode} node A node to set as invalid. This is one of
122 * a ThisExpression and a Super.
123 * @returns {void}
124 */
125 function setInvalid(node) {
126 const segments = funcInfo.currentSegments;
127
128 for (const segment of segments) {
129 if (segment.reachable) {
130 segInfoMap[segment.id].invalidNodes.push(node);
131 }
132 }
133 }
134
135 /**
136 * Sets the current segment as `super` was called.
137 * @returns {void}
138 */
139 function setSuperCalled() {
140 const segments = funcInfo.currentSegments;
141
142 for (const segment of segments) {
143 if (segment.reachable) {
144 segInfoMap[segment.id].superCalled = true;
145 }
146 }
147 }
148
149 return {
150
151 /**
152 * Adds information of a constructor into the stack.
153 * @param {CodePath} codePath A code path which was started.
154 * @param {ASTNode} node The current node.
155 * @returns {void}
156 */
157 onCodePathStart(codePath, node) {
158 if (isConstructorFunction(node)) {
159
160 // Class > ClassBody > MethodDefinition > FunctionExpression
161 const classNode = node.parent.parent.parent;
162
163 funcInfo = {
164 upper: funcInfo,
165 isConstructor: true,
166 hasExtends: Boolean(
167 classNode.superClass &&
168 !astUtils.isNullOrUndefined(classNode.superClass)
169 ),
170 codePath,
171 currentSegments: new Set()
172 };
173 } else {
174 funcInfo = {
175 upper: funcInfo,
176 isConstructor: false,
177 hasExtends: false,
178 codePath,
179 currentSegments: new Set()
180 };
181 }
182 },
183
184 /**
185 * Removes the top of stack item.
186 *
187 * And this traverses all segments of this code path then reports every
188 * invalid node.
189 * @param {CodePath} codePath A code path which was ended.
190 * @returns {void}
191 */
192 onCodePathEnd(codePath) {
193 const isDerivedClass = funcInfo.hasExtends;
194
195 funcInfo = funcInfo.upper;
196 if (!isDerivedClass) {
197 return;
198 }
199
200 codePath.traverseSegments((segment, controller) => {
201 const info = segInfoMap[segment.id];
202
203 for (let i = 0; i < info.invalidNodes.length; ++i) {
204 const invalidNode = info.invalidNodes[i];
205
206 context.report({
207 messageId: "noBeforeSuper",
208 node: invalidNode,
209 data: {
210 kind: invalidNode.type === "Super" ? "super" : "this"
211 }
212 });
213 }
214
215 if (info.superCalled) {
216 controller.skip();
217 }
218 });
219 },
220
221 /**
222 * Initialize information of a given code path segment.
223 * @param {CodePathSegment} segment A code path segment to initialize.
224 * @returns {void}
225 */
226 onCodePathSegmentStart(segment) {
227 funcInfo.currentSegments.add(segment);
228
229 if (!isInConstructorOfDerivedClass()) {
230 return;
231 }
232
233 // Initialize info.
234 segInfoMap[segment.id] = {
235 superCalled: (
236 segment.prevSegments.length > 0 &&
237 segment.prevSegments.every(isCalled)
238 ),
239 invalidNodes: []
240 };
241 },
242
243 onUnreachableCodePathSegmentStart(segment) {
244 funcInfo.currentSegments.add(segment);
245 },
246
247 onUnreachableCodePathSegmentEnd(segment) {
248 funcInfo.currentSegments.delete(segment);
249 },
250
251 onCodePathSegmentEnd(segment) {
252 funcInfo.currentSegments.delete(segment);
253 },
254
255 /**
256 * Update information of the code path segment when a code path was
257 * looped.
258 * @param {CodePathSegment} fromSegment The code path segment of the
259 * end of a loop.
260 * @param {CodePathSegment} toSegment A code path segment of the head
261 * of a loop.
262 * @returns {void}
263 */
264 onCodePathSegmentLoop(fromSegment, toSegment) {
265 if (!isInConstructorOfDerivedClass()) {
266 return;
267 }
268
269 // Update information inside of the loop.
270 funcInfo.codePath.traverseSegments(
271 { first: toSegment, last: fromSegment },
272 (segment, controller) => {
273 const info = segInfoMap[segment.id];
274
275 if (info.superCalled) {
276 info.invalidNodes = [];
277 controller.skip();
278 } else if (
279 segment.prevSegments.length > 0 &&
280 segment.prevSegments.every(isCalled)
281 ) {
282 info.superCalled = true;
283 info.invalidNodes = [];
284 }
285 }
286 );
287 },
288
289 /**
290 * Reports if this is before `super()`.
291 * @param {ASTNode} node A target node.
292 * @returns {void}
293 */
294 ThisExpression(node) {
295 if (isBeforeCallOfSuper()) {
296 setInvalid(node);
297 }
298 },
299
300 /**
301 * Reports if this is before `super()`.
302 * @param {ASTNode} node A target node.
303 * @returns {void}
304 */
305 Super(node) {
306 if (!astUtils.isCallee(node) && isBeforeCallOfSuper()) {
307 setInvalid(node);
308 }
309 },
310
311 /**
312 * Marks `super()` called.
313 * @param {ASTNode} node A target node.
314 * @returns {void}
315 */
316 "CallExpression:exit"(node) {
317 if (node.callee.type === "Super" && isBeforeCallOfSuper()) {
318 setSuperCalled();
319 }
320 },
321
322 /**
323 * Resets state.
324 * @returns {void}
325 */
326 "Program:exit"() {
327 segInfoMap = Object.create(null);
328 }
329 };
330 }
331};
Note: See TracBrowser for help on using the repository browser.