1 /////////////////////////////////////////////////////////////////////////////////////////////// 2 // checkstyle: Checks Java source code and other text files for adherence to a set of rules. 3 // Copyright (C) 2001-2024 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.modifier; 21 22 import com.puppycrawl.tools.checkstyle.StatelessCheck; 23 import com.puppycrawl.tools.checkstyle.api.AbstractCheck; 24 import com.puppycrawl.tools.checkstyle.api.DetailAST; 25 import com.puppycrawl.tools.checkstyle.api.TokenTypes; 26 import com.puppycrawl.tools.checkstyle.utils.ScopeUtil; 27 28 /** 29 * <div> 30 * Checks for implicit modifiers on interface members and nested types. 31 * </div> 32 * 33 * <p> 34 * This check is effectively the opposite of 35 * <a href="https://checkstyle.org/checks/modifier/redundantmodifier.html#RedundantModifier"> 36 * RedundantModifier</a>. 37 * It checks the modifiers on interface members, ensuring that certain modifiers are explicitly 38 * specified even though they are actually redundant. 39 * </p> 40 * 41 * <p> 42 * Methods in interfaces are {@code public} by default, however from Java 9 they can also be 43 * {@code private}. This check provides the ability to enforce that {@code public} is explicitly 44 * coded and not implicitly added by the compiler. 45 * </p> 46 * 47 * <p> 48 * From Java 8, there are three types of methods in interfaces - static methods marked with 49 * {@code static}, default methods marked with {@code default} and abstract methods which do not 50 * have to be marked with anything. From Java 9, there are also private methods marked with 51 * {@code private}. This check provides the ability to enforce that {@code abstract} is 52 * explicitly coded and not implicitly added by the compiler. 53 * </p> 54 * 55 * <p> 56 * Fields in interfaces are always {@code public static final} and as such the compiler does not 57 * require these modifiers. This check provides the ability to enforce that these modifiers are 58 * explicitly coded and not implicitly added by the compiler. 59 * </p> 60 * 61 * <p> 62 * Nested types within an interface are always {@code public static} and as such the compiler 63 * does not require the {@code public static} modifiers. This check provides the ability to 64 * enforce that the {@code public} and {@code static} modifiers are explicitly coded and not 65 * implicitly added by the compiler. 66 * </p> 67 * <pre> 68 * public interface AddressFactory { 69 * // check enforces code contains "public static final" 70 * public static final String UNKNOWN = "Unknown"; 71 * 72 * String OTHER = "Other"; // violation 73 * 74 * // check enforces code contains "public" or "private" 75 * public static AddressFactory instance(); 76 * 77 * // check enforces code contains "public abstract" 78 * public abstract Address createAddress(String addressLine, String city); 79 * 80 * List<Address> findAddresses(String city); // violation 81 * 82 * // check enforces default methods are explicitly declared "public" 83 * public default Address createAddress(String city) { 84 * return createAddress(UNKNOWN, city); 85 * } 86 * 87 * default Address createOtherAddress() { // violation 88 * return createAddress(OTHER, OTHER); 89 * } 90 * } 91 * </pre> 92 * 93 * <p> 94 * Rationale for this check: Methods, fields and nested types are treated differently 95 * depending on whether they are part of an interface or part of a class. For example, by 96 * default methods are package-scoped on classes, but public in interfaces. However, from 97 * Java 8 onwards, interfaces have changed to be much more like abstract classes. 98 * Interfaces now have static and instance methods with code. Developers should not have to 99 * remember which modifiers are required and which are implied. This check allows the simpler 100 * alternative approach to be adopted where the implied modifiers must always be coded explicitly. 101 * </p> 102 * <ul> 103 * <li> 104 * Property {@code violateImpliedAbstractMethod} - Control whether to enforce that {@code abstract} 105 * is explicitly coded on interface methods. 106 * Type is {@code boolean}. 107 * Default value is {@code true}. 108 * </li> 109 * <li> 110 * Property {@code violateImpliedFinalField} - Control whether to enforce that {@code final} 111 * is explicitly coded on interface fields. 112 * Type is {@code boolean}. 113 * Default value is {@code true}. 114 * </li> 115 * <li> 116 * Property {@code violateImpliedPublicField} - Control whether to enforce that {@code public} 117 * is explicitly coded on interface fields. 118 * Type is {@code boolean}. 119 * Default value is {@code true}. 120 * </li> 121 * <li> 122 * Property {@code violateImpliedPublicMethod} - Control whether to enforce that {@code public} 123 * is explicitly coded on interface methods. 124 * Type is {@code boolean}. 125 * Default value is {@code true}. 126 * </li> 127 * <li> 128 * Property {@code violateImpliedPublicNested} - Control whether to enforce that {@code public} 129 * is explicitly coded on interface nested types. 130 * Type is {@code boolean}. 131 * Default value is {@code true}. 132 * </li> 133 * <li> 134 * Property {@code violateImpliedStaticField} - Control whether to enforce that {@code static} 135 * is explicitly coded on interface fields. 136 * Type is {@code boolean}. 137 * Default value is {@code true}. 138 * </li> 139 * <li> 140 * Property {@code violateImpliedStaticNested} - Control whether to enforce that {@code static} 141 * is explicitly coded on interface nested types. 142 * Type is {@code boolean}. 143 * Default value is {@code true}. 144 * </li> 145 * </ul> 146 * 147 * <p> 148 * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker} 149 * </p> 150 * 151 * <p> 152 * Violation Message Keys: 153 * </p> 154 * <ul> 155 * <li> 156 * {@code interface.implied.modifier} 157 * </li> 158 * </ul> 159 * 160 * @since 8.12 161 */ 162 @StatelessCheck 163 public class InterfaceMemberImpliedModifierCheck 164 extends AbstractCheck { 165 166 /** 167 * A key is pointing to the warning message text in "messages.properties" file. 168 */ 169 public static final String MSG_KEY = "interface.implied.modifier"; 170 171 /** Name for 'public' access modifier. */ 172 private static final String PUBLIC_ACCESS_MODIFIER = "public"; 173 174 /** Name for 'abstract' keyword. */ 175 private static final String ABSTRACT_KEYWORD = "abstract"; 176 177 /** Name for 'static' keyword. */ 178 private static final String STATIC_KEYWORD = "static"; 179 180 /** Name for 'final' keyword. */ 181 private static final String FINAL_KEYWORD = "final"; 182 183 /** 184 * Control whether to enforce that {@code public} is explicitly coded 185 * on interface fields. 186 */ 187 private boolean violateImpliedPublicField = true; 188 189 /** 190 * Control whether to enforce that {@code static} is explicitly coded 191 * on interface fields. 192 */ 193 private boolean violateImpliedStaticField = true; 194 195 /** 196 * Control whether to enforce that {@code final} is explicitly coded 197 * on interface fields. 198 */ 199 private boolean violateImpliedFinalField = true; 200 201 /** 202 * Control whether to enforce that {@code public} is explicitly coded 203 * on interface methods. 204 */ 205 private boolean violateImpliedPublicMethod = true; 206 207 /** 208 * Control whether to enforce that {@code abstract} is explicitly coded 209 * on interface methods. 210 */ 211 private boolean violateImpliedAbstractMethod = true; 212 213 /** 214 * Control whether to enforce that {@code public} is explicitly coded 215 * on interface nested types. 216 */ 217 private boolean violateImpliedPublicNested = true; 218 219 /** 220 * Control whether to enforce that {@code static} is explicitly coded 221 * on interface nested types. 222 */ 223 private boolean violateImpliedStaticNested = true; 224 225 /** 226 * Setter to control whether to enforce that {@code public} is explicitly coded 227 * on interface fields. 228 * 229 * @param violateImpliedPublicField 230 * True to perform the check, false to turn the check off. 231 * @since 8.12 232 */ 233 public void setViolateImpliedPublicField(boolean violateImpliedPublicField) { 234 this.violateImpliedPublicField = violateImpliedPublicField; 235 } 236 237 /** 238 * Setter to control whether to enforce that {@code static} is explicitly coded 239 * on interface fields. 240 * 241 * @param violateImpliedStaticField 242 * True to perform the check, false to turn the check off. 243 * @since 8.12 244 */ 245 public void setViolateImpliedStaticField(boolean violateImpliedStaticField) { 246 this.violateImpliedStaticField = violateImpliedStaticField; 247 } 248 249 /** 250 * Setter to control whether to enforce that {@code final} is explicitly coded 251 * on interface fields. 252 * 253 * @param violateImpliedFinalField 254 * True to perform the check, false to turn the check off. 255 * @since 8.12 256 */ 257 public void setViolateImpliedFinalField(boolean violateImpliedFinalField) { 258 this.violateImpliedFinalField = violateImpliedFinalField; 259 } 260 261 /** 262 * Setter to control whether to enforce that {@code public} is explicitly coded 263 * on interface methods. 264 * 265 * @param violateImpliedPublicMethod 266 * True to perform the check, false to turn the check off. 267 * @since 8.12 268 */ 269 public void setViolateImpliedPublicMethod(boolean violateImpliedPublicMethod) { 270 this.violateImpliedPublicMethod = violateImpliedPublicMethod; 271 } 272 273 /** 274 * Setter to control whether to enforce that {@code abstract} is explicitly coded 275 * on interface methods. 276 * 277 * @param violateImpliedAbstractMethod 278 * True to perform the check, false to turn the check off. 279 * @since 8.12 280 */ 281 public void setViolateImpliedAbstractMethod(boolean violateImpliedAbstractMethod) { 282 this.violateImpliedAbstractMethod = violateImpliedAbstractMethod; 283 } 284 285 /** 286 * Setter to control whether to enforce that {@code public} is explicitly coded 287 * on interface nested types. 288 * 289 * @param violateImpliedPublicNested 290 * True to perform the check, false to turn the check off. 291 * @since 8.12 292 */ 293 public void setViolateImpliedPublicNested(boolean violateImpliedPublicNested) { 294 this.violateImpliedPublicNested = violateImpliedPublicNested; 295 } 296 297 /** 298 * Setter to control whether to enforce that {@code static} is explicitly coded 299 * on interface nested types. 300 * 301 * @param violateImpliedStaticNested 302 * True to perform the check, false to turn the check off. 303 * @since 8.12 304 */ 305 public void setViolateImpliedStaticNested(boolean violateImpliedStaticNested) { 306 this.violateImpliedStaticNested = violateImpliedStaticNested; 307 } 308 309 @Override 310 public int[] getDefaultTokens() { 311 return getAcceptableTokens(); 312 } 313 314 @Override 315 public int[] getRequiredTokens() { 316 return getAcceptableTokens(); 317 } 318 319 @Override 320 public int[] getAcceptableTokens() { 321 return new int[] { 322 TokenTypes.METHOD_DEF, 323 TokenTypes.VARIABLE_DEF, 324 TokenTypes.INTERFACE_DEF, 325 TokenTypes.CLASS_DEF, 326 TokenTypes.ENUM_DEF, 327 }; 328 } 329 330 @Override 331 public void visitToken(DetailAST ast) { 332 if (ScopeUtil.isInInterfaceBlock(ast) && !ScopeUtil.isInCodeBlock(ast)) { 333 switch (ast.getType()) { 334 case TokenTypes.METHOD_DEF: 335 processMethod(ast); 336 break; 337 case TokenTypes.VARIABLE_DEF: 338 processField(ast); 339 break; 340 case TokenTypes.CLASS_DEF: 341 case TokenTypes.INTERFACE_DEF: 342 case TokenTypes.ENUM_DEF: 343 processNestedType(ast); 344 break; 345 default: 346 throw new IllegalStateException(ast.toString()); 347 } 348 } 349 } 350 351 /** 352 * Check method in interface. 353 * 354 * @param ast the method AST 355 */ 356 private void processMethod(DetailAST ast) { 357 final DetailAST modifiers = ast.findFirstToken(TokenTypes.MODIFIERS); 358 if (violateImpliedPublicMethod 359 && modifiers.findFirstToken(TokenTypes.LITERAL_PRIVATE) == null 360 && modifiers.findFirstToken(TokenTypes.LITERAL_PUBLIC) == null) { 361 log(ast, MSG_KEY, PUBLIC_ACCESS_MODIFIER); 362 } 363 if (violateImpliedAbstractMethod 364 && modifiers.findFirstToken(TokenTypes.LITERAL_PRIVATE) == null 365 && modifiers.findFirstToken(TokenTypes.LITERAL_STATIC) == null 366 && modifiers.findFirstToken(TokenTypes.LITERAL_DEFAULT) == null 367 && modifiers.findFirstToken(TokenTypes.ABSTRACT) == null) { 368 log(ast, MSG_KEY, ABSTRACT_KEYWORD); 369 } 370 } 371 372 /** 373 * Check field in interface. 374 * 375 * @param ast the field AST 376 */ 377 private void processField(DetailAST ast) { 378 final DetailAST modifiers = ast.findFirstToken(TokenTypes.MODIFIERS); 379 if (violateImpliedPublicField 380 && modifiers.findFirstToken(TokenTypes.LITERAL_PUBLIC) == null) { 381 log(ast, MSG_KEY, PUBLIC_ACCESS_MODIFIER); 382 } 383 if (violateImpliedStaticField 384 && modifiers.findFirstToken(TokenTypes.LITERAL_STATIC) == null) { 385 log(ast, MSG_KEY, STATIC_KEYWORD); 386 } 387 if (violateImpliedFinalField 388 && modifiers.findFirstToken(TokenTypes.FINAL) == null) { 389 log(ast, MSG_KEY, FINAL_KEYWORD); 390 } 391 } 392 393 /** 394 * Check nested types in interface. 395 * 396 * @param ast the nested type AST 397 */ 398 private void processNestedType(DetailAST ast) { 399 final DetailAST modifiers = ast.findFirstToken(TokenTypes.MODIFIERS); 400 if (violateImpliedPublicNested 401 && modifiers.findFirstToken(TokenTypes.LITERAL_PUBLIC) == null) { 402 log(ast, MSG_KEY, PUBLIC_ACCESS_MODIFIER); 403 } 404 if (violateImpliedStaticNested 405 && modifiers.findFirstToken(TokenTypes.LITERAL_STATIC) == null) { 406 log(ast, MSG_KEY, STATIC_KEYWORD); 407 } 408 } 409 410 }