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.javadoc; 021 022import java.util.Set; 023import java.util.regex.Matcher; 024import java.util.regex.Pattern; 025 026import com.puppycrawl.tools.checkstyle.FileStatefulCheck; 027import com.puppycrawl.tools.checkstyle.api.AbstractCheck; 028import com.puppycrawl.tools.checkstyle.api.DetailAST; 029import com.puppycrawl.tools.checkstyle.api.FileContents; 030import com.puppycrawl.tools.checkstyle.api.Scope; 031import com.puppycrawl.tools.checkstyle.api.TextBlock; 032import com.puppycrawl.tools.checkstyle.api.TokenTypes; 033import com.puppycrawl.tools.checkstyle.utils.AnnotationUtil; 034import com.puppycrawl.tools.checkstyle.utils.CheckUtil; 035import com.puppycrawl.tools.checkstyle.utils.CommonUtil; 036import com.puppycrawl.tools.checkstyle.utils.ScopeUtil; 037 038/** 039 * <p> 040 * Checks for missing Javadoc comments for a method or constructor. The scope to verify is 041 * specified using the {@code Scope} class and defaults to {@code Scope.PUBLIC}. To verify 042 * another scope, set property scope to a different 043 * <a href="https://checkstyle.org/property_types.html#Scope">scope</a>. 044 * </p> 045 * <p> 046 * Javadoc is not required on a method that is tagged with the {@code @Override} annotation. 047 * However, under Java 5 it is not possible to mark a method required for an interface (this 048 * was <i>corrected</i> under Java 6). Hence, Checkstyle supports using the convention of using 049 * a single {@code {@inheritDoc}} tag instead of all the other tags. 050 * </p> 051 * <p> 052 * For getters and setters for the property {@code allowMissingPropertyJavadoc}, the methods must 053 * match exactly the structures below. 054 * </p> 055 * <pre> 056 * public void setNumber(final int number) 057 * { 058 * mNumber = number; 059 * } 060 * 061 * public int getNumber() 062 * { 063 * return mNumber; 064 * } 065 * 066 * public boolean isSomething() 067 * { 068 * return false; 069 * } 070 * </pre> 071 * <ul> 072 * <li> 073 * Property {@code minLineCount} - Control the minimal amount of lines in method to allow no 074 * documentation. 075 * Type is {@code int}. 076 * Default value is {@code -1}. 077 * </li> 078 * <li> 079 * Property {@code allowedAnnotations} - Configure annotations that allow missed 080 * documentation. 081 * Type is {@code java.lang.String[]}. 082 * Default value is {@code Override}. 083 * </li> 084 * <li> 085 * Property {@code scope} - Specify the visibility scope where Javadoc comments are checked. 086 * Type is {@code com.puppycrawl.tools.checkstyle.api.Scope}. 087 * Default value is {@code public}. 088 * </li> 089 * <li> 090 * Property {@code excludeScope} - Specify the visibility scope where Javadoc comments are 091 * not checked. 092 * Type is {@code com.puppycrawl.tools.checkstyle.api.Scope}. 093 * Default value is {@code null}. 094 * </li> 095 * <li> 096 * Property {@code allowMissingPropertyJavadoc} - Control whether to allow missing Javadoc on 097 * accessor methods for properties (setters and getters). 098 * Type is {@code boolean}. 099 * Default value is {@code false}. 100 * </li> 101 * <li> 102 * Property {@code ignoreMethodNamesRegex} - ignore method whose names are matching specified 103 * regex. 104 * Type is {@code java.util.regex.Pattern}. 105 * Default value is {@code null}. 106 * </li> 107 * <li> 108 * Property {@code tokens} - tokens to check 109 * Type is {@code java.lang.String[]}. 110 * Validation type is {@code tokenSet}. 111 * Default value is: 112 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#METHOD_DEF"> 113 * METHOD_DEF</a>, 114 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#CTOR_DEF"> 115 * CTOR_DEF</a>, 116 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#ANNOTATION_FIELD_DEF"> 117 * ANNOTATION_FIELD_DEF</a>, 118 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#COMPACT_CTOR_DEF"> 119 * COMPACT_CTOR_DEF</a>. 120 * </li> 121 * </ul> 122 * <p> 123 * To configure the default check: 124 * </p> 125 * <pre> 126 * <module name="MissingJavadocMethod"/> 127 * </pre> 128 * <p> 129 * Example: 130 * </p> 131 * <pre> 132 * public class Test { 133 * public Test() {} // violation, missing javadoc for constructor 134 * public void test() {} // violation, missing javadoc for method 135 * /** 136 * * Some description here. 137 * */ 138 * public void test2() {} // OK 139 * 140 * @Override 141 * public String toString() { // OK 142 * return "Some string"; 143 * } 144 * 145 * private void test1() {} // OK 146 * protected void test2() {} // OK 147 * void test3() {} // OK 148 * } 149 * </pre> 150 * 151 * <p> 152 * To configure the check for {@code private} scope: 153 * </p> 154 * <pre> 155 * <module name="MissingJavadocMethod"> 156 * <property name="scope" value="private"/> 157 * </module> 158 * </pre> 159 * <p>Example:</p> 160 * <pre> 161 * public class Test { 162 * private void test1() {} // violation, the private method is missing javadoc 163 * } 164 * </pre> 165 * 166 * <p> 167 * To configure the check for methods which are in {@code private}, but not in {@code protected} 168 * scope: 169 * </p> 170 * <pre> 171 * <module name="MissingJavadocMethod"> 172 * <property name="scope" value="private"/> 173 * <property name="excludeScope" value="protected"/> 174 * </module> 175 * </pre> 176 * <p>Example:</p> 177 * <pre> 178 * public class Test { 179 * private void test1() {} // violation, the private method is missing javadoc 180 * /** 181 * * Some description here 182 * */ 183 * private void test1() {} // OK 184 * protected void test2() {} // OK 185 * } 186 * </pre> 187 * 188 * <p> 189 * To configure the check for ignoring methods named {@code foo(),foo1(),foo2()}, etc.: 190 * </p> 191 * <pre> 192 * <module name="MissingJavadocMethod"> 193 * <property name="ignoreMethodNamesRegex" value="^foo.*$"/> 194 * </module> 195 * </pre> 196 * <p>Example:</p> 197 * <pre> 198 * public class Test { 199 * public void test1() {} // violation, method is missing javadoc 200 * public void foo() {} // OK 201 * public void foobar() {} // OK 202 * } 203 * </pre> 204 * 205 * <p> 206 * To configure the check for ignoring missing javadoc for accessor methods: 207 * </p> 208 * <pre> 209 * <module name="MissingJavadocMethod"> 210 * <property name="allowMissingPropertyJavadoc" value="true"/> 211 * </module> 212 * </pre> 213 * <p>Example:</p> 214 * <pre> 215 * public class Test { 216 * private String text; 217 * 218 * public void test() {} // violation, method is missing javadoc 219 * public String getText() { return text; } // OK 220 * public void setText(String text) { this.text = text; } // OK 221 * } 222 * </pre> 223 * 224 * <p> 225 * To configure the check with annotations that allow missed documentation: 226 * </p> 227 * <pre> 228 * <module name="MissingJavadocMethod"> 229 * <property name="allowedAnnotations" value="Override,Deprecated"/> 230 * </module> 231 * </pre> 232 * <p>Example:</p> 233 * <pre> 234 * public class Test { 235 * public void test() {} // violation, method is missing javadoc 236 * @Override 237 * public void test1() {} // OK 238 * @Deprecated 239 * public void test2() {} // OK 240 * @SuppressWarnings 241 * public void test3() {} // violation, method is missing javadoc 242 * /** 243 * * Some description here. 244 * */ 245 * @SuppressWarnings 246 * public void test4() {} // OK 247 * } 248 * </pre> 249 * <p> 250 * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker} 251 * </p> 252 * <p> 253 * Violation Message Keys: 254 * </p> 255 * <ul> 256 * <li> 257 * {@code javadoc.missing} 258 * </li> 259 * </ul> 260 * 261 * @since 8.21 262 */ 263@FileStatefulCheck 264public class MissingJavadocMethodCheck extends AbstractCheck { 265 266 /** 267 * A key is pointing to the warning message text in "messages.properties" 268 * file. 269 */ 270 public static final String MSG_JAVADOC_MISSING = "javadoc.missing"; 271 272 /** Default value of minimal amount of lines in method to allow no documentation.*/ 273 private static final int DEFAULT_MIN_LINE_COUNT = -1; 274 275 /** Specify the visibility scope where Javadoc comments are checked. */ 276 private Scope scope = Scope.PUBLIC; 277 278 /** Specify the visibility scope where Javadoc comments are not checked. */ 279 private Scope excludeScope; 280 281 /** Control the minimal amount of lines in method to allow no documentation.*/ 282 private int minLineCount = DEFAULT_MIN_LINE_COUNT; 283 284 /** 285 * Control whether to allow missing Javadoc on accessor methods for 286 * properties (setters and getters). 287 */ 288 private boolean allowMissingPropertyJavadoc; 289 290 /** Ignore method whose names are matching specified regex. */ 291 private Pattern ignoreMethodNamesRegex; 292 293 /** Configure annotations that allow missed documentation. */ 294 private Set<String> allowedAnnotations = Set.of("Override"); 295 296 /** 297 * Setter to configure annotations that allow missed documentation. 298 * 299 * @param userAnnotations user's value. 300 */ 301 public void setAllowedAnnotations(String... userAnnotations) { 302 allowedAnnotations = Set.of(userAnnotations); 303 } 304 305 /** 306 * Setter to ignore method whose names are matching specified regex. 307 * 308 * @param pattern a pattern. 309 */ 310 public void setIgnoreMethodNamesRegex(Pattern pattern) { 311 ignoreMethodNamesRegex = pattern; 312 } 313 314 /** 315 * Setter to control the minimal amount of lines in method to allow no documentation. 316 * 317 * @param value user's value. 318 */ 319 public void setMinLineCount(int value) { 320 minLineCount = value; 321 } 322 323 /** 324 * Setter to control whether to allow missing Javadoc on accessor methods for properties 325 * (setters and getters). 326 * 327 * @param flag a {@code Boolean} value 328 */ 329 public void setAllowMissingPropertyJavadoc(final boolean flag) { 330 allowMissingPropertyJavadoc = flag; 331 } 332 333 /** 334 * Setter to specify the visibility scope where Javadoc comments are checked. 335 * 336 * @param scope a scope. 337 */ 338 public void setScope(Scope scope) { 339 this.scope = scope; 340 } 341 342 /** 343 * Setter to specify the visibility scope where Javadoc comments are not checked. 344 * 345 * @param excludeScope a scope. 346 */ 347 public void setExcludeScope(Scope excludeScope) { 348 this.excludeScope = excludeScope; 349 } 350 351 @Override 352 public final int[] getRequiredTokens() { 353 return CommonUtil.EMPTY_INT_ARRAY; 354 } 355 356 @Override 357 public int[] getDefaultTokens() { 358 return getAcceptableTokens(); 359 } 360 361 @Override 362 public int[] getAcceptableTokens() { 363 return new int[] { 364 TokenTypes.METHOD_DEF, 365 TokenTypes.CTOR_DEF, 366 TokenTypes.ANNOTATION_FIELD_DEF, 367 TokenTypes.COMPACT_CTOR_DEF, 368 }; 369 } 370 371 // suppress deprecation until https://github.com/checkstyle/checkstyle/issues/11166 372 @SuppressWarnings("deprecation") 373 @Override 374 public final void visitToken(DetailAST ast) { 375 final Scope theScope = ScopeUtil.getScope(ast); 376 if (shouldCheck(ast, theScope)) { 377 final FileContents contents = getFileContents(); 378 final TextBlock textBlock = contents.getJavadocBefore(ast.getLineNo()); 379 380 if (textBlock == null && !isMissingJavadocAllowed(ast)) { 381 log(ast, MSG_JAVADOC_MISSING); 382 } 383 } 384 } 385 386 /** 387 * Some javadoc. 388 * 389 * @param methodDef Some javadoc. 390 * @return Some javadoc. 391 */ 392 private static int getMethodsNumberOfLine(DetailAST methodDef) { 393 final int numberOfLines; 394 final DetailAST lcurly = methodDef.getLastChild(); 395 final DetailAST rcurly = lcurly.getLastChild(); 396 397 if (lcurly.getFirstChild() == rcurly) { 398 numberOfLines = 1; 399 } 400 else { 401 numberOfLines = rcurly.getLineNo() - lcurly.getLineNo() - 1; 402 } 403 return numberOfLines; 404 } 405 406 /** 407 * Checks if a missing Javadoc is allowed by the check's configuration. 408 * 409 * @param ast the tree node for the method or constructor. 410 * @return True if this method or constructor doesn't need Javadoc. 411 */ 412 private boolean isMissingJavadocAllowed(final DetailAST ast) { 413 return allowMissingPropertyJavadoc 414 && (CheckUtil.isSetterMethod(ast) || CheckUtil.isGetterMethod(ast)) 415 || matchesSkipRegex(ast) 416 || isContentsAllowMissingJavadoc(ast); 417 } 418 419 /** 420 * Checks if the Javadoc can be missing if the method or constructor is 421 * below the minimum line count or has a special annotation. 422 * 423 * @param ast the tree node for the method or constructor. 424 * @return True if this method or constructor doesn't need Javadoc. 425 */ 426 private boolean isContentsAllowMissingJavadoc(DetailAST ast) { 427 return (ast.getType() == TokenTypes.METHOD_DEF 428 || ast.getType() == TokenTypes.CTOR_DEF 429 || ast.getType() == TokenTypes.COMPACT_CTOR_DEF) 430 && (getMethodsNumberOfLine(ast) <= minLineCount 431 || AnnotationUtil.containsAnnotation(ast, allowedAnnotations)); 432 } 433 434 /** 435 * Checks if the given method name matches the regex. In that case 436 * we skip enforcement of javadoc for this method 437 * 438 * @param methodDef {@link TokenTypes#METHOD_DEF METHOD_DEF} 439 * @return true if given method name matches the regex. 440 */ 441 private boolean matchesSkipRegex(DetailAST methodDef) { 442 boolean result = false; 443 if (ignoreMethodNamesRegex != null) { 444 final DetailAST ident = methodDef.findFirstToken(TokenTypes.IDENT); 445 final String methodName = ident.getText(); 446 447 final Matcher matcher = ignoreMethodNamesRegex.matcher(methodName); 448 if (matcher.matches()) { 449 result = true; 450 } 451 } 452 return result; 453 } 454 455 /** 456 * Whether we should check this node. 457 * 458 * @param ast a given node. 459 * @param nodeScope the scope of the node. 460 * @return whether we should check a given node. 461 */ 462 private boolean shouldCheck(final DetailAST ast, final Scope nodeScope) { 463 final Scope surroundingScope = ScopeUtil.getSurroundingScope(ast); 464 465 return (excludeScope == null 466 || nodeScope != excludeScope 467 && surroundingScope != excludeScope) 468 && nodeScope.isIn(scope) 469 && surroundingScope.isIn(scope); 470 } 471 472}