]>
git.proxmox.com Git - pve-eslint.git/blob - eslint/lib/rules/no-this-before-super.js
2 * @fileoverview A rule to disallow using `this`/`super` before `super()`.
3 * @author Toru Nagashima
8 //------------------------------------------------------------------------------
10 //------------------------------------------------------------------------------
12 const astUtils
= require("./utils/ast-utils");
14 //------------------------------------------------------------------------------
16 //------------------------------------------------------------------------------
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.
25 function isConstructorFunction(node
) {
27 node
.type
=== "FunctionExpression" &&
28 node
.parent
.type
=== "MethodDefinition" &&
29 node
.parent
.kind
=== "constructor"
33 //------------------------------------------------------------------------------
35 //------------------------------------------------------------------------------
37 /** @type {import('../shared/types').Rule} */
43 description
: "disallow `this`/`super` before calling `super()` in constructors",
45 url
: "https://eslint.org/docs/rules/no-this-before-super"
51 noBeforeSuper
: "'{{kind}}' is not allowed before 'super()'."
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
62 * - scope: The scope of the owner class.
63 * - codePath: The code path of this constructor.
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.
74 let segInfoMap
= Object
.create(null);
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.
81 function isCalled(segment
) {
82 return !segment
.reachable
|| segInfoMap
[segment
.id
].superCalled
;
86 * Checks whether or not this is in a constructor.
87 * @returns {boolean} `true` if this is in a constructor.
89 function isInConstructorOfDerivedClass() {
90 return Boolean(funcInfo
&& funcInfo
.isConstructor
&& funcInfo
.hasExtends
);
94 * Checks whether or not this is before `super()` is called.
95 * @returns {boolean} `true` if this is before `super()` is called.
97 function isBeforeCallOfSuper() {
99 isInConstructorOfDerivedClass() &&
100 !funcInfo
.codePath
.currentSegments
.every(isCalled
)
105 * Sets a given node as invalid.
106 * @param {ASTNode} node A node to set as invalid. This is one of
107 * a ThisExpression and a Super.
110 function setInvalid(node
) {
111 const segments
= funcInfo
.codePath
.currentSegments
;
113 for (let i
= 0; i
< segments
.length
; ++i
) {
114 const segment
= segments
[i
];
116 if (segment
.reachable
) {
117 segInfoMap
[segment
.id
].invalidNodes
.push(node
);
123 * Sets the current segment as `super` was called.
126 function setSuperCalled() {
127 const segments
= funcInfo
.codePath
.currentSegments
;
129 for (let i
= 0; i
< segments
.length
; ++i
) {
130 const segment
= segments
[i
];
132 if (segment
.reachable
) {
133 segInfoMap
[segment
.id
].superCalled
= true;
141 * Adds information of a constructor into the stack.
142 * @param {CodePath} codePath A code path which was started.
143 * @param {ASTNode} node The current node.
146 onCodePathStart(codePath
, node
) {
147 if (isConstructorFunction(node
)) {
149 // Class > ClassBody > MethodDefinition > FunctionExpression
150 const classNode
= node
.parent
.parent
.parent
;
156 classNode
.superClass
&&
157 !astUtils
.isNullOrUndefined(classNode
.superClass
)
164 isConstructor
: false,
172 * Removes the top of stack item.
174 * And this traverses all segments of this code path then reports every
176 * @param {CodePath} codePath A code path which was ended.
179 onCodePathEnd(codePath
) {
180 const isDerivedClass
= funcInfo
.hasExtends
;
182 funcInfo
= funcInfo
.upper
;
183 if (!isDerivedClass
) {
187 codePath
.traverseSegments((segment
, controller
) => {
188 const info
= segInfoMap
[segment
.id
];
190 for (let i
= 0; i
< info
.invalidNodes
.length
; ++i
) {
191 const invalidNode
= info
.invalidNodes
[i
];
194 messageId
: "noBeforeSuper",
197 kind
: invalidNode
.type
=== "Super" ? "super" : "this"
202 if (info
.superCalled
) {
209 * Initialize information of a given code path segment.
210 * @param {CodePathSegment} segment A code path segment to initialize.
213 onCodePathSegmentStart(segment
) {
214 if (!isInConstructorOfDerivedClass()) {
219 segInfoMap
[segment
.id
] = {
221 segment
.prevSegments
.length
> 0 &&
222 segment
.prevSegments
.every(isCalled
)
229 * Update information of the code path segment when a code path was
231 * @param {CodePathSegment} fromSegment The code path segment of the
233 * @param {CodePathSegment} toSegment A code path segment of the head
237 onCodePathSegmentLoop(fromSegment
, toSegment
) {
238 if (!isInConstructorOfDerivedClass()) {
242 // Update information inside of the loop.
243 funcInfo
.codePath
.traverseSegments(
244 { first
: toSegment
, last
: fromSegment
},
245 (segment
, controller
) => {
246 const info
= segInfoMap
[segment
.id
];
248 if (info
.superCalled
) {
249 info
.invalidNodes
= [];
252 segment
.prevSegments
.length
> 0 &&
253 segment
.prevSegments
.every(isCalled
)
255 info
.superCalled
= true;
256 info
.invalidNodes
= [];
263 * Reports if this is before `super()`.
264 * @param {ASTNode} node A target node.
267 ThisExpression(node
) {
268 if (isBeforeCallOfSuper()) {
274 * Reports if this is before `super()`.
275 * @param {ASTNode} node A target node.
279 if (!astUtils
.isCallee(node
) && isBeforeCallOfSuper()) {
285 * Marks `super()` called.
286 * @param {ASTNode} node A target node.
289 "CallExpression:exit"(node
) {
290 if (node
.callee
.type
=== "Super" && isBeforeCallOfSuper()) {
300 segInfoMap
= Object
.create(null);