1 | //////////////////////////////////////////////////////////////////////////////// | |
2 | // checkstyle: Checks Java source code for adherence to a set of rules. | |
3 | // Copyright (C) 2001-2017 the original author or authors. | |
4 | // | |
5 | // This library is free software; you can redistribute it and/or | |
6 | // modify it under the terms of the GNU Lesser General Public | |
7 | // License as published by the Free Software Foundation; either | |
8 | // version 2.1 of the License, or (at your option) any later version. | |
9 | // | |
10 | // This library is distributed in the hope that it will be useful, | |
11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
13 | // Lesser General Public License for more details. | |
14 | // | |
15 | // You should have received a copy of the GNU Lesser General Public | |
16 | // License along with this library; if not, write to the Free Software | |
17 | // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
18 | //////////////////////////////////////////////////////////////////////////////// | |
19 | ||
20 | package com.puppycrawl.tools.checkstyle.checks.metrics; | |
21 | ||
22 | import java.math.BigInteger; | |
23 | import java.util.ArrayDeque; | |
24 | import java.util.Deque; | |
25 | ||
26 | import com.puppycrawl.tools.checkstyle.api.AbstractCheck; | |
27 | import com.puppycrawl.tools.checkstyle.api.DetailAST; | |
28 | import com.puppycrawl.tools.checkstyle.api.TokenTypes; | |
29 | ||
30 | /** | |
31 | * Checks cyclomatic complexity against a specified limit. The complexity is | |
32 | * measured by the number of "if", "while", "do", "for", "?:", "catch", | |
33 | * "switch", "case", "&&" and "||" statements (plus one) in the body of | |
34 | * the member. It is a measure of the minimum number of possible paths through | |
35 | * the source and therefore the number of required tests. Generally 1-4 is | |
36 | * considered good, 5-7 ok, 8-10 consider re-factoring, and 11+ re-factor now! | |
37 | * | |
38 | * <p>Check has following properties: | |
39 | * | |
40 | * <p><b>switchBlockAsSingleDecisionPoint</b> - controls whether to treat the whole switch | |
41 | * block as a single decision point. Default value is <b>false</b> | |
42 | * | |
43 | * | |
44 | * @author <a href="mailto:simon@redhillconsulting.com.au">Simon Harris</a> | |
45 | * @author Oliver Burn | |
46 | * @author <a href="mailto:andreyselkin@gmail.com">Andrei Selkin</a> | |
47 | */ | |
48 | public class CyclomaticComplexityCheck | |
49 | extends AbstractCheck { | |
50 | ||
51 | /** | |
52 | * A key is pointing to the warning message text in "messages.properties" | |
53 | * file. | |
54 | */ | |
55 | public static final String MSG_KEY = "cyclomaticComplexity"; | |
56 | ||
57 | /** The initial current value. */ | |
58 | private static final BigInteger INITIAL_VALUE = BigInteger.ONE; | |
59 | ||
60 | /** Default allowed complexity. */ | |
61 | private static final int DEFAULT_COMPLEXITY_VALUE = 10; | |
62 | ||
63 | /** Stack of values - all but the current value. */ | |
64 | private final Deque<BigInteger> valueStack = new ArrayDeque<>(); | |
65 | ||
66 | /** Whether to treat the whole switch block as a single decision point.*/ | |
67 | private boolean switchBlockAsSingleDecisionPoint; | |
68 | ||
69 | /** The current value. */ | |
70 | private BigInteger currentValue = INITIAL_VALUE; | |
71 | ||
72 | /** Threshold to report error for. */ | |
73 | private int max = DEFAULT_COMPLEXITY_VALUE; | |
74 | ||
75 | /** | |
76 | * Sets whether to treat the whole switch block as a single decision point. | |
77 | * @param switchBlockAsSingleDecisionPoint whether to treat the whole switch | |
78 | * block as a single decision point. | |
79 | */ | |
80 | public void setSwitchBlockAsSingleDecisionPoint(boolean switchBlockAsSingleDecisionPoint) { | |
81 | this.switchBlockAsSingleDecisionPoint = switchBlockAsSingleDecisionPoint; | |
82 | } | |
83 | ||
84 | /** | |
85 | * Set the maximum threshold allowed. | |
86 | * | |
87 | * @param max the maximum threshold | |
88 | */ | |
89 | public final void setMax(int max) { | |
90 | this.max = max; | |
91 | } | |
92 | ||
93 | @Override | |
94 | public int[] getDefaultTokens() { | |
95 |
1
1. getDefaultTokens : mutated return of Object value for com/puppycrawl/tools/checkstyle/checks/metrics/CyclomaticComplexityCheck::getDefaultTokens to ( if (x != null) null else throw new RuntimeException ) → KILLED |
return new int[] { |
96 | TokenTypes.CTOR_DEF, | |
97 | TokenTypes.METHOD_DEF, | |
98 | TokenTypes.INSTANCE_INIT, | |
99 | TokenTypes.STATIC_INIT, | |
100 | TokenTypes.LITERAL_WHILE, | |
101 | TokenTypes.LITERAL_DO, | |
102 | TokenTypes.LITERAL_FOR, | |
103 | TokenTypes.LITERAL_IF, | |
104 | TokenTypes.LITERAL_SWITCH, | |
105 | TokenTypes.LITERAL_CASE, | |
106 | TokenTypes.LITERAL_CATCH, | |
107 | TokenTypes.QUESTION, | |
108 | TokenTypes.LAND, | |
109 | TokenTypes.LOR, | |
110 | }; | |
111 | } | |
112 | ||
113 | @Override | |
114 | public int[] getAcceptableTokens() { | |
115 |
1
1. getAcceptableTokens : mutated return of Object value for com/puppycrawl/tools/checkstyle/checks/metrics/CyclomaticComplexityCheck::getAcceptableTokens to ( if (x != null) null else throw new RuntimeException ) → KILLED |
return new int[] { |
116 | TokenTypes.CTOR_DEF, | |
117 | TokenTypes.METHOD_DEF, | |
118 | TokenTypes.INSTANCE_INIT, | |
119 | TokenTypes.STATIC_INIT, | |
120 | TokenTypes.LITERAL_WHILE, | |
121 | TokenTypes.LITERAL_DO, | |
122 | TokenTypes.LITERAL_FOR, | |
123 | TokenTypes.LITERAL_IF, | |
124 | TokenTypes.LITERAL_SWITCH, | |
125 | TokenTypes.LITERAL_CASE, | |
126 | TokenTypes.LITERAL_CATCH, | |
127 | TokenTypes.QUESTION, | |
128 | TokenTypes.LAND, | |
129 | TokenTypes.LOR, | |
130 | }; | |
131 | } | |
132 | ||
133 | @Override | |
134 | public final int[] getRequiredTokens() { | |
135 |
1
1. getRequiredTokens : mutated return of Object value for com/puppycrawl/tools/checkstyle/checks/metrics/CyclomaticComplexityCheck::getRequiredTokens to ( if (x != null) null else throw new RuntimeException ) → KILLED |
return new int[] { |
136 | TokenTypes.CTOR_DEF, | |
137 | TokenTypes.METHOD_DEF, | |
138 | TokenTypes.INSTANCE_INIT, | |
139 | TokenTypes.STATIC_INIT, | |
140 | }; | |
141 | } | |
142 | ||
143 | @Override | |
144 | public void visitToken(DetailAST ast) { | |
145 | switch (ast.getType()) { | |
146 | case TokenTypes.CTOR_DEF: | |
147 | case TokenTypes.METHOD_DEF: | |
148 | case TokenTypes.INSTANCE_INIT: | |
149 | case TokenTypes.STATIC_INIT: | |
150 |
1
1. visitToken : removed call to com/puppycrawl/tools/checkstyle/checks/metrics/CyclomaticComplexityCheck::visitMethodDef → KILLED |
visitMethodDef(); |
151 | break; | |
152 | default: | |
153 |
1
1. visitToken : removed call to com/puppycrawl/tools/checkstyle/checks/metrics/CyclomaticComplexityCheck::visitTokenHook → KILLED |
visitTokenHook(ast); |
154 | } | |
155 | } | |
156 | ||
157 | @Override | |
158 | public void leaveToken(DetailAST ast) { | |
159 | switch (ast.getType()) { | |
160 | case TokenTypes.CTOR_DEF: | |
161 | case TokenTypes.METHOD_DEF: | |
162 | case TokenTypes.INSTANCE_INIT: | |
163 | case TokenTypes.STATIC_INIT: | |
164 |
1
1. leaveToken : removed call to com/puppycrawl/tools/checkstyle/checks/metrics/CyclomaticComplexityCheck::leaveMethodDef → KILLED |
leaveMethodDef(ast); |
165 | break; | |
166 | default: | |
167 | break; | |
168 | } | |
169 | } | |
170 | ||
171 | /** | |
172 | * Hook called when visiting a token. Will not be called the method | |
173 | * definition tokens. | |
174 | * | |
175 | * @param ast the token being visited | |
176 | */ | |
177 | private void visitTokenHook(DetailAST ast) { | |
178 |
1
1. visitTokenHook : negated conditional → KILLED |
if (switchBlockAsSingleDecisionPoint) { |
179 |
1
1. visitTokenHook : negated conditional → KILLED |
if (ast.getType() != TokenTypes.LITERAL_CASE) { |
180 |
1
1. visitTokenHook : removed call to com/puppycrawl/tools/checkstyle/checks/metrics/CyclomaticComplexityCheck::incrementCurrentValue → KILLED |
incrementCurrentValue(BigInteger.ONE); |
181 | } | |
182 | } | |
183 |
1
1. visitTokenHook : negated conditional → KILLED |
else if (ast.getType() != TokenTypes.LITERAL_SWITCH) { |
184 |
1
1. visitTokenHook : removed call to com/puppycrawl/tools/checkstyle/checks/metrics/CyclomaticComplexityCheck::incrementCurrentValue → KILLED |
incrementCurrentValue(BigInteger.ONE); |
185 | } | |
186 | } | |
187 | ||
188 | /** | |
189 | * Process the end of a method definition. | |
190 | * | |
191 | * @param ast the token representing the method definition | |
192 | */ | |
193 | private void leaveMethodDef(DetailAST ast) { | |
194 | final BigInteger bigIntegerMax = BigInteger.valueOf(max); | |
195 |
2
1. leaveMethodDef : changed conditional boundary → KILLED 2. leaveMethodDef : negated conditional → KILLED |
if (currentValue.compareTo(bigIntegerMax) > 0) { |
196 |
1
1. leaveMethodDef : removed call to com/puppycrawl/tools/checkstyle/checks/metrics/CyclomaticComplexityCheck::log → KILLED |
log(ast, MSG_KEY, currentValue, bigIntegerMax); |
197 | } | |
198 |
1
1. leaveMethodDef : removed call to com/puppycrawl/tools/checkstyle/checks/metrics/CyclomaticComplexityCheck::popValue → KILLED |
popValue(); |
199 | } | |
200 | ||
201 | /** | |
202 | * Increments the current value by a specified amount. | |
203 | * | |
204 | * @param amount the amount to increment by | |
205 | */ | |
206 | private void incrementCurrentValue(BigInteger amount) { | |
207 | currentValue = currentValue.add(amount); | |
208 | } | |
209 | ||
210 | /** Push the current value on the stack. */ | |
211 | private void pushValue() { | |
212 |
1
1. pushValue : removed call to java/util/Deque::push → KILLED |
valueStack.push(currentValue); |
213 | currentValue = INITIAL_VALUE; | |
214 | } | |
215 | ||
216 | /** | |
217 | * Pops a value off the stack and makes it the current value. | |
218 | */ | |
219 | private void popValue() { | |
220 | currentValue = valueStack.pop(); | |
221 | } | |
222 | ||
223 | /** Process the start of the method definition. */ | |
224 | private void visitMethodDef() { | |
225 |
1
1. visitMethodDef : removed call to com/puppycrawl/tools/checkstyle/checks/metrics/CyclomaticComplexityCheck::pushValue → KILLED |
pushValue(); |
226 | } | |
227 | } | |
Mutations | ||
95 |
1.1 |
|
115 |
1.1 |
|
135 |
1.1 |
|
150 |
1.1 |
|
153 |
1.1 |
|
164 |
1.1 |
|
178 |
1.1 |
|
179 |
1.1 |
|
180 |
1.1 |
|
183 |
1.1 |
|
184 |
1.1 |
|
195 |
1.1 2.2 |
|
196 |
1.1 |
|
198 |
1.1 |
|
212 |
1.1 |
|
225 |
1.1 |