ReturnCountCheck.java

1
////////////////////////////////////////////////////////////////////////////////
2
// checkstyle: Checks Java source code for adherence to a set of rules.
3
// Copyright (C) 2001-2018 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.coding;
21
22
import java.util.ArrayDeque;
23
import java.util.Deque;
24
import java.util.regex.Pattern;
25
26
import com.puppycrawl.tools.checkstyle.FileStatefulCheck;
27
import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
28
import com.puppycrawl.tools.checkstyle.api.DetailAST;
29
import com.puppycrawl.tools.checkstyle.api.TokenTypes;
30
31
/**
32
 * <p>
33
 * Restricts the number of return statements in methods, constructors and lambda expressions
34
 * (2 by default). Ignores specified methods ({@code equals()} by default).
35
 * </p>
36
 * <p>
37
 * <b>max</b> property will only check returns in methods and lambdas that
38
 * return a specific value (Ex: 'return 1;').
39
 * </p>
40
 * <p>
41
 * <b>maxForVoid</b> property will only check returns in methods, constructors,
42
 * and lambdas that have no return type (IE 'return;'). It will only count
43
 * visible return statements. Return statements not normally written, but
44
 * implied, at the end of the method/constructor definition will not be taken
45
 * into account. To disallow "return;" in void return type methods, use a value
46
 * of 0.
47
 * </p>
48
 * <p>
49
 * Rationale: Too many return points can be indication that code is
50
 * attempting to do too much or may be difficult to understand.
51
 * </p>
52
 *
53
 * @author <a href="mailto:simon@redhillconsulting.com.au">Simon Harris</a>
54
 */
