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