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