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