blob: 969e40bbd855c55ce1bf29f364f858c659026f94 [file] [log] [blame]
Yang Guo4fd355c2019-09-19 10:59:03 +02001/**
2 * @fileoverview A rule to set the maximum number of statements in a function.
3 * @author Ian Christian Myers
4 */
5
6"use strict";
7
8//------------------------------------------------------------------------------
9// Requirements
10//------------------------------------------------------------------------------
11
Yang Guo4fd355c2019-09-19 10:59:03 +020012const astUtils = require("./utils/ast-utils");
Simon Zünd52e20202021-06-16 08:34:28 +020013const { upperCaseFirst } = require("../shared/string-utils");
Yang Guo4fd355c2019-09-19 10:59:03 +020014
15//------------------------------------------------------------------------------
16// Rule Definition
17//------------------------------------------------------------------------------
18
19module.exports = {
20 meta: {
21 type: "suggestion",
22
23 docs: {
24 description: "enforce a maximum number of statements allowed in function blocks",
Yang Guo4fd355c2019-09-19 10:59:03 +020025 recommended: false,
26 url: "https://eslint.org/docs/rules/max-statements"
27 },
28
29 schema: [
30 {
31 oneOf: [
32 {
33 type: "integer",
34 minimum: 0
35 },
36 {
37 type: "object",
38 properties: {
39 maximum: {
40 type: "integer",
41 minimum: 0
42 },
43 max: {
44 type: "integer",
45 minimum: 0
46 }
47 },
48 additionalProperties: false
49 }
50 ]
51 },
52 {
53 type: "object",
54 properties: {
55 ignoreTopLevelFunctions: {
56 type: "boolean"
57 }
58 },
59 additionalProperties: false
60 }
61 ],
62 messages: {
63 exceed: "{{name}} has too many statements ({{count}}). Maximum allowed is {{max}}."
64 }
65 },
66
67 create(context) {
68
69 //--------------------------------------------------------------------------
70 // Helpers
71 //--------------------------------------------------------------------------
72
73 const functionStack = [],
74 option = context.options[0],
75 ignoreTopLevelFunctions = context.options[1] && context.options[1].ignoreTopLevelFunctions || false,
76 topLevelFunctions = [];
77 let maxStatements = 10;
78
79 if (
80 typeof option === "object" &&
81 (Object.prototype.hasOwnProperty.call(option, "maximum") || Object.prototype.hasOwnProperty.call(option, "max"))
82 ) {
83 maxStatements = option.maximum || option.max;
84 } else if (typeof option === "number") {
85 maxStatements = option;
86 }
87
88 /**
89 * Reports a node if it has too many statements
90 * @param {ASTNode} node node to evaluate
91 * @param {int} count Number of statements in node
92 * @param {int} max Maximum number of statements allowed
93 * @returns {void}
94 * @private
95 */
96 function reportIfTooManyStatements(node, count, max) {
97 if (count > max) {
Simon Zünd52e20202021-06-16 08:34:28 +020098 const name = upperCaseFirst(astUtils.getFunctionNameWithKind(node));
Yang Guo4fd355c2019-09-19 10:59:03 +020099
100 context.report({
101 node,
102 messageId: "exceed",
103 data: { name, count, max }
104 });
105 }
106 }
107
108 /**
109 * When parsing a new function, store it in our function stack
110 * @returns {void}
111 * @private
112 */
113 function startFunction() {
114 functionStack.push(0);
115 }
116
117 /**
118 * Evaluate the node at the end of function
119 * @param {ASTNode} node node to evaluate
120 * @returns {void}
121 * @private
122 */
123 function endFunction(node) {
124 const count = functionStack.pop();
125
Tim van der Lippe0124c682021-11-23 15:12:10 +0000126 /*
127 * This rule does not apply to class static blocks, but we have to track them so
128 * that stataments in them do not count as statements in the enclosing function.
129 */
130 if (node.type === "StaticBlock") {
131 return;
132 }
133
Yang Guo4fd355c2019-09-19 10:59:03 +0200134 if (ignoreTopLevelFunctions && functionStack.length === 0) {
135 topLevelFunctions.push({ node, count });
136 } else {
137 reportIfTooManyStatements(node, count, maxStatements);
138 }
139 }
140
141 /**
142 * Increment the count of the functions
143 * @param {ASTNode} node node to evaluate
144 * @returns {void}
145 * @private
146 */
147 function countStatements(node) {
148 functionStack[functionStack.length - 1] += node.body.length;
149 }
150
151 //--------------------------------------------------------------------------
152 // Public API
153 //--------------------------------------------------------------------------
154
155 return {
156 FunctionDeclaration: startFunction,
157 FunctionExpression: startFunction,
158 ArrowFunctionExpression: startFunction,
Tim van der Lippe0124c682021-11-23 15:12:10 +0000159 StaticBlock: startFunction,
Yang Guo4fd355c2019-09-19 10:59:03 +0200160
161 BlockStatement: countStatements,
162
163 "FunctionDeclaration:exit": endFunction,
164 "FunctionExpression:exit": endFunction,
165 "ArrowFunctionExpression:exit": endFunction,
Tim van der Lippe0124c682021-11-23 15:12:10 +0000166 "StaticBlock:exit": endFunction,
Yang Guo4fd355c2019-09-19 10:59:03 +0200167
168 "Program:exit"() {
169 if (topLevelFunctions.length === 1) {
170 return;
171 }
172
173 topLevelFunctions.forEach(element => {
174 const count = element.count;
175 const node = element.node;
176
177 reportIfTooManyStatements(node, count, maxStatements);
178 });
179 }
180 };
181
182 }
183};