55
@FileStatefulCheck
56
public final class ReturnCountCheck extends AbstractCheck {
57
58
    /**
59
     * A key is pointing to the warning message text in "messages.properties"
60
     * file.
61
     */
62
    public static final String MSG_KEY = "return.count";
63
    /**
64
     * A key pointing to the warning message text in "messages.properties"
65
     * file.
66
     */
67
    public static final String MSG_KEY_VOID = "return.countVoid";
68
69
    /** Stack of method contexts. */
70
    private final Deque<Context> contextStack = new ArrayDeque<>();
71
72
    /** The regexp to match against. */
73
    private Pattern format = Pattern.compile("^equals$");
74
75
    /** Maximum allowed number of return statements. */
76
    private int max = 2;
77
    /** Maximum allowed number of return statements for void methods. */
78
    private int maxForVoid = 1;
79
    /** Current method context. */
80
    private Context context;
81
82
    @Override
83
    public int[] getDefaultTokens() {
84 1 1. getDefaultTokens : mutated return of Object value for com/puppycrawl/tools/checkstyle/checks/coding/ReturnCountCheck::getDefaultTokens to ( if (x != null) null else throw new RuntimeException ) → KILLED
        return new int[] {
85
            TokenTypes.CTOR_DEF,
86
            TokenTypes.METHOD_DEF,
87
            TokenTypes.LAMBDA,
88
            TokenTypes.LITERAL_RETURN,
89
        };
90
    }
91
92
    @Override
93
    public int[] getRequiredTokens() {
94 1 1. getRequiredTokens : mutated return of Object value for com/puppycrawl/tools/checkstyle/checks/coding/ReturnCountCheck::getRequiredTokens to ( if (x != null) null else throw new RuntimeException ) → KILLED
        return new int[] {TokenTypes.LITERAL_RETURN};
95
    }
96
97
    @Override
98
    public int[] getAcceptableTokens() {
99 1 1. getAcceptableTokens : mutated return of Object value for com/puppycrawl/tools/checkstyle/checks/coding/ReturnCountCheck::getAcceptableTokens to ( if (x != null) null else throw new RuntimeException ) → KILLED
        return new int[] {
100
            TokenTypes.CTOR_DEF,
101
            TokenTypes.METHOD_DEF,
102
            TokenTypes.LAMBDA,
103
            TokenTypes.LITERAL_RETURN,
104
        };
105
    }
106
107
    /**
108
     * Set the format for the specified regular expression.
109
     * @param pattern a pattern.
110
     */
111
    public void setFormat(Pattern pattern) {
112
        format = pattern;
113
    }
114
115
    /**
116
     * Setter for max property.
117
     * @param max maximum allowed number of return statements.
118
     */
119
    public void setMax(int max) {
120
        this.max = max;
121
    }
122
123
    /**
124
     * Setter for maxForVoid property.
125
     * @param maxForVoid maximum allowed number of return statements for void methods.
126
     */
127
    public void setMaxForVoid(int maxForVoid) {
128
        this.maxForVoid = maxForVoid;
129
    }
130
131
    @Override
132
    public void beginTree(DetailAST rootAST) {
133
        context = new Context(false);
134 1 1. beginTree : removed call to java/util/Deque::clear → KILLED
        contextStack.clear();
135
    }
136
137
    @Override
138
    public void visitToken(DetailAST ast) {
139
        switch (ast.getType()) {
140
            case TokenTypes.CTOR_DEF:
141
            case TokenTypes.METHOD_DEF:
142 1 1. visitToken : removed call to com/puppycrawl/tools/checkstyle/checks/coding/ReturnCountCheck::visitMethodDef → KILLED
                visitMethodDef(ast);
143
                break;
144
            case TokenTypes.LAMBDA:
145 1 1. visitToken : removed call to com/puppycrawl/tools/checkstyle/checks/coding/ReturnCountCheck::visitLambda → KILLED
                visitLambda();
146
                break;
147
            case TokenTypes.LITERAL_RETURN:
148 1 1. visitToken : removed call to com/puppycrawl/tools/checkstyle/checks/coding/ReturnCountCheck::visitReturn → KILLED
                visitReturn(ast);
149
                break;
150
            default:
151
                throw new IllegalStateException(ast.toString());
152
        }
153
    }
154
155
    @Override
156
    public void leaveToken(DetailAST ast) {
157
        switch (ast.getType()) {
158
            case TokenTypes.CTOR_DEF:
159
            case TokenTypes.METHOD_DEF:
160
            case TokenTypes.LAMBDA:
161 1 1. leaveToken : removed call to com/puppycrawl/tools/checkstyle/checks/coding/ReturnCountCheck::leave → KILLED
                leave(ast);
162
                break;
163
            case TokenTypes.LITERAL_RETURN:
164
                // Do nothing
165
                break;
166
            default:
167
                throw new IllegalStateException(ast.toString());
168
        }
169
    }
170
171
    /**
172
     * Creates new method context and places old one on the stack.
173
     * @param ast method definition for check.
174
     */
175
    private void visitMethodDef(DetailAST ast) {
176 1 1. visitMethodDef : removed call to java/util/Deque::push → KILLED
        contextStack.push(context);
177
        final DetailAST methodNameAST = ast.findFirstToken(TokenTypes.IDENT);
178 1 1. visitMethodDef : negated conditional → KILLED
        final boolean check = !format.matcher(methodNameAST.getText()).find();
179
        context = new Context(check);
180
    }
181
182
    /**
183
     * Checks number of return statements and restore previous context.
184
     * @param ast node to leave.
185
     */
186
    private void leave(DetailAST ast) {
187 1 1. leave : removed call to com/puppycrawl/tools/checkstyle/checks/coding/ReturnCountCheck$Context::checkCount → KILLED
        context.checkCount(ast);
188
        context = contextStack.pop();
189
    }
190
191
    /**
192
     * Creates new lambda context and places old one on the stack.
193
     */
194
    private void visitLambda() {
195 1 1. visitLambda : removed call to java/util/Deque::push → KILLED
        contextStack.push(context);
196
        context = new Context(true);
197
    }
198
199
    /**
200
     * Examines the return statement and tells context about it.
201
     * @param ast return statement to check.
202
     */
203
    private void visitReturn(DetailAST ast) {
204
        // we can't identify which max to use for lambdas, so we can only assign
205
        // after the first return statement is seen
206 1 1. visitReturn : negated conditional → KILLED
        if (ast.getFirstChild().getType() == TokenTypes.SEMI) {
207 1 1. visitReturn : removed call to com/puppycrawl/tools/checkstyle/checks/coding/ReturnCountCheck$Context::visitLiteralReturn → KILLED
            context.visitLiteralReturn(maxForVoid, true);
208
        }
209
        else {
210 1 1. visitReturn : removed call to com/puppycrawl/tools/checkstyle/checks/coding/ReturnCountCheck$Context::visitLiteralReturn → KILLED
            context.visitLiteralReturn(max, false);
211
        }
212
    }
213
214
    /**
215
     * Class to encapsulate information about one method.
216
     * @author <a href="mailto:simon@redhillconsulting.com.au">Simon Harris</a>
217
     */
218
    private class Context {
219
220
        /** Whether we should check this method or not. */
221
        private final boolean checking;
222
        /** Counter for return statements. */
223
        private int count;
224
        /** Maximum allowed number of return statements. */
225
        private Integer maxAllowed;
226
        /** Identifies if context is void. */
227
        private boolean isVoidContext;
228
229
        /**
230
         * Creates new method context.
231
         * @param checking should we check this method or not.
232
         */
233
        Context(boolean checking) {
234
            this.checking = checking;
235
        }
236
237
        /**
238
         * Increase the number of return statements and set context return type.
239
         * @param maxAssigned Maximum allowed number of return statements.
240
         * @param voidReturn Identifies if context is void.
241
         */
242
        public void visitLiteralReturn(int maxAssigned, Boolean voidReturn) {
243
            isVoidContext = voidReturn;
244 1 1. visitLiteralReturn : negated conditional → KILLED
            if (maxAllowed == null) {
245
                maxAllowed = maxAssigned;
246
            }
247
248 1 1. visitLiteralReturn : Replaced integer addition with subtraction → KILLED
            ++count;
249
        }
250
251
        /**
252
         * Checks if number of return statements in the method are more
253
         * than allowed.
254
         * @param ast method def associated with this context.
255
         */
256
        public void checkCount(DetailAST ast) {
257 4 1. checkCount : changed conditional boundary → KILLED
2. checkCount : negated conditional → KILLED
3. checkCount : negated conditional → KILLED
4. checkCount : negated conditional → KILLED
            if (checking && maxAllowed != null && count > maxAllowed) {
258 1 1. checkCount : negated conditional → KILLED
                if (isVoidContext) {
259 1 1. checkCount : removed call to com/puppycrawl/tools/checkstyle/checks/coding/ReturnCountCheck::log → KILLED
                    log(ast.getLineNo(), ast.getColumnNo(), MSG_KEY_VOID, count, maxAllowed);
260
                }
261
                else {
262 1 1. checkCount : removed call to com/puppycrawl/tools/checkstyle/checks/coding/ReturnCountCheck::log → KILLED
                    log(ast.getLineNo(), ast.getColumnNo(), MSG_KEY, count, maxAllowed);
263
                }
264
            }
265
        }
266
267
    }
268
269
}

