001/////////////////////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code and other text files for adherence to a set of rules. 003// Copyright (C) 2001-2025 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 * <div> 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 * </div> 041 * 042 * <p> 043 * According to 044 * <a href="https://checkstyle.org/styleguides/sun-code-conventions-19990420/CodeConventions.doc2.html#a1852"> 045 * Code Conventions for the Java Programming Language</a>, the parts of a class 046 * or interface declaration should appear in the following order: 047 * </p> 048 * <ol> 049 * <li> 050 * Class (static) variables. First the public class variables, then 051 * protected, then package level (no access modifier), and then private. 052 * </li> 053 * <li> Instance variables. First the public class variables, then 054 * protected, then package level (no access modifier), and then private. 055 * </li> 056 * <li> Constructors </li> 057 * <li> Methods </li> 058 * </ol> 059 * 060 * <p> 061 * Purpose of <b>ignore*</b> option is to ignore related violations, 062 * however it still impacts on other class members. 063 * </p> 064 * 065 * <p>ATTENTION: the check skips class fields which have 066 * <a href="https://docs.oracle.com/javase/specs/jls/se11/html/jls-8.html#jls-8.3.3"> 067 * forward references </a> from validation due to the fact that we have Checkstyle's limitations 068 * to clearly detect user intention of fields location and grouping. For example: 069 * </p> 070 * <div class="wrapper"><pre class="prettyprint"><code class="language-java"> 071 * public class A { 072 * private double x = 1.0; 073 * private double y = 2.0; 074 * public double slope = x / y; // will be skipped from validation due to forward reference 075 * } 076 * </code></pre></div> 077 * <ul> 078 * <li> 079 * Property {@code ignoreConstructors} - Control whether to ignore constructors. 080 * Type is {@code boolean}. 081 * Default value is {@code false}. 082 * </li> 083 * <li> 084 * Property {@code ignoreModifiers} - Control whether to ignore modifiers (fields, ...). 085 * Type is {@code boolean}. 086 * Default value is {@code false}. 087 * </li> 088 * </ul> 089 * 090 * <p> 091 * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker} 092 * </p> 093 * 094 * <p> 095 * Violation Message Keys: 096 * </p> 097 * <ul> 098 * <li> 099 * {@code declaration.order.access} 100 * </li> 101 * <li> 102 * {@code declaration.order.constructor} 103 * </li> 104 * <li> 105 * {@code declaration.order.instance} 106 * </li> 107 * <li> 108 * {@code declaration.order.static} 109 * </li> 110 * </ul> 111 * 112 * @since 3.2 113 */ 114@FileStatefulCheck 115public class DeclarationOrderCheck extends AbstractCheck { 116 117 /** 118 * A key is pointing to the warning message text in "messages.properties" 119 * file. 120 */ 121 public static final String MSG_CONSTRUCTOR = "declaration.order.constructor"; 122 123 /** 124 * A key is pointing to the warning message text in "messages.properties" 125 * file. 126 */ 127 public static final String MSG_STATIC = "declaration.order.static"; 128 129 /** 130 * A key is pointing to the warning message text in "messages.properties" 131 * file. 132 */ 133 public static final String MSG_INSTANCE = "declaration.order.instance"; 134 135 /** 136 * A key is pointing to the warning message text in "messages.properties" 137 * file. 138 */ 139 public static final String MSG_ACCESS = "declaration.order.access"; 140 141 /** State for the VARIABLE_DEF. */ 142 private static final int STATE_STATIC_VARIABLE_DEF = 1; 143 144 /** State for the VARIABLE_DEF. */ 145 private static final int STATE_INSTANCE_VARIABLE_DEF = 2; 146 147 /** State for the CTOR_DEF. */ 148 private static final int STATE_CTOR_DEF = 3; 149 150 /** State for the METHOD_DEF. */ 151 private static final int STATE_METHOD_DEF = 4; 152 153 /** 154 * List of Declaration States. This is necessary due to 155 * inner classes that have their own state. 156 */ 157 private Deque<ScopeState> scopeStates; 158 159 /** Set of all class field names.*/ 160 private Set<String> classFieldNames; 161 162 /** Control whether to ignore constructors. */ 163 private boolean ignoreConstructors; 164 /** Control whether to ignore modifiers (fields, ...). */ 165 private boolean ignoreModifiers; 166 167 @Override 168 public int[] getDefaultTokens() { 169 return getRequiredTokens(); 170 } 171 172 @Override 173 public int[] getAcceptableTokens() { 174 return getRequiredTokens(); 175 } 176 177 @Override 178 public int[] getRequiredTokens() { 179 return new int[] { 180 TokenTypes.CTOR_DEF, 181 TokenTypes.METHOD_DEF, 182 TokenTypes.MODIFIERS, 183 TokenTypes.OBJBLOCK, 184 TokenTypes.VARIABLE_DEF, 185 TokenTypes.COMPACT_CTOR_DEF, 186 }; 187 } 188 189 @Override 190 public void beginTree(DetailAST rootAST) { 191 scopeStates = new ArrayDeque<>(); 192 classFieldNames = new HashSet<>(); 193 } 194 195 @Override 196 public void visitToken(DetailAST ast) { 197 final int parentType = ast.getParent().getType(); 198 199 switch (ast.getType()) { 200 case TokenTypes.OBJBLOCK -> scopeStates.push(new ScopeState()); 201 202 case TokenTypes.MODIFIERS -> { 203 if (parentType == TokenTypes.VARIABLE_DEF 204 && ast.getParent().getParent().getType() == TokenTypes.OBJBLOCK) { 205 processModifiers(ast); 206 } 207 } 208 209 case TokenTypes.CTOR_DEF, TokenTypes.COMPACT_CTOR_DEF -> { 210 if (parentType == TokenTypes.OBJBLOCK) { 211 processConstructor(ast); 212 } 213 } 214 215 case TokenTypes.METHOD_DEF -> { 216 if (parentType == TokenTypes.OBJBLOCK) { 217 final ScopeState state = scopeStates.peek(); 218 // nothing can be bigger than method's state 219 state.currentScopeState = STATE_METHOD_DEF; 220 } 221 } 222 223 case TokenTypes.VARIABLE_DEF -> { 224 if (ScopeUtil.isClassFieldDef(ast)) { 225 final DetailAST fieldDef = ast.findFirstToken(TokenTypes.IDENT); 226 classFieldNames.add(fieldDef.getText()); 227 } 228 } 229 230 default -> { 231 // do nothing 232 } 233 } 234 } 235 236 /** 237 * Processes constructor. 238 * 239 * @param ast constructor AST. 240 */ 241 private void processConstructor(DetailAST ast) { 242 final ScopeState state = scopeStates.peek(); 243 if (state.currentScopeState > STATE_CTOR_DEF) { 244 if (!ignoreConstructors) { 245 log(ast, MSG_CONSTRUCTOR); 246 } 247 } 248 else { 249 state.currentScopeState = STATE_CTOR_DEF; 250 } 251 } 252 253 /** 254 * Processes modifiers. 255 * 256 * @param ast ast of Modifiers. 257 */ 258 private void processModifiers(DetailAST ast) { 259 final ScopeState state = scopeStates.peek(); 260 final boolean isStateValid = processModifiersState(ast, state); 261 processModifiersSubState(ast, state, isStateValid); 262 } 263 264 /** 265 * Process if given modifiers are appropriate in given state 266 * ({@code STATE_STATIC_VARIABLE_DEF}, {@code STATE_INSTANCE_VARIABLE_DEF}, 267 * ({@code STATE_CTOR_DEF}, {@code STATE_METHOD_DEF}), if it is 268 * it updates states where appropriate or logs violation. 269 * 270 * @param modifierAst modifiers to process 271 * @param state current state 272 * @return true if modifierAst is valid in given state, false otherwise 273 */ 274 private boolean processModifiersState(DetailAST modifierAst, ScopeState state) { 275 boolean isStateValid = true; 276 if (modifierAst.findFirstToken(TokenTypes.LITERAL_STATIC) == null) { 277 if (state.currentScopeState > STATE_INSTANCE_VARIABLE_DEF) { 278 isStateValid = false; 279 log(modifierAst, MSG_INSTANCE); 280 } 281 else if (state.currentScopeState == STATE_STATIC_VARIABLE_DEF) { 282 state.declarationAccess = Scope.PUBLIC; 283 state.currentScopeState = STATE_INSTANCE_VARIABLE_DEF; 284 } 285 } 286 else if (state.currentScopeState > STATE_INSTANCE_VARIABLE_DEF 287 || state.currentScopeState > STATE_STATIC_VARIABLE_DEF && !ignoreModifiers) { 288 isStateValid = false; 289 log(modifierAst, MSG_STATIC); 290 } 291 return isStateValid; 292 } 293 294 /** 295 * Checks if given modifiers are valid in substate of given 296 * state({@code Scope}), if it is it updates substate or else it 297 * logs violation. 298 * 299 * @param modifiersAst modifiers to process 300 * @param state current state 301 * @param isStateValid is main state for given modifiers is valid 302 */ 303 private void processModifiersSubState(DetailAST modifiersAst, ScopeState state, 304 boolean isStateValid) { 305 final Scope access = ScopeUtil.getScopeFromMods(modifiersAst); 306 if (state.declarationAccess.compareTo(access) > 0) { 307 if (isStateValid 308 && !ignoreModifiers 309 && !isForwardReference(modifiersAst.getParent())) { 310 log(modifiersAst, MSG_ACCESS); 311 } 312 } 313 else { 314 state.declarationAccess = access; 315 } 316 } 317 318 /** 319 * Checks whether an identifier references a field which has been already defined in class. 320 * 321 * @param fieldDef a field definition. 322 * @return true if an identifier references a field which has been already defined in class. 323 */ 324 private boolean isForwardReference(DetailAST fieldDef) { 325 final DetailAST exprStartIdent = fieldDef.findFirstToken(TokenTypes.IDENT); 326 final Set<DetailAST> exprIdents = getAllTokensOfType(exprStartIdent, TokenTypes.IDENT); 327 boolean forwardReference = false; 328 for (DetailAST ident : exprIdents) { 329 if (classFieldNames.contains(ident.getText())) { 330 forwardReference = true; 331 break; 332 } 333 } 334 return forwardReference; 335 } 336 337 /** 338 * Collects all tokens of specific type starting with the current ast node. 339 * 340 * @param ast ast node. 341 * @param tokenType token type. 342 * @return a set of all tokens of specific type starting with the current ast node. 343 */ 344 private static Set<DetailAST> getAllTokensOfType(DetailAST ast, int tokenType) { 345 DetailAST vertex = ast; 346 final Set<DetailAST> result = new HashSet<>(); 347 final Deque<DetailAST> stack = new ArrayDeque<>(); 348 while (vertex != null || !stack.isEmpty()) { 349 if (!stack.isEmpty()) { 350 vertex = stack.pop(); 351 } 352 while (vertex != null) { 353 if (vertex.getType() == tokenType && !vertex.equals(ast)) { 354 result.add(vertex); 355 } 356 if (vertex.getNextSibling() != null) { 357 stack.push(vertex.getNextSibling()); 358 } 359 vertex = vertex.getFirstChild(); 360 } 361 } 362 return result; 363 } 364 365 @Override 366 public void leaveToken(DetailAST ast) { 367 if (ast.getType() == TokenTypes.OBJBLOCK) { 368 scopeStates.pop(); 369 } 370 } 371 372 /** 373 * Setter to control whether to ignore constructors. 374 * 375 * @param ignoreConstructors whether to ignore constructors. 376 * @since 5.2 377 */ 378 public void setIgnoreConstructors(boolean ignoreConstructors) { 379 this.ignoreConstructors = ignoreConstructors; 380 } 381 382 /** 383 * Setter to control whether to ignore modifiers (fields, ...). 384 * 385 * @param ignoreModifiers whether to ignore modifiers. 386 * @since 5.2 387 */ 388 public void setIgnoreModifiers(boolean ignoreModifiers) { 389 this.ignoreModifiers = ignoreModifiers; 390 } 391 392 /** 393 * Private class to encapsulate the state. 394 */ 395 private static final class ScopeState { 396 397 /** The state the check is in. */ 398 private int currentScopeState = STATE_STATIC_VARIABLE_DEF; 399 400 /** The sub-state the check is in. */ 401 private Scope declarationAccess = Scope.PUBLIC; 402 403 } 404 405}