| 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.util.ArrayDeque; | |
| 23 | import java.util.Deque; | |
| 24 | ||
| 25 | import com.puppycrawl.tools.checkstyle.api.AbstractCheck; | |
| 26 | import com.puppycrawl.tools.checkstyle.api.DetailAST; | |
| 27 | import com.puppycrawl.tools.checkstyle.api.TokenTypes; | |
| 28 | import com.puppycrawl.tools.checkstyle.utils.CheckUtils; | |
| 29 | ||
| 30 | /** | |
| 31 | * Restricts nested boolean operators (&&, ||, &, | and ^) to | |
| 32 | * a specified depth (default = 3). | |
| 33 | * Note: &, | and ^ are not checked if they are part of constructor or | |
| 34 | * method call because they can be applied to non boolean variables and | |
| 35 | * Checkstyle does not know types of methods from different classes. | |
| 36 | * | |
| 37 | * @author <a href="mailto:simon@redhillconsulting.com.au">Simon Harris</a> | |
| 38 | * @author o_sukhodolsky | |
| 39 | */ | |
| 40 | public final class BooleanExpressionComplexityCheck extends AbstractCheck { | |
| 41 | ||
| 42 | /** | |
| 43 | * A key is pointing to the warning message text in "messages.properties" | |
| 44 | * file. | |
| 45 | */ | |
| 46 | public static final String MSG_KEY = "booleanExpressionComplexity"; | |
| 47 | ||
| 48 | /** Default allowed complexity. */ | |
| 49 | private static final int DEFAULT_MAX = 3; | |
| 50 | ||
| 51 | /** Stack of contexts. */ | |
| 52 | private final Deque<Context> contextStack = new ArrayDeque<>(); | |
| 53 | /** Maximum allowed complexity. */ | |
| 54 | private int max; | |
| 55 | /** Current context. */ | |
| 56 | private Context context = new Context(false); | |
| 57 | ||
| 58 | /** Creates new instance of the check. */ | |
| 59 | public BooleanExpressionComplexityCheck() { | |
| 60 | max = DEFAULT_MAX; | |
| 61 | } | |
| 62 | ||
| 63 | @Override | |
| 64 | public int[] getDefaultTokens() { | |
| 65 |
1
1. getDefaultTokens : mutated return of Object value for com/puppycrawl/tools/checkstyle/checks/metrics/BooleanExpressionComplexityCheck::getDefaultTokens to ( if (x != null) null else throw new RuntimeException ) → KILLED |
return new int[] { |
| 66 | TokenTypes.CTOR_DEF, | |
| 67 | TokenTypes.METHOD_DEF, | |
| 68 | TokenTypes.EXPR, | |
| 69 | TokenTypes.LAND, | |
| 70 | TokenTypes.BAND, | |
| 71 | TokenTypes.LOR, | |
| 72 | TokenTypes.BOR, | |
| 73 | TokenTypes.BXOR, | |
| 74 | }; | |
| 75 | } | |
| 76 | ||
| 77 | @Override | |
| 78 | public int[] getRequiredTokens() { | |
| 79 |
1
1. getRequiredTokens : mutated return of Object value for com/puppycrawl/tools/checkstyle/checks/metrics/BooleanExpressionComplexityCheck::getRequiredTokens to ( if (x != null) null else throw new RuntimeException ) → KILLED |
return new int[] { |
| 80 | TokenTypes.CTOR_DEF, | |
| 81 | TokenTypes.METHOD_DEF, | |
| 82 | TokenTypes.EXPR, | |
| 83 | }; | |
| 84 | } | |
| 85 | ||
| 86 | @Override | |
| 87 | public int[] getAcceptableTokens() { | |
| 88 |
1
1. getAcceptableTokens : mutated return of Object value for com/puppycrawl/tools/checkstyle/checks/metrics/BooleanExpressionComplexityCheck::getAcceptableTokens to ( if (x != null) null else throw new RuntimeException ) → KILLED |
return new int[] { |
| 89 | TokenTypes.CTOR_DEF, | |
| 90 | TokenTypes.METHOD_DEF, | |
| 91 | TokenTypes.EXPR, | |
| 92 | TokenTypes.LAND, | |
| 93 | TokenTypes.BAND, | |
| 94 | TokenTypes.LOR, | |
| 95 | TokenTypes.BOR, | |
| 96 | TokenTypes.BXOR, | |
| 97 | }; | |
| 98 | } | |
| 99 | ||
| 100 | /** | |
| 101 | * Setter for maximum allowed complexity. | |
| 102 | * @param max new maximum allowed complexity. | |
| 103 | */ | |
| 104 | public void setMax(int max) { | |
| 105 | this.max = max; | |
| 106 | } | |
| 107 | ||
| 108 | @Override | |
| 109 | public void visitToken(DetailAST ast) { | |
| 110 | switch (ast.getType()) { | |
| 111 | case TokenTypes.CTOR_DEF: | |
| 112 | case TokenTypes.METHOD_DEF: | |
| 113 |
1
1. visitToken : removed call to com/puppycrawl/tools/checkstyle/checks/metrics/BooleanExpressionComplexityCheck::visitMethodDef → KILLED |
visitMethodDef(ast); |
| 114 | break; | |
| 115 | case TokenTypes.EXPR: | |
| 116 |
1
1. visitToken : removed call to com/puppycrawl/tools/checkstyle/checks/metrics/BooleanExpressionComplexityCheck::visitExpr → KILLED |
visitExpr(); |
| 117 | break; | |
| 118 | case TokenTypes.BOR: | |
| 119 |
2
1. visitToken : negated conditional → KILLED 2. visitToken : negated conditional → KILLED |
if (!isPipeOperator(ast) && !isPassedInParameter(ast)) { |
| 120 |
1
1. visitToken : removed call to com/puppycrawl/tools/checkstyle/checks/metrics/BooleanExpressionComplexityCheck$Context::visitBooleanOperator → KILLED |
context.visitBooleanOperator(); |
| 121 | } | |
| 122 | break; | |
| 123 | case TokenTypes.BAND: | |
| 124 | case TokenTypes.BXOR: | |
| 125 |
1
1. visitToken : negated conditional → KILLED |
if (!isPassedInParameter(ast)) { |
| 126 |
1
1. visitToken : removed call to com/puppycrawl/tools/checkstyle/checks/metrics/BooleanExpressionComplexityCheck$Context::visitBooleanOperator → KILLED |
context.visitBooleanOperator(); |
| 127 | } | |
| 128 | break; | |
| 129 | case TokenTypes.LAND: | |
| 130 | case TokenTypes.LOR: | |
| 131 |
1
1. visitToken : removed call to com/puppycrawl/tools/checkstyle/checks/metrics/BooleanExpressionComplexityCheck$Context::visitBooleanOperator → KILLED |
context.visitBooleanOperator(); |
| 132 | break; | |
| 133 | default: | |
| 134 | throw new IllegalArgumentException("Unknown type: " + ast); | |
| 135 | } | |
| 136 | } | |
| 137 | ||
| 138 | /** | |
| 139 | * Checks if logical operator is part of constructor or method call. | |
| 140 | * @param logicalOperator logical operator | |
| 141 | * @return true if logical operator is part of constructor or method call | |
| 142 | */ | |
| 143 | private static boolean isPassedInParameter(DetailAST logicalOperator) { | |
| 144 |
2
1. isPassedInParameter : negated conditional → KILLED 2. isPassedInParameter : replaced return of integer sized value with (x == 0 ? 1 : 0) → KILLED |
return logicalOperator.getParent().getType() == TokenTypes.EXPR |
| 145 |
1
1. isPassedInParameter : negated conditional → KILLED |
&& logicalOperator.getParent().getParent().getType() == TokenTypes.ELIST; |
| 146 | } | |
| 147 | ||
| 148 | /** | |
| 149 | * Checks if {@link TokenTypes#BOR binary OR} is applied to exceptions | |
| 150 | * in | |
| 151 | * <a href="http://docs.oracle.com/javase/specs/jls/se8/html/jls-14.html#jls-14.20"> | |
| 152 | * multi-catch</a> (pipe-syntax). | |
| 153 | * @param binaryOr {@link TokenTypes#BOR binary or} | |
| 154 | * @return true if binary or is applied to exceptions in multi-catch. | |
| 155 | */ | |
| 156 | private static boolean isPipeOperator(DetailAST binaryOr) { | |
| 157 |
2
1. isPipeOperator : negated conditional → KILLED 2. isPipeOperator : replaced return of integer sized value with (x == 0 ? 1 : 0) → KILLED |
return binaryOr.getParent().getType() == TokenTypes.TYPE; |
| 158 | } | |
| 159 | ||
| 160 | @Override | |
| 161 | public void leaveToken(DetailAST ast) { | |
| 162 | switch (ast.getType()) { | |
| 163 | case TokenTypes.CTOR_DEF: | |
| 164 | case TokenTypes.METHOD_DEF: | |
| 165 |
1
1. leaveToken : removed call to com/puppycrawl/tools/checkstyle/checks/metrics/BooleanExpressionComplexityCheck::leaveMethodDef → SURVIVED |
leaveMethodDef(); |
| 166 | break; | |
| 167 | case TokenTypes.EXPR: | |
| 168 |
1
1. leaveToken : removed call to com/puppycrawl/tools/checkstyle/checks/metrics/BooleanExpressionComplexityCheck::leaveExpr → KILLED |
leaveExpr(ast); |
| 169 | break; | |
| 170 | default: | |
| 171 | // Do nothing | |
| 172 | } | |
| 173 | } | |
| 174 | ||
| 175 | /** | |
| 176 | * Creates new context for a given method. | |
| 177 | * @param ast a method we start to check. | |
| 178 | */ | |
| 179 | private void visitMethodDef(DetailAST ast) { | |
| 180 |
1
1. visitMethodDef : removed call to java/util/Deque::push → KILLED |
contextStack.push(context); |
| 181 |
1
1. visitMethodDef : negated conditional → KILLED |
final boolean check = !CheckUtils.isEqualsMethod(ast); |
| 182 | context = new Context(check); | |
| 183 | } | |
| 184 | ||
| 185 | /** Removes old context. */ | |
| 186 | private void leaveMethodDef() { | |
| 187 | context = contextStack.pop(); | |
| 188 | } | |
| 189 | ||
| 190 | /** Creates and pushes new context. */ | |
| 191 | private void visitExpr() { | |
| 192 |
1
1. visitExpr : removed call to java/util/Deque::push → KILLED |
contextStack.push(context); |
| 193 | context = new Context(context.isChecking()); | |
| 194 | } | |
| 195 | ||
| 196 | /** | |
| 197 | * Restores previous context. | |
| 198 | * @param ast expression we leave. | |
| 199 | */ | |
| 200 | private void leaveExpr(DetailAST ast) { | |
| 201 |
1
1. leaveExpr : removed call to com/puppycrawl/tools/checkstyle/checks/metrics/BooleanExpressionComplexityCheck$Context::checkCount → KILLED |
context.checkCount(ast); |
| 202 | context = contextStack.pop(); | |
| 203 | } | |
| 204 | ||
| 205 | /** | |
| 206 | * Represents context (method/expression) in which we check complexity. | |
| 207 | * | |
| 208 | * @author <a href="mailto:simon@redhillconsulting.com.au">Simon Harris</a> | |
| 209 | * @author o_sukhodolsky | |
| 210 | */ | |
| 211 | private class Context { | |
| 212 | /** | |
| 213 | * Should we perform check in current context or not. | |
| 214 | * Usually false if we are inside equals() method. | |
| 215 | */ | |
| 216 | private final boolean checking; | |
| 217 | /** Count of boolean operators. */ | |
| 218 | private int count; | |
| 219 | ||
| 220 | /** | |
| 221 | * Creates new instance. | |
| 222 | * @param checking should we check in current context or not. | |
| 223 | */ | |
| 224 | Context(boolean checking) { | |
| 225 | this.checking = checking; | |
| 226 | count = 0; | |
| 227 | } | |
| 228 | ||
| 229 | /** | |
| 230 | * Getter for checking property. | |
| 231 | * @return should we check in current context or not. | |
| 232 | */ | |
| 233 | public boolean isChecking() { | |
| 234 |
1
1. isChecking : replaced return of integer sized value with (x == 0 ? 1 : 0) → KILLED |
return checking; |
| 235 | } | |
| 236 | ||
| 237 | /** Increases operator counter. */ | |
| 238 | public void visitBooleanOperator() { | |
| 239 |
1
1. visitBooleanOperator : Replaced integer addition with subtraction → KILLED |
++count; |
| 240 | } | |
| 241 | ||
| 242 | /** | |
| 243 | * Checks if we violates maximum allowed complexity. | |
| 244 | * @param ast a node we check now. | |
| 245 | */ | |
| 246 | public void checkCount(DetailAST ast) { | |
| 247 |
3
1. checkCount : changed conditional boundary → KILLED 2. checkCount : negated conditional → KILLED 3. checkCount : negated conditional → KILLED |
if (checking && count > max) { |
| 248 | final DetailAST parentAST = ast.getParent(); | |
| 249 | ||
| 250 |
1
1. checkCount : removed call to com/puppycrawl/tools/checkstyle/checks/metrics/BooleanExpressionComplexityCheck::log → KILLED |
log(parentAST.getLineNo(), parentAST.getColumnNo(), |
| 251 | MSG_KEY, count, max); | |
| 252 | } | |
| 253 | } | |
| 254 | } | |
| 255 | } | |
Mutations | ||
| 65 |
1.1 |
|
| 79 |
1.1 |
|
| 88 |
1.1 |
|
| 113 |
1.1 |
|
| 116 |
1.1 |
|
| 119 |
1.1 2.2 |
|
| 120 |
1.1 |
|
| 125 |
1.1 |
|
| 126 |
1.1 |
|
| 131 |
1.1 |
|
| 144 |
1.1 2.2 |
|
| 145 |
1.1 |
|
| 157 |
1.1 2.2 |
|
| 165 |
1.1 |
|
| 168 |
1.1 |
|
| 180 |
1.1 |
|
| 181 |
1.1 |
|
| 192 |
1.1 |
|
| 201 |
1.1 |
|
| 234 |
1.1 |
|
| 239 |
1.1 |
|
| 247 |
1.1 2.2 3.3 |
|
| 250 |
1.1 |