UNPKG

5.56 kBJavaScriptView Raw
1/**
2 * @fileoverview Rule to flag the use of redundant constructors in classes.
3 * @author Alberto Rodríguez
4 */
5"use strict";
6
7//------------------------------------------------------------------------------
8// Helpers
9//------------------------------------------------------------------------------
10
11/**
12 * Checks whether a given array of statements is a single call of `super`.
13 *
14 * @param {ASTNode[]} body - An array of statements to check.
15 * @returns {boolean} `true` if the body is a single call of `super`.
16 */
17function isSingleSuperCall(body) {
18 return (
19 body.length === 1 &&
20 body[0].type === "ExpressionStatement" &&
21 body[0].expression.type === "CallExpression" &&
22 body[0].expression.callee.type === "Super"
23 );
24}
25
26/**
27 * Checks whether a given node is a pattern which doesn't have any side effects.
28 * Default parameters and Destructuring parameters can have side effects.
29 *
30 * @param {ASTNode} node - A pattern node.
31 * @returns {boolean} `true` if the node doesn't have any side effects.
32 */
33function isSimple(node) {
34 return node.type === "Identifier" || node.type === "RestElement";
35}
36
37/**
38 * Checks whether a given array of expressions is `...arguments` or not.
39 * `super(...arguments)` passes all arguments through.
40 *
41 * @param {ASTNode[]} superArgs - An array of expressions to check.
42 * @returns {boolean} `true` if the superArgs is `...arguments`.
43 */
44function isSpreadArguments(superArgs) {
45 return (
46 superArgs.length === 1 &&
47 superArgs[0].type === "SpreadElement" &&
48 superArgs[0].argument.type === "Identifier" &&
49 superArgs[0].argument.name === "arguments"
50 );
51}
52
53/**
54 * Checks whether given 2 nodes are identifiers which have the same name or not.
55 *
56 * @param {ASTNode} ctorParam - A node to check.
57 * @param {ASTNode} superArg - A node to check.
58 * @returns {boolean} `true` if the nodes are identifiers which have the same
59 * name.
60 */
61function isValidIdentifierPair(ctorParam, superArg) {
62 return (
63 ctorParam.type === "Identifier" &&
64 superArg.type === "Identifier" &&
65 ctorParam.name === superArg.name
66 );
67}
68
69/**
70 * Checks whether given 2 nodes are a rest/spread pair which has the same values.
71 *
72 * @param {ASTNode} ctorParam - A node to check.
73 * @param {ASTNode} superArg - A node to check.
74 * @returns {boolean} `true` if the nodes are a rest/spread pair which has the
75 * same values.
76 */
77function isValidRestSpreadPair(ctorParam, superArg) {
78 return (
79 ctorParam.type === "RestElement" &&
80 superArg.type === "SpreadElement" &&
81 isValidIdentifierPair(ctorParam.argument, superArg.argument)
82 );
83}
84
85/**
86 * Checks whether given 2 nodes have the same value or not.
87 *
88 * @param {ASTNode} ctorParam - A node to check.
89 * @param {ASTNode} superArg - A node to check.
90 * @returns {boolean} `true` if the nodes have the same value or not.
91 */
92function isValidPair(ctorParam, superArg) {
93 return (
94 isValidIdentifierPair(ctorParam, superArg) ||
95 isValidRestSpreadPair(ctorParam, superArg)
96 );
97}
98
99/**
100 * Checks whether the parameters of a constructor and the arguments of `super()`
101 * have the same values or not.
102 *
103 * @param {ASTNode} ctorParams - The parameters of a constructor to check.
104 * @param {ASTNode} superArgs - The arguments of `super()` to check.
105 * @returns {boolean} `true` if those have the same values.
106 */
107function isPassingThrough(ctorParams, superArgs) {
108 if (ctorParams.length !== superArgs.length) {
109 return false;
110 }
111
112 for (let i = 0; i < ctorParams.length; ++i) {
113 if (!isValidPair(ctorParams[i], superArgs[i])) {
114 return false;
115 }
116 }
117
118 return true;
119}
120
121/**
122 * Checks whether the constructor body is a redundant super call.
123 *
124 * @param {Array} body - constructor body content.
125 * @param {Array} ctorParams - The params to check against super call.
126 * @returns {boolean} true if the construtor body is redundant
127 */
128function isRedundantSuperCall(body, ctorParams) {
129 return (
130 isSingleSuperCall(body) &&
131 ctorParams.every(isSimple) &&
132 (
133 isSpreadArguments(body[0].expression.arguments) ||
134 isPassingThrough(ctorParams, body[0].expression.arguments)
135 )
136 );
137}
138
139//------------------------------------------------------------------------------
140// Rule Definition
141//------------------------------------------------------------------------------
142
143module.exports = {
144 meta: {
145 docs: {
146 description: "disallow unnecessary constructors",
147 category: "ECMAScript 6",
148 recommended: false,
149 url: "https://eslint.org/docs/rules/no-useless-constructor"
150 },
151
152 schema: []
153 },
154
155 create(context) {
156
157 /**
158 * Checks whether a node is a redundant constructor
159 * @param {ASTNode} node - node to check
160 * @returns {void}
161 */
162 function checkForConstructor(node) {
163 if (node.kind !== "constructor") {
164 return;
165 }
166
167 const body = node.value.body.body;
168 const ctorParams = node.value.params;
169 const superClass = node.parent.parent.superClass;
170
171 if (superClass ? isRedundantSuperCall(body, ctorParams) : (body.length === 0)) {
172 context.report({
173 node,
174 message: "Useless constructor."
175 });
176 }
177 }
178
179 return {
180 MethodDefinition: checkForConstructor
181 };
182 }
183};