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