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