Actualizacion maquina principal
[dotfiles/.git] / .config / coc / extensions / node_modules / coc-prettier / node_modules / eslint / lib / rules / no-this-before-super.js
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
12 const 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  */
25 function 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 module.exports = {
38     meta: {
39         type: "problem",
40
41         docs: {
42             description: "disallow `this`/`super` before calling `super()` in constructors",
43             category: "ECMAScript 6",
44             recommended: true,
45             url: "https://eslint.org/docs/rules/no-this-before-super"
46         },
47
48         schema: []
49     },
50
51     create(context) {
52
53         /*
54          * Information for each constructor.
55          * - upper:      Information of the upper constructor.
56          * - hasExtends: A flag which shows whether the owner class has a valid
57          *   `extends` part.
58          * - scope:      The scope of the owner class.
59          * - codePath:   The code path of this constructor.
60          */
61         let funcInfo = null;
62
63         /*
64          * Information for each code path segment.
65          * Each key is the id of a code path segment.
66          * Each value is an object:
67          * - superCalled:  The flag which shows `super()` called in all code paths.
68          * - invalidNodes: The array of invalid ThisExpression and Super nodes.
69          */
70         let segInfoMap = Object.create(null);
71
72         /**
73          * Gets whether or not `super()` is called in a given code path segment.
74          * @param {CodePathSegment} segment A code path segment to get.
75          * @returns {boolean} `true` if `super()` is called.
76          */
77         function isCalled(segment) {
78             return !segment.reachable || segInfoMap[segment.id].superCalled;
79         }
80
81         /**
82          * Checks whether or not this is in a constructor.
83          * @returns {boolean} `true` if this is in a constructor.
84          */
85         function isInConstructorOfDerivedClass() {
86             return Boolean(funcInfo && funcInfo.isConstructor && funcInfo.hasExtends);
87         }
88
89         /**
90          * Checks whether or not this is before `super()` is called.
91          * @returns {boolean} `true` if this is before `super()` is called.
92          */
93         function isBeforeCallOfSuper() {
94             return (
95                 isInConstructorOfDerivedClass() &&
96                 !funcInfo.codePath.currentSegments.every(isCalled)
97             );
98         }
99
100         /**
101          * Sets a given node as invalid.
102          * @param {ASTNode} node A node to set as invalid. This is one of
103          *      a ThisExpression and a Super.
104          * @returns {void}
105          */
106         function setInvalid(node) {
107             const segments = funcInfo.codePath.currentSegments;
108
109             for (let i = 0; i < segments.length; ++i) {
110                 const segment = segments[i];
111
112                 if (segment.reachable) {
113                     segInfoMap[segment.id].invalidNodes.push(node);
114                 }
115             }
116         }
117
118         /**
119          * Sets the current segment as `super` was called.
120          * @returns {void}
121          */
122         function setSuperCalled() {
123             const segments = funcInfo.codePath.currentSegments;
124
125             for (let i = 0; i < segments.length; ++i) {
126                 const segment = segments[i];
127
128                 if (segment.reachable) {
129                     segInfoMap[segment.id].superCalled = true;
130                 }
131             }
132         }
133
134         return {
135
136             /**
137              * Adds information of a constructor into the stack.
138              * @param {CodePath} codePath A code path which was started.
139              * @param {ASTNode} node The current node.
140              * @returns {void}
141              */
142             onCodePathStart(codePath, node) {
143                 if (isConstructorFunction(node)) {
144
145                     // Class > ClassBody > MethodDefinition > FunctionExpression
146                     const classNode = node.parent.parent.parent;
147
148                     funcInfo = {
149                         upper: funcInfo,
150                         isConstructor: true,
151                         hasExtends: Boolean(
152                             classNode.superClass &&
153                             !astUtils.isNullOrUndefined(classNode.superClass)
154                         ),
155                         codePath
156                     };
157                 } else {
158                     funcInfo = {
159                         upper: funcInfo,
160                         isConstructor: false,
161                         hasExtends: false,
162                         codePath
163                     };
164                 }
165             },
166
167             /**
168              * Removes the top of stack item.
169              *
170              * And this treverses all segments of this code path then reports every
171              * invalid node.
172              * @param {CodePath} codePath A code path which was ended.
173              * @returns {void}
174              */
175             onCodePathEnd(codePath) {
176                 const isDerivedClass = funcInfo.hasExtends;
177
178                 funcInfo = funcInfo.upper;
179                 if (!isDerivedClass) {
180                     return;
181                 }
182
183                 codePath.traverseSegments((segment, controller) => {
184                     const info = segInfoMap[segment.id];
185
186                     for (let i = 0; i < info.invalidNodes.length; ++i) {
187                         const invalidNode = info.invalidNodes[i];
188
189                         context.report({
190                             message: "'{{kind}}' is not allowed before 'super()'.",
191                             node: invalidNode,
192                             data: {
193                                 kind: invalidNode.type === "Super" ? "super" : "this"
194                             }
195                         });
196                     }
197
198                     if (info.superCalled) {
199                         controller.skip();
200                     }
201                 });
202             },
203
204             /**
205              * Initialize information of a given code path segment.
206              * @param {CodePathSegment} segment A code path segment to initialize.
207              * @returns {void}
208              */
209             onCodePathSegmentStart(segment) {
210                 if (!isInConstructorOfDerivedClass()) {
211                     return;
212                 }
213
214                 // Initialize info.
215                 segInfoMap[segment.id] = {
216                     superCalled: (
217                         segment.prevSegments.length > 0 &&
218                         segment.prevSegments.every(isCalled)
219                     ),
220                     invalidNodes: []
221                 };
222             },
223
224             /**
225              * Update information of the code path segment when a code path was
226              * looped.
227              * @param {CodePathSegment} fromSegment The code path segment of the
228              *      end of a loop.
229              * @param {CodePathSegment} toSegment A code path segment of the head
230              *      of a loop.
231              * @returns {void}
232              */
233             onCodePathSegmentLoop(fromSegment, toSegment) {
234                 if (!isInConstructorOfDerivedClass()) {
235                     return;
236                 }
237
238                 // Update information inside of the loop.
239                 funcInfo.codePath.traverseSegments(
240                     { first: toSegment, last: fromSegment },
241                     (segment, controller) => {
242                         const info = segInfoMap[segment.id];
243
244                         if (info.superCalled) {
245                             info.invalidNodes = [];
246                             controller.skip();
247                         } else if (
248                             segment.prevSegments.length > 0 &&
249                             segment.prevSegments.every(isCalled)
250                         ) {
251                             info.superCalled = true;
252                             info.invalidNodes = [];
253                         }
254                     }
255                 );
256             },
257
258             /**
259              * Reports if this is before `super()`.
260              * @param {ASTNode} node A target node.
261              * @returns {void}
262              */
263             ThisExpression(node) {
264                 if (isBeforeCallOfSuper()) {
265                     setInvalid(node);
266                 }
267             },
268
269             /**
270              * Reports if this is before `super()`.
271              * @param {ASTNode} node A target node.
272              * @returns {void}
273              */
274             Super(node) {
275                 if (!astUtils.isCallee(node) && isBeforeCallOfSuper()) {
276                     setInvalid(node);
277                 }
278             },
279
280             /**
281              * Marks `super()` called.
282              * @param {ASTNode} node A target node.
283              * @returns {void}
284              */
285             "CallExpression:exit"(node) {
286                 if (node.callee.type === "Super" && isBeforeCallOfSuper()) {
287                     setSuperCalled();
288                 }
289             },
290
291             /**
292              * Resets state.
293              * @returns {void}
294              */
295             "Program:exit"() {
296                 segInfoMap = Object.create(null);
297             }
298         };
299     }
300 };