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 |
|
94 |
1.1 |
|
99 |
1.1 |
|
134 |
1.1 |
|
142 |
1.1 |
|
145 |
1.1 |
|
148 |
1.1 |
|
161 |
1.1 |
|
176 |
1.1 |
|
178 |
1.1 |
|
187 |
1.1 |
|
195 |
1.1 |
|
206 |
1.1 |
|
207 |
1.1 |
|
210 |
1.1 |
|
244 |
1.1 |
|
248 |
1.1 |
|
257 |
1.1 2.2 3.3 4.4 |
|
258 |
1.1 |
|
259 |
1.1 |
|
262 |
1.1 |