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