Mutations

84

1.1
Location : getDefaultTokens
Killed by : com.puppycrawl.tools.checkstyle.checks.coding.ReturnCountCheckTest.testWithReturnOnlyAsTokens(com.puppycrawl.tools.checkstyle.checks.coding.ReturnCountCheckTest)
mutated return of Object value for com/puppycrawl/tools/checkstyle/checks/coding/ReturnCountCheck::getDefaultTokens to ( if (x != null) null else throw new RuntimeException ) → KILLED

94

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

99

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

134

1.1
Location : beginTree
Killed by : com.puppycrawl.tools.checkstyle.checks.coding.ReturnCountCheckTest.testClearState(com.puppycrawl.tools.checkstyle.checks.coding.ReturnCountCheckTest)
removed call to java/util/Deque::clear → KILLED

142

1.1
Location : visitToken
Killed by : com.puppycrawl.tools.checkstyle.checks.coding.ReturnCountCheckTest.testMaxForVoid(com.puppycrawl.tools.checkstyle.checks.coding.ReturnCountCheckTest)
removed call to com/puppycrawl/tools/checkstyle/checks/coding/ReturnCountCheck::visitMethodDef → KILLED

145

1.1
Location : visitToken
Killed by : com.puppycrawl.tools.checkstyle.checks.coding.ReturnCountCheckTest.testLambdasOnly(com.puppycrawl.tools.checkstyle.checks.coding.ReturnCountCheckTest)
removed call to com/puppycrawl/tools/checkstyle/checks/coding/ReturnCountCheck::visitLambda → KILLED

148

1.1
Location : visitToken
Killed by : com.puppycrawl.tools.checkstyle.checks.coding.ReturnCountCheckTest.testMaxForVoid(com.puppycrawl.tools.checkstyle.checks.coding.ReturnCountCheckTest)
removed call to com/puppycrawl/tools/checkstyle/checks/coding/ReturnCountCheck::visitReturn → KILLED

161

1.1
Location : leaveToken
Killed by : com.puppycrawl.tools.checkstyle.checks.coding.ReturnCountCheckTest.testMaxForVoid(com.puppycrawl.tools.checkstyle.checks.coding.ReturnCountCheckTest)
removed call to com/puppycrawl/tools/checkstyle/checks/coding/ReturnCountCheck::leave → KILLED

176

1.1
Location : visitMethodDef
Killed by : com.puppycrawl.tools.checkstyle.checks.coding.ReturnCountCheckTest.testMaxForVoid(com.puppycrawl.tools.checkstyle.checks.coding.ReturnCountCheckTest)
removed call to java/util/Deque::push → KILLED

178

1.1
Location : visitMethodDef
Killed by : com.puppycrawl.tools.checkstyle.checks.coding.ReturnCountCheckTest.testMaxForVoid(com.puppycrawl.tools.checkstyle.checks.coding.ReturnCountCheckTest)
negated conditional → KILLED

