| 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.HashSet; | |
| 25 | import java.util.Set; | |
| 26 | ||
| 27 | import com.puppycrawl.tools.checkstyle.FileStatefulCheck; | |
| 28 | import com.puppycrawl.tools.checkstyle.api.AbstractCheck; | |
| 29 | import com.puppycrawl.tools.checkstyle.api.DetailAST; | |
| 30 | import com.puppycrawl.tools.checkstyle.api.Scope; | |
| 31 | import com.puppycrawl.tools.checkstyle.api.TokenTypes; | |
| 32 | import com.puppycrawl.tools.checkstyle.utils.ScopeUtils; | |
| 33 | ||
| 34 | /** | |
| 35 | * Checks that the parts of a class or interface declaration | |
| 36 | * appear in the order suggested by the | |
| 37 | * <a href= | |
| 38 | * "http://www.oracle.com/technetwork/java/javase/documentation/codeconventions-141855.html#1852"> | |
| 39 | * Code Conventions for the Java Programming Language</a>. | |
| 40 | * | |
| 41 | * | |
| 42 | * <ol> | |
| 43 | * <li> Class (static) variables. First the public class variables, then | |
| 44 | * the protected, then package level (no access modifier), and then | |
| 45 | * the private. </li> | |
| 46 | * <li> Instance variables. First the public class variables, then | |
| 47 | * the protected, then package level (no access modifier), and then | |
| 48 | * the private. </li> | |
| 49 | * <li> Constructors </li> | |
| 50 | * <li> Methods </li> | |
| 51 | * </ol> | |
| 52 | * | |
| 53 | * <p>ATTENTION: the check skips class fields which have | |
| 54 | * <a href="https://docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#jls-8.3.3"> | |
| 55 | * forward references </a> from validation due to the fact that we have Checkstyle's limitations | |
| 56 | * to clearly detect user intention of fields location and grouping. For example, | |
| 57 | * <pre>{@code | |
| 58 | * public class A { | |
| 59 | * private double x = 1.0; | |
| 60 | * private double y = 2.0; | |
| 61 | * public double slope = x / y; // will be skipped from validation due to forward reference | |
| 62 | * } | |
| 63 | * }</pre> | |
| 64 | * | |
| 65 | * <p>Available options: | |
| 66 | * <ul> | |
| 67 | * <li>ignoreModifiers</li> | |
| 68 | * <li>ignoreConstructors</li> | |
| 69 | * </ul> | |
| 70 | * | |
| 71 | * <p>Purpose of <b>ignore*</b> option is to ignore related violations, | |
| 72 | * however it still impacts on other class members. | |
| 73 | * | |
| 74 | * <p>For example: | |
| 75 | * <pre>{@code | |
| 76 | * class K { | |
| 77 | * int a; | |
| 78 | * void m(){} | |
| 79 | * K(){} <-- "Constructor definition in wrong order" | |
| 80 | * int b; <-- "Instance variable definition in wrong order" | |
| 81 | * } | |
| 82 | * }</pre> | |
| 83 | * | |
| 84 | * <p>With <b>ignoreConstructors</b> option: | |
| 85 | * <pre>{@code | |
| 86 | * class K { | |
| 87 | * int a; | |
| 88 | * void m(){} | |
| 89 | * K(){} | |
| 90 | * int b; <-- "Instance variable definition in wrong order" | |
| 91 | * } | |
| 92 | * }</pre> | |
| 93 | * | |
| 94 | * <p>With <b>ignoreConstructors</b> option and without a method definition in a source class: | |
| 95 | * <pre>{@code | |
| 96 | * class K { | |
| 97 | * int a; | |
| 98 | * K(){} | |
| 99 | * int b; <-- "Instance variable definition in wrong order" | |
| 100 | * } | |
| 101 | * }</pre> | |
| 102 | * | |
| 103 | * <p>An example of how to configure the check is: | |
| 104 | * | |
| 105 | * <pre> | |
| 106 | * <module name="DeclarationOrder"/> | |
| 107 | * </pre> | |
| 108 | * | |
| 109 | * @author r_auckenthaler | |
| 110 | */ | |
| 111 | @FileStatefulCheck | |
| 112 | public class DeclarationOrderCheck extends AbstractCheck { | |
| 113 | ||
| 114 | /** | |
| 115 | * A key is pointing to the warning message text in "messages.properties" | |
| 116 | * file. | |
| 117 | */ | |
| 118 | public static final String MSG_CONSTRUCTOR = "declaration.order.constructor"; | |
| 119 | ||
| 120 | /** | |
| 121 | * A key is pointing to the warning message text in "messages.properties" | |
| 122 | * file. | |
| 123 | */ | |
| 124 | public static final String MSG_STATIC = "declaration.order.static"; | |
| 125 | ||
| 126 | /** | |
| 127 | * A key is pointing to the warning message text in "messages.properties" | |
| 128 | * file. | |
| 129 | */ | |
| 130 | public static final String MSG_INSTANCE = "declaration.order.instance"; | |
| 131 | ||
| 132 | /** | |
| 133 | * A key is pointing to the warning message text in "messages.properties" | |
| 134 | * file. | |
| 135 | */ | |
| 136 | public static final String MSG_ACCESS = "declaration.order.access"; | |
| 137 | ||
| 138 | /** State for the VARIABLE_DEF. */ | |
| 139 | private static final int STATE_STATIC_VARIABLE_DEF = 1; | |
| 140 | ||
| 141 | /** State for the VARIABLE_DEF. */ | |
| 142 | private static final int STATE_INSTANCE_VARIABLE_DEF = 2; | |
| 143 | ||
| 144 | /** State for the CTOR_DEF. */ | |
| 145 | private static final int STATE_CTOR_DEF = 3; | |
| 146 | ||
| 147 | /** State for the METHOD_DEF. */ | |
| 148 | private static final int STATE_METHOD_DEF = 4; | |
| 149 | ||
| 150 | /** | |
| 151 | * List of Declaration States. This is necessary due to | |
| 152 | * inner classes that have their own state. | |
| 153 | */ | |
| 154 | private Deque<ScopeState> scopeStates; | |
| 155 | ||
| 156 | /** Set of all class field names.*/ | |
| 157 | private Set<String> classFieldNames; | |
| 158 | ||
| 159 | /** If true, ignores the check to constructors. */ | |
| 160 | private boolean ignoreConstructors; | |
| 161 | /** If true, ignore the check to modifiers (fields, ...). */ | |
| 162 | private boolean ignoreModifiers; | |
| 163 | ||
| 164 | @Override | |
| 165 | public int[] getDefaultTokens() { | |
| 166 |
1
1. getDefaultTokens : mutated return of Object value for com/puppycrawl/tools/checkstyle/checks/coding/DeclarationOrderCheck::getDefaultTokens to ( if (x != null) null else throw new RuntimeException ) → KILLED |
return getRequiredTokens(); |
| 167 | } | |
| 168 | ||
| 169 | @Override | |
| 170 | public int[] getAcceptableTokens() { | |
| 171 |
1
1. getAcceptableTokens : mutated return of Object value for com/puppycrawl/tools/checkstyle/checks/coding/DeclarationOrderCheck::getAcceptableTokens to ( if (x != null) null else throw new RuntimeException ) → KILLED |
return getRequiredTokens(); |
| 172 | } | |
| 173 | ||
| 174 | @Override | |
| 175 | public int[] getRequiredTokens() { | |
| 176 |
1
1. getRequiredTokens : mutated return of Object value for com/puppycrawl/tools/checkstyle/checks/coding/DeclarationOrderCheck::getRequiredTokens to ( if (x != null) null else throw new RuntimeException ) → KILLED |
return new int[] { |
| 177 | TokenTypes.CTOR_DEF, | |
| 178 | TokenTypes.METHOD_DEF, | |
| 179 | TokenTypes.MODIFIERS, | |
| 180 | TokenTypes.OBJBLOCK, | |
| 181 | TokenTypes.VARIABLE_DEF, | |
| 182 | }; | |
| 183 | } | |
| 184 | ||
| 185 | @Override | |
| 186 | public void beginTree(DetailAST rootAST) { | |
| 187 | scopeStates = new ArrayDeque<>(); | |
| 188 | classFieldNames = new HashSet<>(); | |
| 189 | } | |
| 190 | ||
| 191 | @Override | |
| 192 | public void visitToken(DetailAST ast) { | |
| 193 | final int parentType = ast.getParent().getType(); | |
| 194 | ||
| 195 | switch (ast.getType()) { | |
| 196 | case TokenTypes.OBJBLOCK: | |
| 197 |
1
1. visitToken : removed call to java/util/Deque::push → KILLED |
scopeStates.push(new ScopeState()); |
| 198 | break; | |
| 199 | case TokenTypes.MODIFIERS: | |
| 200 |
1
1. visitToken : negated conditional → KILLED |
if (parentType == TokenTypes.VARIABLE_DEF |
| 201 |
1
1. visitToken : negated conditional → KILLED |
&& ast.getParent().getParent().getType() == TokenTypes.OBJBLOCK) { |
| 202 |
1
1. visitToken : removed call to com/puppycrawl/tools/checkstyle/checks/coding/DeclarationOrderCheck::processModifiers → KILLED |
processModifiers(ast); |
| 203 | } | |
| 204 | break; | |
| 205 | case TokenTypes.CTOR_DEF: | |
| 206 |
1
1. visitToken : negated conditional → KILLED |
if (parentType == TokenTypes.OBJBLOCK) { |
| 207 |
1
1. visitToken : removed call to com/puppycrawl/tools/checkstyle/checks/coding/DeclarationOrderCheck::processConstructor → KILLED |
processConstructor(ast); |
| 208 | } | |
| 209 | break; | |
| 210 | case TokenTypes.METHOD_DEF: | |
| 211 |
1
1. visitToken : negated conditional → KILLED |
if (parentType == TokenTypes.OBJBLOCK) { |
| 212 | final ScopeState state = scopeStates.peek(); | |
| 213 | // nothing can be bigger than method's state | |
| 214 | state.currentScopeState = STATE_METHOD_DEF; | |
| 215 | } | |
| 216 | break; | |
| 217 | case TokenTypes.VARIABLE_DEF: | |
| 218 |
1
1. visitToken : negated conditional → KILLED |
if (ScopeUtils.isClassFieldDef(ast)) { |
| 219 | final DetailAST fieldDef = ast.findFirstToken(TokenTypes.IDENT); | |
| 220 | classFieldNames.add(fieldDef.getText()); | |
| 221 | } | |
| 222 | break; | |
| 223 | default: | |
| 224 | break; | |
| 225 | } | |
| 226 | } | |
| 227 | ||
| 228 | /** | |
| 229 | * Processes constructor. | |
| 230 | * @param ast constructor AST. | |
| 231 | */ | |
| 232 | private void processConstructor(DetailAST ast) { | |
| 233 | final ScopeState state = scopeStates.peek(); | |
| 234 |
2
1. processConstructor : changed conditional boundary → KILLED 2. processConstructor : negated conditional → KILLED |
if (state.currentScopeState > STATE_CTOR_DEF) { |
| 235 |
1
1. processConstructor : negated conditional → KILLED |
if (!ignoreConstructors) { |
| 236 |
1
1. processConstructor : removed call to com/puppycrawl/tools/checkstyle/checks/coding/DeclarationOrderCheck::log → KILLED |
log(ast, MSG_CONSTRUCTOR); |
| 237 | } | |
| 238 | } | |
| 239 | else { | |
| 240 | state.currentScopeState = STATE_CTOR_DEF; | |
| 241 | } | |
| 242 | } | |
| 243 | ||
| 244 | /** | |
| 245 | * Processes modifiers. | |
| 246 | * @param ast ast of Modifiers. | |
| 247 | */ | |
| 248 | private void processModifiers(DetailAST ast) { | |
| 249 | final ScopeState state = scopeStates.peek(); | |
| 250 | final boolean isStateValid = processModifiersState(ast, state); | |
| 251 |
1
1. processModifiers : removed call to com/puppycrawl/tools/checkstyle/checks/coding/DeclarationOrderCheck::processModifiersSubState → KILLED |
processModifiersSubState(ast, state, isStateValid); |
| 252 | } | |
| 253 | ||
| 254 | /** | |
| 255 | * Process if given modifiers are appropriate in given state | |
| 256 | * ({@code STATE_STATIC_VARIABLE_DEF}, {@code STATE_INSTANCE_VARIABLE_DEF}, | |
| 257 | * ({@code STATE_CTOR_DEF}, {@code STATE_METHOD_DEF}), if it is | |
| 258 | * it updates states where appropriate or logs violation. | |
| 259 | * @param modifierAst modifiers to process | |
| 260 | * @param state current state | |
| 261 | * @return true if modifierAst is valid in given state, false otherwise | |
| 262 | */ | |
| 263 | private boolean processModifiersState(DetailAST modifierAst, ScopeState state) { | |
| 264 | boolean isStateValid = true; | |
| 265 |
1
1. processModifiersState : negated conditional → KILLED |
if (modifierAst.findFirstToken(TokenTypes.LITERAL_STATIC) == null) { |
| 266 |
2
1. processModifiersState : changed conditional boundary → KILLED 2. processModifiersState : negated conditional → KILLED |
if (state.currentScopeState > STATE_INSTANCE_VARIABLE_DEF) { |
| 267 | isStateValid = false; | |
| 268 |
1
1. processModifiersState : removed call to com/puppycrawl/tools/checkstyle/checks/coding/DeclarationOrderCheck::log → KILLED |
log(modifierAst, MSG_INSTANCE); |
| 269 | } | |
| 270 |
1
1. processModifiersState : negated conditional → KILLED |
else if (state.currentScopeState == STATE_STATIC_VARIABLE_DEF) { |
| 271 | state.declarationAccess = Scope.PUBLIC; | |
| 272 | state.currentScopeState = STATE_INSTANCE_VARIABLE_DEF; | |
| 273 | } | |
| 274 | } | |
| 275 | else { | |
| 276 |
2
1. processModifiersState : changed conditional boundary → KILLED 2. processModifiersState : negated conditional → KILLED |
if (state.currentScopeState > STATE_STATIC_VARIABLE_DEF) { |
| 277 |
1
1. processModifiersState : negated conditional → KILLED |
if (!ignoreModifiers |
| 278 |
2
1. processModifiersState : changed conditional boundary → KILLED 2. processModifiersState : negated conditional → KILLED |
|| state.currentScopeState > STATE_INSTANCE_VARIABLE_DEF) { |
| 279 | isStateValid = false; | |
| 280 |
1
1. processModifiersState : removed call to com/puppycrawl/tools/checkstyle/checks/coding/DeclarationOrderCheck::log → KILLED |
log(modifierAst, MSG_STATIC); |
| 281 | } | |
| 282 | } | |
| 283 | else { | |
| 284 | state.currentScopeState = STATE_STATIC_VARIABLE_DEF; | |
| 285 | } | |
| 286 | } | |
| 287 |
1
1. processModifiersState : replaced return of integer sized value with (x == 0 ? 1 : 0) → KILLED |
return isStateValid; |
| 288 | } | |
| 289 | ||
| 290 | /** | |
| 291 | * Checks if given modifiers are valid in substate of given | |
| 292 | * state({@code Scope}), if it is it updates substate or else it | |
| 293 | * logs violation. | |
| 294 | * @param modifiersAst modifiers to process | |
| 295 | * @param state current state | |
| 296 | * @param isStateValid is main state for given modifiers is valid | |
| 297 | */ | |
| 298 | private void processModifiersSubState(DetailAST modifiersAst, ScopeState state, | |
| 299 | boolean isStateValid) { | |
| 300 | final Scope access = ScopeUtils.getScopeFromMods(modifiersAst); | |
| 301 |
2
1. processModifiersSubState : changed conditional boundary → KILLED 2. processModifiersSubState : negated conditional → KILLED |
if (state.declarationAccess.compareTo(access) > 0) { |
| 302 |
2
1. processModifiersSubState : negated conditional → KILLED 2. processModifiersSubState : negated conditional → KILLED |
if (isStateValid |
| 303 | && !ignoreModifiers | |
| 304 |
1
1. processModifiersSubState : negated conditional → KILLED |
&& !isForwardReference(modifiersAst.getParent())) { |
| 305 |
1
1. processModifiersSubState : removed call to com/puppycrawl/tools/checkstyle/checks/coding/DeclarationOrderCheck::log → KILLED |
log(modifiersAst, MSG_ACCESS); |
| 306 | } | |
| 307 | } | |
| 308 | else { | |
| 309 | state.declarationAccess = access; | |
| 310 | } | |
| 311 | } | |
| 312 | ||
| 313 | /** | |
| 314 | * Checks whether an identifier references a field which has been already defined in class. | |
| 315 | * @param fieldDef a field definition. | |
| 316 | * @return true if an identifier references a field which has been already defined in class. | |
| 317 | */ | |
| 318 | private boolean isForwardReference(DetailAST fieldDef) { | |
| 319 | final DetailAST exprStartIdent = fieldDef.findFirstToken(TokenTypes.IDENT); | |
| 320 | final Set<DetailAST> exprIdents = getAllTokensOfType(exprStartIdent, TokenTypes.IDENT); | |
| 321 | boolean forwardReference = false; | |
| 322 | for (DetailAST ident : exprIdents) { | |
| 323 |
1
1. isForwardReference : negated conditional → KILLED |
if (classFieldNames.contains(ident.getText())) { |
| 324 | forwardReference = true; | |
| 325 | break; | |
| 326 | } | |
| 327 | } | |
| 328 |
1
1. isForwardReference : replaced return of integer sized value with (x == 0 ? 1 : 0) → KILLED |
return forwardReference; |
| 329 | } | |
| 330 | ||
| 331 | /** | |
| 332 | * Collects all tokens of specific type starting with the current ast node. | |
| 333 | * @param ast ast node. | |
| 334 | * @param tokenType token type. | |
| 335 | * @return a set of all tokens of specific type starting with the current ast node. | |
| 336 | */ | |
| 337 | private static Set<DetailAST> getAllTokensOfType(DetailAST ast, int tokenType) { | |
| 338 | DetailAST vertex = ast; | |
| 339 | final Set<DetailAST> result = new HashSet<>(); | |
| 340 | final Deque<DetailAST> stack = new ArrayDeque<>(); | |
| 341 |
2
1. getAllTokensOfType : negated conditional → KILLED 2. getAllTokensOfType : negated conditional → KILLED |
while (vertex != null || !stack.isEmpty()) { |
| 342 |
1
1. getAllTokensOfType : negated conditional → KILLED |
if (!stack.isEmpty()) { |
| 343 | vertex = stack.pop(); | |
| 344 | } | |
| 345 |
1
1. getAllTokensOfType : negated conditional → TIMED_OUT |
while (vertex != null) { |
| 346 |
2
1. getAllTokensOfType : negated conditional → KILLED 2. getAllTokensOfType : negated conditional → KILLED |
if (vertex.getType() == tokenType && !vertex.equals(ast)) { |
| 347 | result.add(vertex); | |
| 348 | } | |
| 349 |
1
1. getAllTokensOfType : negated conditional → KILLED |
if (vertex.getNextSibling() != null) { |
| 350 |
1
1. getAllTokensOfType : removed call to java/util/Deque::push → KILLED |
stack.push(vertex.getNextSibling()); |
| 351 | } | |
| 352 | vertex = vertex.getFirstChild(); | |
| 353 | } | |
| 354 | } | |
| 355 |
1
1. getAllTokensOfType : mutated return of Object value for com/puppycrawl/tools/checkstyle/checks/coding/DeclarationOrderCheck::getAllTokensOfType to ( if (x != null) null else throw new RuntimeException ) → KILLED |
return result; |
| 356 | } | |
| 357 | ||
| 358 | @Override | |
| 359 | public void leaveToken(DetailAST ast) { | |
| 360 |
1
1. leaveToken : negated conditional → KILLED |
if (ast.getType() == TokenTypes.OBJBLOCK) { |
| 361 | scopeStates.pop(); | |
| 362 | } | |
| 363 | } | |
| 364 | ||
| 365 | /** | |
| 366 | * Sets whether to ignore constructors. | |
| 367 | * @param ignoreConstructors whether to ignore constructors. | |
| 368 | */ | |
| 369 | public void setIgnoreConstructors(boolean ignoreConstructors) { | |
| 370 | this.ignoreConstructors = ignoreConstructors; | |
| 371 | } | |
| 372 | ||
| 373 | /** | |
| 374 | * Sets whether to ignore modifiers. | |
| 375 | * @param ignoreModifiers whether to ignore modifiers. | |
| 376 | */ | |
| 377 | public void setIgnoreModifiers(boolean ignoreModifiers) { | |
| 378 | this.ignoreModifiers = ignoreModifiers; | |
| 379 | } | |
| 380 | ||
| 381 | /** | |
| 382 | * Private class to encapsulate the state. | |
| 383 | */ | |
| 384 | private static class ScopeState { | |
| 385 | ||
| 386 | /** The state the check is in. */ | |
| 387 | private int currentScopeState = STATE_STATIC_VARIABLE_DEF; | |
| 388 | ||
| 389 | /** The sub-state the check is in. */ | |
| 390 | private Scope declarationAccess = Scope.PUBLIC; | |
| 391 | ||
| 392 | } | |
| 393 | ||
| 394 | } | |
Mutations | ||
| 166 |
1.1 |
|
| 171 |
1.1 |
|
| 176 |
1.1 |
|
| 197 |
1.1 |
|
| 200 |
1.1 |
|
| 201 |
1.1 |
|
| 202 |
1.1 |
|
| 206 |
1.1 |
|
| 207 |
1.1 |
|
| 211 |
1.1 |
|
| 218 |
1.1 |
|
| 234 |
1.1 2.2 |
|
| 235 |
1.1 |
|
| 236 |
1.1 |
|
| 251 |
1.1 |
|
| 265 |
1.1 |
|
| 266 |
1.1 2.2 |
|
| 268 |
1.1 |
|
| 270 |
1.1 |
|
| 276 |
1.1 2.2 |
|
| 277 |
1.1 |
|
| 278 |
1.1 2.2 |
|
| 280 |
1.1 |
|
| 287 |
1.1 |
|
| 301 |
1.1 2.2 |
|
| 302 |
1.1 2.2 |
|
| 304 |
1.1 |
|
| 305 |
1.1 |
|
| 323 |
1.1 |
|
| 328 |
1.1 |
|
| 341 |
1.1 2.2 |
|
| 342 |
1.1 |
|
| 345 |
1.1 |
|
| 346 |
1.1 2.2 |
|
| 349 |
1.1 |
|
| 350 |
1.1 |
|
| 355 |
1.1 |
|
| 360 |
1.1 |