blob: 116c8ad0a63ee9b10039080b38e3abbaba0983e4 [file] [log] [blame]
Yang Guo4fd355c2019-09-19 10:59:03 +02001/**
2 * @fileoverview Counts the cyclomatic complexity of each function of the script. See http://en.wikipedia.org/wiki/Cyclomatic_complexity.
Tim van der Lippe16aca392020-11-13 11:37:13 +00003 * Counts the number of if, conditional, for, while, try, switch/case,
Yang Guo4fd355c2019-09-19 10:59:03 +02004 * @author Patrick Brosset
5 */
6
7"use strict";
8
9//------------------------------------------------------------------------------
10// Requirements
11//------------------------------------------------------------------------------
12
Yang Guo4fd355c2019-09-19 10:59:03 +020013const astUtils = require("./utils/ast-utils");
Simon Zünd52e20202021-06-16 08:34:28 +020014const { upperCaseFirst } = require("../shared/string-utils");
Yang Guo4fd355c2019-09-19 10:59:03 +020015
16//------------------------------------------------------------------------------
17// Rule Definition
18//------------------------------------------------------------------------------
19
20module.exports = {
21 meta: {
22 type: "suggestion",
23
24 docs: {
25 description: "enforce a maximum cyclomatic complexity allowed in a program",
26 category: "Best Practices",
27 recommended: false,
28 url: "https://eslint.org/docs/rules/complexity"
29 },
30
31 schema: [
32 {
33 oneOf: [
34 {
35 type: "integer",
36 minimum: 0
37 },
38 {
39 type: "object",
40 properties: {
41 maximum: {
42 type: "integer",
43 minimum: 0
44 },
45 max: {
46 type: "integer",
47 minimum: 0
48 }
49 },
50 additionalProperties: false
51 }
52 ]
53 }
54 ],
55
56 messages: {
57 complex: "{{name}} has a complexity of {{complexity}}. Maximum allowed is {{max}}."
58 }
59 },
60
61 create(context) {
62 const option = context.options[0];
63 let THRESHOLD = 20;
64
65 if (
66 typeof option === "object" &&
67 (Object.prototype.hasOwnProperty.call(option, "maximum") || Object.prototype.hasOwnProperty.call(option, "max"))
68 ) {
69 THRESHOLD = option.maximum || option.max;
70 } else if (typeof option === "number") {
71 THRESHOLD = option;
72 }
73
74 //--------------------------------------------------------------------------
75 // Helpers
76 //--------------------------------------------------------------------------
77
78 // Using a stack to store complexity (handling nested functions)
79 const fns = [];
80
81 /**
82 * When parsing a new function, store it in our function stack
83 * @returns {void}
84 * @private
85 */
86 function startFunction() {
87 fns.push(1);
88 }
89
90 /**
91 * Evaluate the node at the end of function
92 * @param {ASTNode} node node to evaluate
93 * @returns {void}
94 * @private
95 */
96 function endFunction(node) {
Simon Zünd52e20202021-06-16 08:34:28 +020097 const name = upperCaseFirst(astUtils.getFunctionNameWithKind(node));
Yang Guo4fd355c2019-09-19 10:59:03 +020098 const complexity = fns.pop();
99
100 if (complexity > THRESHOLD) {
101 context.report({
102 node,
103 messageId: "complex",
104 data: { name, complexity, max: THRESHOLD }
105 });
106 }
107 }
108
109 /**
110 * Increase the complexity of the function in context
111 * @returns {void}
112 * @private
113 */
114 function increaseComplexity() {
115 if (fns.length) {
116 fns[fns.length - 1]++;
117 }
118 }
119
120 /**
121 * Increase the switch complexity in context
122 * @param {ASTNode} node node to evaluate
123 * @returns {void}
124 * @private
125 */
126 function increaseSwitchComplexity(node) {
127
128 // Avoiding `default`
129 if (node.test) {
130 increaseComplexity();
131 }
132 }
133
134 //--------------------------------------------------------------------------
135 // Public API
136 //--------------------------------------------------------------------------
137
138 return {
139 FunctionDeclaration: startFunction,
140 FunctionExpression: startFunction,
141 ArrowFunctionExpression: startFunction,
142 "FunctionDeclaration:exit": endFunction,
143 "FunctionExpression:exit": endFunction,
144 "ArrowFunctionExpression:exit": endFunction,
145
146 CatchClause: increaseComplexity,
147 ConditionalExpression: increaseComplexity,
148 LogicalExpression: increaseComplexity,
149 ForStatement: increaseComplexity,
150 ForInStatement: increaseComplexity,
151 ForOfStatement: increaseComplexity,
152 IfStatement: increaseComplexity,
153 SwitchCase: increaseSwitchComplexity,
154 WhileStatement: increaseComplexity,
Tim van der Lippeb97da6b2021-02-12 14:32:53 +0000155 DoWhileStatement: increaseComplexity,
156
157 AssignmentExpression(node) {
158 if (astUtils.isLogicalAssignmentOperator(node.operator)) {
159 increaseComplexity();
160 }
161 }
Yang Guo4fd355c2019-09-19 10:59:03 +0200162 };
163
164 }
165};