187

1.1
Location : leave
Killed by : com.puppycrawl.tools.checkstyle.checks.coding.ReturnCountCheckTest.testMaxForVoid(com.puppycrawl.tools.checkstyle.checks.coding.ReturnCountCheckTest)
removed call to com/puppycrawl/tools/checkstyle/checks/coding/ReturnCountCheck$Context::checkCount → KILLED

195

1.1
Location : visitLambda
Killed by : com.puppycrawl.tools.checkstyle.checks.coding.ReturnCountCheckTest.testLambdasOnly(com.puppycrawl.tools.checkstyle.checks.coding.ReturnCountCheckTest)
removed call to java/util/Deque::push → KILLED

206

1.1
Location : visitReturn
Killed by : com.puppycrawl.tools.checkstyle.checks.coding.ReturnCountCheckTest.testMaxForVoid(com.puppycrawl.tools.checkstyle.checks.coding.ReturnCountCheckTest)
negated conditional → KILLED

207

1.1
Location : visitReturn
Killed by : com.puppycrawl.tools.checkstyle.checks.coding.ReturnCountCheckTest.testMaxForVoid(com.puppycrawl.tools.checkstyle.checks.coding.ReturnCountCheckTest)
removed call to com/puppycrawl/tools/checkstyle/checks/coding/ReturnCountCheck$Context::visitLiteralReturn → KILLED

210

1.1
Location : visitReturn
Killed by : com.puppycrawl.tools.checkstyle.checks.coding.ReturnCountCheckTest.testMaxForVoid(com.puppycrawl.tools.checkstyle.checks.coding.ReturnCountCheckTest)
removed call to com/puppycrawl/tools/checkstyle/checks/coding/ReturnCountCheck$Context::visitLiteralReturn → KILLED

244

1.1
Location : visitLiteralReturn
Killed by : com.puppycrawl.tools.checkstyle.checks.coding.ReturnCountCheckTest.testMaxForVoid(com.puppycrawl.tools.checkstyle.checks.coding.ReturnCountCheckTest)
negated conditional → KILLED

248

1.1
Location : visitLiteralReturn
Killed by : com.puppycrawl.tools.checkstyle.checks.coding.ReturnCountCheckTest.testMaxForVoid(com.puppycrawl.tools.checkstyle.checks.coding.ReturnCountCheckTest)
Replaced integer addition with subtraction → KILLED

257

1.1
Location : checkCount
Killed by : com.puppycrawl.tools.checkstyle.checks.coding.ReturnCountCheckTest.testMaxForVoid(com.puppycrawl.tools.checkstyle.checks.coding.ReturnCountCheckTest)
changed conditional boundary → KILLED

2.2
Location : checkCount
Killed by : com.puppycrawl.tools.checkstyle.checks.coding.ReturnCountCheckTest.testMaxForVoid(com.puppycrawl.tools.checkstyle.checks.coding.ReturnCountCheckTest)
negated conditional → KILLED

3.3
Location : checkCount
Killed by : com.puppycrawl.tools.checkstyle.checks.coding.ReturnCountCheckTest.testMaxForVoid(com.puppycrawl.tools.checkstyle.checks.coding.ReturnCountCheckTest)
negated conditional → KILLED

4.4
Location : checkCount
Killed by : com.puppycrawl.tools.checkstyle.checks.coding.ReturnCountCheckTest.testMaxForVoid(com.puppycrawl.tools.checkstyle.checks.coding.ReturnCountCheckTest)
negated conditional → KILLED

258

1.1
Location : checkCount
Killed by : com.puppycrawl.tools.checkstyle.checks.coding.ReturnCountCheckTest.testMaxForVoid(com.puppycrawl.tools.checkstyle.checks.coding.ReturnCountCheckTest)
negated conditional → KILLED

259

1.1
Location : checkCount
Killed by : com.puppycrawl.tools.checkstyle.checks.coding.ReturnCountCheckTest.testMaxForVoid(com.puppycrawl.tools.checkstyle.checks.coding.ReturnCountCheckTest)
removed call to com/puppycrawl/tools/checkstyle/checks/coding/ReturnCountCheck::log → KILLED

262

1.1
Location : checkCount
Killed by : com.puppycrawl.tools.checkstyle.checks.coding.ReturnCountCheckTest.testMaxForVoid(com.puppycrawl.tools.checkstyle.checks.coding.ReturnCountCheckTest)
removed call to com/puppycrawl/tools/checkstyle/checks/coding/ReturnCountCheck::log → KILLED

Active mutators

Tests examined


Report generated by PIT 1.3.1