no-empty-function.js 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. /**
  2. * @fileoverview Rule to disallow empty functions.
  3. * @author Toru Nagashima
  4. */
  5. "use strict";
  6. //------------------------------------------------------------------------------
  7. // Requirements
  8. //------------------------------------------------------------------------------
  9. const astUtils = require("./utils/ast-utils");
  10. //------------------------------------------------------------------------------
  11. // Helpers
  12. //------------------------------------------------------------------------------
  13. const ALLOW_OPTIONS = Object.freeze([
  14. "functions",
  15. "arrowFunctions",
  16. "generatorFunctions",
  17. "methods",
  18. "generatorMethods",
  19. "getters",
  20. "setters",
  21. "constructors"
  22. ]);
  23. /**
  24. * Gets the kind of a given function node.
  25. * @param {ASTNode} node A function node to get. This is one of
  26. * an ArrowFunctionExpression, a FunctionDeclaration, or a
  27. * FunctionExpression.
  28. * @returns {string} The kind of the function. This is one of "functions",
  29. * "arrowFunctions", "generatorFunctions", "asyncFunctions", "methods",
  30. * "generatorMethods", "asyncMethods", "getters", "setters", and
  31. * "constructors".
  32. */
  33. function getKind(node) {
  34. const parent = node.parent;
  35. let kind = "";
  36. if (node.type === "ArrowFunctionExpression") {
  37. return "arrowFunctions";
  38. }
  39. // Detects main kind.
  40. if (parent.type === "Property") {
  41. if (parent.kind === "get") {
  42. return "getters";
  43. }
  44. if (parent.kind === "set") {
  45. return "setters";
  46. }
  47. kind = parent.method ? "methods" : "functions";
  48. } else if (parent.type === "MethodDefinition") {
  49. if (parent.kind === "get") {
  50. return "getters";
  51. }
  52. if (parent.kind === "set") {
  53. return "setters";
  54. }
  55. if (parent.kind === "constructor") {
  56. return "constructors";
  57. }
  58. kind = "methods";
  59. } else {
  60. kind = "functions";
  61. }
  62. // Detects prefix.
  63. let prefix = "";
  64. if (node.generator) {
  65. prefix = "generator";
  66. } else if (node.async) {
  67. prefix = "async";
  68. } else {
  69. return kind;
  70. }
  71. return prefix + kind[0].toUpperCase() + kind.slice(1);
  72. }
  73. //------------------------------------------------------------------------------
  74. // Rule Definition
  75. //------------------------------------------------------------------------------
  76. module.exports = {
  77. meta: {
  78. type: "suggestion",
  79. docs: {
  80. description: "disallow empty functions",
  81. category: "Best Practices",
  82. recommended: false,
  83. url: "https://eslint.org/docs/rules/no-empty-function"
  84. },
  85. schema: [
  86. {
  87. type: "object",
  88. properties: {
  89. allow: {
  90. type: "array",
  91. items: { enum: ALLOW_OPTIONS },
  92. uniqueItems: true
  93. }
  94. },
  95. additionalProperties: false
  96. }
  97. ],
  98. messages: {
  99. unexpected: "Unexpected empty {{name}}."
  100. }
  101. },
  102. create(context) {
  103. const options = context.options[0] || {};
  104. const allowed = options.allow || [];
  105. const sourceCode = context.getSourceCode();
  106. /**
  107. * Reports a given function node if the node matches the following patterns.
  108. *
  109. * - Not allowed by options.
  110. * - The body is empty.
  111. * - The body doesn't have any comments.
  112. * @param {ASTNode} node A function node to report. This is one of
  113. * an ArrowFunctionExpression, a FunctionDeclaration, or a
  114. * FunctionExpression.
  115. * @returns {void}
  116. */
  117. function reportIfEmpty(node) {
  118. const kind = getKind(node);
  119. const name = astUtils.getFunctionNameWithKind(node);
  120. const innerComments = sourceCode.getTokens(node.body, {
  121. includeComments: true,
  122. filter: astUtils.isCommentToken
  123. });
  124. if (allowed.indexOf(kind) === -1 &&
  125. node.body.type === "BlockStatement" &&
  126. node.body.body.length === 0 &&
  127. innerComments.length === 0
  128. ) {
  129. context.report({
  130. node,
  131. loc: node.body.loc.start,
  132. messageId: "unexpected",
  133. data: { name }
  134. });
  135. }
  136. }
  137. return {
  138. ArrowFunctionExpression: reportIfEmpty,
  139. FunctionDeclaration: reportIfEmpty,
  140. FunctionExpression: reportIfEmpty
  141. };
  142. }
  143. };