| 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 |