001/////////////////////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code and other text files for adherence to a set of rules. 003// Copyright (C) 2001-2023 the original author or authors. 004// 005// This library is free software; you can redistribute it and/or 006// modify it under the terms of the GNU Lesser General Public 007// License as published by the Free Software Foundation; either 008// version 2.1 of the License, or (at your option) any later version. 009// 010// This library is distributed in the hope that it will be useful, 011// but WITHOUT ANY WARRANTY; without even the implied warranty of 012// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 013// Lesser General Public License for more details. 014// 015// You should have received a copy of the GNU Lesser General Public 016// License along with this library; if not, write to the Free Software 017// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 018/////////////////////////////////////////////////////////////////////////////////////////////// 019 020package com.puppycrawl.tools.checkstyle.checks.coding; 021 022import java.util.ArrayDeque; 023import java.util.Deque; 024import java.util.HashSet; 025import java.util.Set; 026 027import com.puppycrawl.tools.checkstyle.FileStatefulCheck; 028import com.puppycrawl.tools.checkstyle.api.AbstractCheck; 029import com.puppycrawl.tools.checkstyle.api.DetailAST; 030import com.puppycrawl.tools.checkstyle.api.Scope; 031import com.puppycrawl.tools.checkstyle.api.TokenTypes; 032import com.puppycrawl.tools.checkstyle.utils.ScopeUtil; 033 034/** 035 * <p> 036 * Checks that the parts of a class, record, or interface declaration appear in the order 037 * suggested by the 038 * <a href="https://checkstyle.org/styleguides/sun-code-conventions-19990420/CodeConventions.doc2.html#a1852"> 039 * Code Conventions for the Java Programming Language</a>. 040 * </p> 041 * <p> 042 * According to 043 * <a href="https://checkstyle.org/styleguides/sun-code-conventions-19990420/CodeConventions.doc2.html#a1852"> 044 * Code Conventions for the Java Programming Language</a>, the parts of a class 045 * or interface declaration should appear in the following order: 046 * </p> 047 * <ol> 048 * <li> 049 * Class (static) variables. First the public class variables, then 050 * protected, then package level (no access modifier), and then private. 051 * </li> 052 * <li> Instance variables. First the public class variables, then 053 * protected, then package level (no access modifier), and then private. 054 * </li> 055 * <li> Constructors </li> 056 * <li> Methods </li> 057 * </ol> 058 * <p> 059 * Purpose of <b>ignore*</b> option is to ignore related violations, 060 * however it still impacts on other class members. 061 * </p> 062 * <p>ATTENTION: the check skips class fields which have 063 * <a href="https://docs.oracle.com/javase/specs/jls/se11/html/jls-8.html#jls-8.3.3"> 064 * forward references </a> from validation due to the fact that we have Checkstyle's limitations 065 * to clearly detect user intention of fields location and grouping. For example: 066 * </p> 067 * <pre> 068 * public class A { 069 * private double x = 1.0; 070 * private double y = 2.0; 071 * public double slope = x / y; // will be skipped from validation due to forward reference 072 * } 073 * </pre> 074 * <ul> 075 * <li> 076 * Property {@code ignoreConstructors} - control whether to ignore constructors. 077 * Type is {@code boolean}. 078 * Default value is {@code false}. 079 * </li> 080 * <li> 081 * Property {@code ignoreModifiers} - control whether to ignore modifiers (fields, ...). 082 * Type is {@code boolean}. 083 * Default value is {@code false}. 084 * </li> 085 * </ul> 086 * <p> 087 * To configure the check: 088 * </p> 089 * <pre> 090 * <module name="DeclarationOrder"/> 091 * </pre> 092 * <p>Example:</p> 093 * <pre> 094 * public class Test { 095 * 096 * public int a; 097 * protected int b; 098 * public int c; // violation, variable access definition in wrong order 099 * 100 * Test() { 101 * this.a = 0; 102 * } 103 * 104 * public void foo() { 105 * // This method does nothing 106 * } 107 * 108 * Test(int a) { // violation, constructor definition in wrong order 109 * this.a = a; 110 * } 111 * 112 * private String name; // violation, instance variable declaration in wrong order 113 * } 114 * </pre> 115 * <p> 116 * To configure the check to ignore validation of constructors: 117 * </p> 118 * <pre> 119 * <module name="DeclarationOrder"> 120 * <property name="ignoreConstructors" value="true"/> 121 * </module> 122 * </pre> 123 * <p>Example:</p> 124 * <pre> 125 * public class Test { 126 * 127 * public int a; 128 * protected int b; 129 * public int c; // violation, variable access definition in wrong order 130 * 131 * Test() { 132 * this.a = 0; 133 * } 134 * 135 * public void foo() { 136 * // This method does nothing 137 * } 138 * 139 * Test(int a) { // OK, validation of constructors ignored 140 * this.a = a; 141 * } 142 * 143 * private String name; // violation, instance variable declaration in wrong order 144 * } 145 * </pre> 146 * <p> 147 * To configure the check to ignore modifiers: 148 * </p> 149 * <pre> 150 * <module name="DeclarationOrder"> 151 * <property name="ignoreModifiers" value="true"/> 152 * </module> 153 * </pre> 154 * <p>Example:</p> 155 * <pre> 156 * public class Test { 157 * 158 * public int a; 159 * protected int b; 160 * public int c; // OK, access modifiers not considered while validating 161 * 162 * Test() { 163 * this.a = 0; 164 * } 165 * 166 * public void foo() { 167 * // This method does nothing 168 * } 169 * 170 * Test(int a) { // violation, constructor definition in wrong order 171 * this.a = a; 172 * } 173 * 174 * private String name; // violation, instance variable declaration in wrong order 175 * } 176 * </pre> 177 * <p> 178 * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker} 179 * </p> 180 * <p> 181 * Violation Message Keys: 182 * </p> 183 * <ul> 184 * <li> 185 * {@code declaration.order.access} 186 * </li> 187 * <li> 188 * {@code declaration.order.constructor} 189 * </li> 190 * <li> 191 * {@code declaration.order.instance} 192 * </li> 193 * <li> 194 * {@code declaration.order.static} 195 * </li> 196 * </ul> 197 * 198 * @since 3.2 199 */ 200@FileStatefulCheck 201public class DeclarationOrderCheck extends AbstractCheck { 202 203 /** 204 * A key is pointing to the warning message text in "messages.properties" 205 * file. 206 */ 207 public static final String MSG_CONSTRUCTOR = "declaration.order.constructor"; 208 209 /** 210 * A key is pointing to the warning message text in "messages.properties" 211 * file. 212 */ 213 public static final String MSG_STATIC = "declaration.order.static"; 214 215 /** 216 * A key is pointing to the warning message text in "messages.properties" 217 * file. 218 */ 219 public static final String MSG_INSTANCE = "declaration.order.instance"; 220 221 /** 222 * A key is pointing to the warning message text in "messages.properties" 223 * file. 224 */ 225 public static final String MSG_ACCESS = "declaration.order.access"; 226 227 /** State for the VARIABLE_DEF. */ 228 private static final int STATE_STATIC_VARIABLE_DEF = 1; 229 230 /** State for the VARIABLE_DEF. */ 231 private static final int STATE_INSTANCE_VARIABLE_DEF = 2; 232 233 /** State for the CTOR_DEF. */ 234 private static final int STATE_CTOR_DEF = 3; 235 236 /** State for the METHOD_DEF. */ 237 private static final int STATE_METHOD_DEF = 4; 238 239 /** 240 * List of Declaration States. This is necessary due to 241 * inner classes that have their own state. 242 */ 243 private Deque<ScopeState> scopeStates; 244 245 /** Set of all class field names.*/ 246 private Set<String> classFieldNames; 247 248 /** Control whether to ignore constructors. */ 249 private boolean ignoreConstructors; 250 /** Control whether to ignore modifiers (fields, ...). */ 251 private boolean ignoreModifiers; 252 253 @Override 254 public int[] getDefaultTokens() { 255 return getRequiredTokens(); 256 } 257 258 @Override 259 public int[] getAcceptableTokens() { 260 return getRequiredTokens(); 261 } 262 263 @Override 264 public int[] getRequiredTokens() { 265 return new int[] { 266 TokenTypes.CTOR_DEF, 267 TokenTypes.METHOD_DEF, 268 TokenTypes.MODIFIERS, 269 TokenTypes.OBJBLOCK, 270 TokenTypes.VARIABLE_DEF, 271 TokenTypes.COMPACT_CTOR_DEF, 272 }; 273 } 274 275 @Override 276 public void beginTree(DetailAST rootAST) { 277 scopeStates = new ArrayDeque<>(); 278 classFieldNames = new HashSet<>(); 279 } 280 281 @Override 282 public void visitToken(DetailAST ast) { 283 final int parentType = ast.getParent().getType(); 284 285 switch (ast.getType()) { 286 case TokenTypes.OBJBLOCK: 287 scopeStates.push(new ScopeState()); 288 break; 289 case TokenTypes.MODIFIERS: 290 if (parentType == TokenTypes.VARIABLE_DEF 291 && ast.getParent().getParent().getType() == TokenTypes.OBJBLOCK) { 292 processModifiers(ast); 293 } 294 break; 295 case TokenTypes.CTOR_DEF: 296 case TokenTypes.COMPACT_CTOR_DEF: 297 if (parentType == TokenTypes.OBJBLOCK) { 298 processConstructor(ast); 299 } 300 break; 301 case TokenTypes.METHOD_DEF: 302 if (parentType == TokenTypes.OBJBLOCK) { 303 final ScopeState state = scopeStates.peek(); 304 // nothing can be bigger than method's state 305 state.currentScopeState = STATE_METHOD_DEF; 306 } 307 break; 308 case TokenTypes.VARIABLE_DEF: 309 if (ScopeUtil.isClassFieldDef(ast)) { 310 final DetailAST fieldDef = ast.findFirstToken(TokenTypes.IDENT); 311 classFieldNames.add(fieldDef.getText()); 312 } 313 break; 314 default: 315 break; 316 } 317 } 318 319 /** 320 * Processes constructor. 321 * 322 * @param ast constructor AST. 323 */ 324 private void processConstructor(DetailAST ast) { 325 final ScopeState state = scopeStates.peek(); 326 if (state.currentScopeState > STATE_CTOR_DEF) { 327 if (!ignoreConstructors) { 328 log(ast, MSG_CONSTRUCTOR); 329 } 330 } 331 else { 332 state.currentScopeState = STATE_CTOR_DEF; 333 } 334 } 335 336 /** 337 * Processes modifiers. 338 * 339 * @param ast ast of Modifiers. 340 */ 341 private void processModifiers(DetailAST ast) { 342 final ScopeState state = scopeStates.peek(); 343 final boolean isStateValid = processModifiersState(ast, state); 344 processModifiersSubState(ast, state, isStateValid); 345 } 346 347 /** 348 * Process if given modifiers are appropriate in given state 349 * ({@code STATE_STATIC_VARIABLE_DEF}, {@code STATE_INSTANCE_VARIABLE_DEF}, 350 * ({@code STATE_CTOR_DEF}, {@code STATE_METHOD_DEF}), if it is 351 * it updates states where appropriate or logs violation. 352 * 353 * @param modifierAst modifiers to process 354 * @param state current state 355 * @return true if modifierAst is valid in given state, false otherwise 356 */ 357 private boolean processModifiersState(DetailAST modifierAst, ScopeState state) { 358 boolean isStateValid = true; 359 if (modifierAst.findFirstToken(TokenTypes.LITERAL_STATIC) == null) { 360 if (state.currentScopeState > STATE_INSTANCE_VARIABLE_DEF) { 361 isStateValid = false; 362 log(modifierAst, MSG_INSTANCE); 363 } 364 else if (state.currentScopeState == STATE_STATIC_VARIABLE_DEF) { 365 state.declarationAccess = Scope.PUBLIC; 366 state.currentScopeState = STATE_INSTANCE_VARIABLE_DEF; 367 } 368 } 369 else { 370 if (state.currentScopeState > STATE_STATIC_VARIABLE_DEF) { 371 if (!ignoreModifiers 372 || state.currentScopeState > STATE_INSTANCE_VARIABLE_DEF) { 373 isStateValid = false; 374 log(modifierAst, MSG_STATIC); 375 } 376 } 377 else { 378 state.currentScopeState = STATE_STATIC_VARIABLE_DEF; 379 } 380 } 381 return isStateValid; 382 } 383 384 /** 385 * Checks if given modifiers are valid in substate of given 386 * state({@code Scope}), if it is it updates substate or else it 387 * logs violation. 388 * 389 * @param modifiersAst modifiers to process 390 * @param state current state 391 * @param isStateValid is main state for given modifiers is valid 392 */ 393 private void processModifiersSubState(DetailAST modifiersAst, ScopeState state, 394 boolean isStateValid) { 395 final Scope access = ScopeUtil.getScopeFromMods(modifiersAst); 396 if (state.declarationAccess.compareTo(access) > 0) { 397 if (isStateValid 398 && !ignoreModifiers 399 && !isForwardReference(modifiersAst.getParent())) { 400 log(modifiersAst, MSG_ACCESS); 401 } 402 } 403 else { 404 state.declarationAccess = access; 405 } 406 } 407 408 /** 409 * Checks whether an identifier references a field which has been already defined in class. 410 * 411 * @param fieldDef a field definition. 412 * @return true if an identifier references a field which has been already defined in class. 413 */ 414 private boolean isForwardReference(DetailAST fieldDef) { 415 final DetailAST exprStartIdent = fieldDef.findFirstToken(TokenTypes.IDENT); 416 final Set<DetailAST> exprIdents = getAllTokensOfType(exprStartIdent, TokenTypes.IDENT); 417 boolean forwardReference = false; 418 for (DetailAST ident : exprIdents) { 419 if (classFieldNames.contains(ident.getText())) { 420 forwardReference = true; 421 break; 422 } 423 } 424 return forwardReference; 425 } 426 427 /** 428 * Collects all tokens of specific type starting with the current ast node. 429 * 430 * @param ast ast node. 431 * @param tokenType token type. 432 * @return a set of all tokens of specific type starting with the current ast node. 433 */ 434 private static Set<DetailAST> getAllTokensOfType(DetailAST ast, int tokenType) { 435 DetailAST vertex = ast; 436 final Set<DetailAST> result = new HashSet<>(); 437 final Deque<DetailAST> stack = new ArrayDeque<>(); 438 while (vertex != null || !stack.isEmpty()) { 439 if (!stack.isEmpty()) { 440 vertex = stack.pop(); 441 } 442 while (vertex != null) { 443 if (vertex.getType() == tokenType && !vertex.equals(ast)) { 444 result.add(vertex); 445 } 446 if (vertex.getNextSibling() != null) { 447 stack.push(vertex.getNextSibling()); 448 } 449 vertex = vertex.getFirstChild(); 450 } 451 } 452 return result; 453 } 454 455 @Override 456 public void leaveToken(DetailAST ast) { 457 if (ast.getType() == TokenTypes.OBJBLOCK) { 458 scopeStates.pop(); 459 } 460 } 461 462 /** 463 * Setter to control whether to ignore constructors. 464 * 465 * @param ignoreConstructors whether to ignore constructors. 466 */ 467 public void setIgnoreConstructors(boolean ignoreConstructors) { 468 this.ignoreConstructors = ignoreConstructors; 469 } 470 471 /** 472 * Setter to control whether to ignore modifiers (fields, ...). 473 * 474 * @param ignoreModifiers whether to ignore modifiers. 475 */ 476 public void setIgnoreModifiers(boolean ignoreModifiers) { 477 this.ignoreModifiers = ignoreModifiers; 478 } 479 480 /** 481 * Private class to encapsulate the state. 482 */ 483 private static class ScopeState { 484 485 /** The state the check is in. */ 486 private int currentScopeState = STATE_STATIC_VARIABLE_DEF; 487 488 /** The sub-state the check is in. */ 489 private Scope declarationAccess = Scope.PUBLIC; 490 491 } 492 493}