no-useless-constructor.js 5.7 KB

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