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