CyclomaticComplexityCheck.java

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
Location : getDefaultTokens
Killed by : com.puppycrawl.tools.checkstyle.checks.metrics.CyclomaticComplexityCheckTest.testHighMax(com.puppycrawl.tools.checkstyle.checks.metrics.CyclomaticComplexityCheckTest)
mutated return of Object value for com/puppycrawl/tools/checkstyle/checks/metrics/CyclomaticComplexityCheck::getDefaultTokens to ( if (x != null) null else throw new RuntimeException ) → KILLED

115

1.1
Location : getAcceptableTokens
Killed by : com.puppycrawl.tools.checkstyle.checks.metrics.CyclomaticComplexityCheckTest.testGetAcceptableTokens(com.puppycrawl.tools.checkstyle.checks.metrics.CyclomaticComplexityCheckTest)
mutated return of Object value for com/puppycrawl/tools/checkstyle/checks/metrics/CyclomaticComplexityCheck::getAcceptableTokens to ( if (x != null) null else throw new RuntimeException ) → KILLED

135

1.1
Location : getRequiredTokens
Killed by : com.puppycrawl.tools.checkstyle.checks.metrics.CyclomaticComplexityCheckTest.testHighMax(com.puppycrawl.tools.checkstyle.checks.metrics.CyclomaticComplexityCheckTest)
mutated return of Object value for com/puppycrawl/tools/checkstyle/checks/metrics/CyclomaticComplexityCheck::getRequiredTokens to ( if (x != null) null else throw new RuntimeException ) → KILLED

150

1.1
Location : visitToken
Killed by : com.puppycrawl.tools.checkstyle.checks.metrics.CyclomaticComplexityCheckTest.testHighMax(com.puppycrawl.tools.checkstyle.checks.metrics.CyclomaticComplexityCheckTest)
removed call to com/puppycrawl/tools/checkstyle/checks/metrics/CyclomaticComplexityCheck::visitMethodDef → KILLED

153

1.1
Location : visitToken
Killed by : com.puppycrawl.tools.checkstyle.checks.metrics.CyclomaticComplexityCheckTest.testSwitchBlockAsSingleDecisionPointSetToTrue(com.puppycrawl.tools.checkstyle.checks.metrics.CyclomaticComplexityCheckTest)
removed call to com/puppycrawl/tools/checkstyle/checks/metrics/CyclomaticComplexityCheck::visitTokenHook → KILLED

164

1.1
Location : leaveToken
Killed by : com.puppycrawl.tools.checkstyle.checks.metrics.CyclomaticComplexityCheckTest.testSwitchBlockAsSingleDecisionPointSetToTrue(com.puppycrawl.tools.checkstyle.checks.metrics.CyclomaticComplexityCheckTest)
removed call to com/puppycrawl/tools/checkstyle/checks/metrics/CyclomaticComplexityCheck::leaveMethodDef → KILLED

178

1.1
Location : visitTokenHook
Killed by : com.puppycrawl.tools.checkstyle.checks.metrics.CyclomaticComplexityCheckTest.testSwitchBlockAsSingleDecisionPointSetToTrue(com.puppycrawl.tools.checkstyle.checks.metrics.CyclomaticComplexityCheckTest)
negated conditional → KILLED

179

1.1
Location : visitTokenHook
Killed by : com.puppycrawl.tools.checkstyle.checks.metrics.CyclomaticComplexityCheckTest.testSwitchBlockAsSingleDecisionPointSetToTrue(com.puppycrawl.tools.checkstyle.checks.metrics.CyclomaticComplexityCheckTest)
negated conditional → KILLED

180

1.1
Location : visitTokenHook
Killed by : com.puppycrawl.tools.checkstyle.checks.metrics.CyclomaticComplexityCheckTest.testSwitchBlockAsSingleDecisionPointSetToTrue(com.puppycrawl.tools.checkstyle.checks.metrics.CyclomaticComplexityCheckTest)
removed call to com/puppycrawl/tools/checkstyle/checks/metrics/CyclomaticComplexityCheck::incrementCurrentValue → KILLED

183

1.1
Location : visitTokenHook
Killed by : com.puppycrawl.tools.checkstyle.checks.metrics.CyclomaticComplexityCheckTest.testSwitchBlockAsSingleDecisionPointSetToFalse(com.puppycrawl.tools.checkstyle.checks.metrics.CyclomaticComplexityCheckTest)
negated conditional → KILLED

184

1.1
Location : visitTokenHook
Killed by : com.puppycrawl.tools.checkstyle.checks.metrics.CyclomaticComplexityCheckTest.testSwitchBlockAsSingleDecisionPointSetToFalse(com.puppycrawl.tools.checkstyle.checks.metrics.CyclomaticComplexityCheckTest)
removed call to com/puppycrawl/tools/checkstyle/checks/metrics/CyclomaticComplexityCheck::incrementCurrentValue → KILLED

195

1.1
Location : leaveMethodDef
Killed by : com.puppycrawl.tools.checkstyle.checks.metrics.CyclomaticComplexityCheckTest.testEqualsMaxComplexity(com.puppycrawl.tools.checkstyle.checks.metrics.CyclomaticComplexityCheckTest)
changed conditional boundary → KILLED

2.2
Location : leaveMethodDef
Killed by : com.puppycrawl.tools.checkstyle.checks.metrics.CyclomaticComplexityCheckTest.testHighMax(com.puppycrawl.tools.checkstyle.checks.metrics.CyclomaticComplexityCheckTest)
negated conditional → KILLED

196

1.1
Location : leaveMethodDef
Killed by : com.puppycrawl.tools.checkstyle.checks.metrics.CyclomaticComplexityCheckTest.testSwitchBlockAsSingleDecisionPointSetToTrue(com.puppycrawl.tools.checkstyle.checks.metrics.CyclomaticComplexityCheckTest)
removed call to com/puppycrawl/tools/checkstyle/checks/metrics/CyclomaticComplexityCheck::log → KILLED

198

1.1
Location : leaveMethodDef
Killed by : com.puppycrawl.tools.checkstyle.checks.metrics.CyclomaticComplexityCheckTest.test(com.puppycrawl.tools.checkstyle.checks.metrics.CyclomaticComplexityCheckTest)
removed call to com/puppycrawl/tools/checkstyle/checks/metrics/CyclomaticComplexityCheck::popValue → KILLED

212

1.1
Location : pushValue
Killed by : com.puppycrawl.tools.checkstyle.checks.metrics.CyclomaticComplexityCheckTest.testHighMax(com.puppycrawl.tools.checkstyle.checks.metrics.CyclomaticComplexityCheckTest)
removed call to java/util/Deque::push → KILLED

225

1.1
Location : visitMethodDef
Killed by : com.puppycrawl.tools.checkstyle.checks.metrics.CyclomaticComplexityCheckTest.testHighMax(com.puppycrawl.tools.checkstyle.checks.metrics.CyclomaticComplexityCheckTest)
removed call to com/puppycrawl/tools/checkstyle/checks/metrics/CyclomaticComplexityCheck::pushValue → KILLED

Active mutators

Tests examined


Report generated by PIT 1.2.4