]>
git.proxmox.com Git - pve-eslint.git/blob - eslint/lib/rules/constructor-super.js
2 * @fileoverview A rule to verify `super()` callings in constructor.
3 * @author Toru Nagashima
8 //------------------------------------------------------------------------------
10 //------------------------------------------------------------------------------
13 * Checks whether a given code path segment is reachable or not.
14 * @param {CodePathSegment} segment A code path segment to check.
15 * @returns {boolean} `true` if the segment is reachable.
17 function isReachable(segment
) {
18 return segment
.reachable
;
22 * Checks whether or not a given node is a constructor.
23 * @param {ASTNode} node A node to check. This node type is one of
24 * `Program`, `FunctionDeclaration`, `FunctionExpression`, and
25 * `ArrowFunctionExpression`.
26 * @returns {boolean} `true` if the node is a constructor.
28 function isConstructorFunction(node
) {
30 node
.type
=== "FunctionExpression" &&
31 node
.parent
.type
=== "MethodDefinition" &&
32 node
.parent
.kind
=== "constructor"
37 * Checks whether a given node can be a constructor or not.
38 * @param {ASTNode} node A node to check.
39 * @returns {boolean} `true` if the node can be a constructor.
41 function isPossibleConstructor(node
) {
47 case "ClassExpression":
48 case "FunctionExpression":
49 case "ThisExpression":
50 case "MemberExpression":
51 case "CallExpression":
53 case "YieldExpression":
54 case "TaggedTemplateExpression":
59 return node
.name
!== "undefined";
61 case "AssignmentExpression":
62 return isPossibleConstructor(node
.right
);
64 case "LogicalExpression":
66 isPossibleConstructor(node
.left
) ||
67 isPossibleConstructor(node
.right
)
70 case "ConditionalExpression":
72 isPossibleConstructor(node
.alternate
) ||
73 isPossibleConstructor(node
.consequent
)
76 case "SequenceExpression": {
77 const lastExpression
= node
.expressions
[node
.expressions
.length
- 1];
79 return isPossibleConstructor(lastExpression
);
87 //------------------------------------------------------------------------------
89 //------------------------------------------------------------------------------
96 description
: "require `super()` calls in constructors",
97 category
: "ECMAScript 6",
99 url
: "https://eslint.org/docs/rules/constructor-super"
105 missingSome
: "Lacked a call of 'super()' in some code paths.",
106 missingAll
: "Expected to call 'super()'.",
108 duplicate
: "Unexpected duplicate 'super()'.",
109 badSuper
: "Unexpected 'super()' because 'super' is not a constructor.",
110 unexpected
: "Unexpected 'super()'."
117 * {{hasExtends: boolean, scope: Scope, codePath: CodePath}[]}
118 * Information for each constructor.
119 * - upper: Information of the upper constructor.
120 * - hasExtends: A flag which shows whether own class has a valid `extends`
122 * - scope: The scope of own class.
123 * - codePath: The code path object of the constructor.
128 * {Map<string, {calledInSomePaths: boolean, calledInEveryPaths: boolean}>}
129 * Information for each code path segment.
130 * - calledInSomePaths: A flag of be called `super()` in some code paths.
131 * - calledInEveryPaths: A flag of be called `super()` in all code paths.
134 let segInfoMap
= Object
.create(null);
137 * Gets the flag which shows `super()` is called in some paths.
138 * @param {CodePathSegment} segment A code path segment to get.
139 * @returns {boolean} The flag which shows `super()` is called in some paths
141 function isCalledInSomePath(segment
) {
142 return segment
.reachable
&& segInfoMap
[segment
.id
].calledInSomePaths
;
146 * Gets the flag which shows `super()` is called in all paths.
147 * @param {CodePathSegment} segment A code path segment to get.
148 * @returns {boolean} The flag which shows `super()` is called in all paths.
150 function isCalledInEveryPath(segment
) {
153 * If specific segment is the looped segment of the current segment,
155 * If not skipped, this never becomes true after a loop.
157 if (segment
.nextSegments
.length
=== 1 &&
158 segment
.nextSegments
[0].isLoopedPrevSegment(segment
)
162 return segment
.reachable
&& segInfoMap
[segment
.id
].calledInEveryPaths
;
168 * Stacks a constructor information.
169 * @param {CodePath} codePath A code path which was started.
170 * @param {ASTNode} node The current node.
173 onCodePathStart(codePath
, node
) {
174 if (isConstructorFunction(node
)) {
176 // Class > ClassBody > MethodDefinition > FunctionExpression
177 const classNode
= node
.parent
.parent
.parent
;
178 const superClass
= classNode
.superClass
;
183 hasExtends
: Boolean(superClass
),
184 superIsConstructor
: isPossibleConstructor(superClass
),
190 isConstructor
: false,
192 superIsConstructor
: false,
199 * Pops a constructor information.
200 * And reports if `super()` lacked.
201 * @param {CodePath} codePath A code path which was ended.
202 * @param {ASTNode} node The current node.
205 onCodePathEnd(codePath
, node
) {
206 const hasExtends
= funcInfo
.hasExtends
;
209 funcInfo
= funcInfo
.upper
;
215 // Reports if `super()` lacked.
216 const segments
= codePath
.returnedSegments
;
217 const calledInEveryPaths
= segments
.every(isCalledInEveryPath
);
218 const calledInSomePaths
= segments
.some(isCalledInSomePath
);
220 if (!calledInEveryPaths
) {
222 messageId
: calledInSomePaths
231 * Initialize information of a given code path segment.
232 * @param {CodePathSegment} segment A code path segment to initialize.
235 onCodePathSegmentStart(segment
) {
236 if (!(funcInfo
&& funcInfo
.isConstructor
&& funcInfo
.hasExtends
)) {
241 const info
= segInfoMap
[segment
.id
] = {
242 calledInSomePaths
: false,
243 calledInEveryPaths
: false,
247 // When there are previous segments, aggregates these.
248 const prevSegments
= segment
.prevSegments
;
250 if (prevSegments
.length
> 0) {
251 info
.calledInSomePaths
= prevSegments
.some(isCalledInSomePath
);
252 info
.calledInEveryPaths
= prevSegments
.every(isCalledInEveryPath
);
257 * Update information of the code path segment when a code path was
259 * @param {CodePathSegment} fromSegment The code path segment of the
261 * @param {CodePathSegment} toSegment A code path segment of the head
265 onCodePathSegmentLoop(fromSegment
, toSegment
) {
266 if (!(funcInfo
&& funcInfo
.isConstructor
&& funcInfo
.hasExtends
)) {
270 // Update information inside of the loop.
271 const isRealLoop
= toSegment
.prevSegments
.length
>= 2;
273 funcInfo
.codePath
.traverseSegments(
274 { first
: toSegment
, last
: fromSegment
},
276 const info
= segInfoMap
[segment
.id
];
277 const prevSegments
= segment
.prevSegments
;
280 info
.calledInSomePaths
= prevSegments
.some(isCalledInSomePath
);
281 info
.calledInEveryPaths
= prevSegments
.every(isCalledInEveryPath
);
283 // If flags become true anew, reports the valid nodes.
284 if (info
.calledInSomePaths
|| isRealLoop
) {
285 const nodes
= info
.validNodes
;
287 info
.validNodes
= [];
289 for (let i
= 0; i
< nodes
.length
; ++i
) {
290 const node
= nodes
[i
];
293 messageId
: "duplicate",
303 * Checks for a call of `super()`.
304 * @param {ASTNode} node A CallExpression node to check.
307 "CallExpression:exit"(node
) {
308 if (!(funcInfo
&& funcInfo
.isConstructor
)) {
312 // Skips except `super()`.
313 if (node
.callee
.type
!== "Super") {
317 // Reports if needed.
318 if (funcInfo
.hasExtends
) {
319 const segments
= funcInfo
.codePath
.currentSegments
;
320 let duplicate
= false;
323 for (let i
= 0; i
< segments
.length
; ++i
) {
324 const segment
= segments
[i
];
326 if (segment
.reachable
) {
327 info
= segInfoMap
[segment
.id
];
329 duplicate
= duplicate
|| info
.calledInSomePaths
;
330 info
.calledInSomePaths
= info
.calledInEveryPaths
= true;
337 messageId
: "duplicate",
340 } else if (!funcInfo
.superIsConstructor
) {
342 messageId
: "badSuper",
346 info
.validNodes
.push(node
);
349 } else if (funcInfo
.codePath
.currentSegments
.some(isReachable
)) {
351 messageId
: "unexpected",
358 * Set the mark to the returned path as `super()` was called.
359 * @param {ASTNode} node A ReturnStatement node to check.
362 ReturnStatement(node
) {
363 if (!(funcInfo
&& funcInfo
.isConstructor
&& funcInfo
.hasExtends
)) {
367 // Skips if no argument.
368 if (!node
.argument
) {
372 // Returning argument is a substitute of 'super()'.
373 const segments
= funcInfo
.codePath
.currentSegments
;
375 for (let i
= 0; i
< segments
.length
; ++i
) {
376 const segment
= segments
[i
];
378 if (segment
.reachable
) {
379 const info
= segInfoMap
[segment
.id
];
381 info
.calledInSomePaths
= info
.calledInEveryPaths
= true;
391 segInfoMap
= Object
.create(null);