no-empty-function.js 4.6 KB

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