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 * To configure the check for {@code private} scope: 116 * </p> 117 * <pre> 118 * <module name="MissingJavadocMethod"> 119 * <property name="scope" value="private"/> 120 * </module> 121 * </pre> 122 * <p> 123 * To configure the check for methods which are in {@code private}, but not in {@code protected} 124 * scope: 125 * </p> 126 * <pre> 127 * <module name="MissingJavadocMethod"> 128 * <property name="scope" value="private"/> 129 * <property name="excludeScope" value="protected"/> 130 * </module> 131 * </pre> 132 * <p> 133 * To configure the check for ignoring methods named {@code foo(),foo1(),foo2()}, etc.: 134 * </p> 135 * <pre> 136 * <module name="MissingJavadocMethod"> 137 * <property name="ignoreMethodNamesRegex" value="^foo.*$"/> 138 * </module> 139 * </pre> 140 * @since 8.21 141 */ 142@FileStatefulCheck 143public class MissingJavadocMethodCheck extends AbstractCheck { 144 145 /** 146 * A key is pointing to the warning message text in "messages.properties" 147 * file. 148 */ 149 public static final String MSG_JAVADOC_MISSING = "javadoc.missing"; 150 151 /** Default value of minimal amount of lines in method to allow no documentation.*/ 152 private static final int DEFAULT_MIN_LINE_COUNT = -1; 153 154 /** Specify the visibility scope where Javadoc comments are checked. */ 155 private Scope scope = Scope.PUBLIC; 156 157 /** Specify the visibility scope where Javadoc comments are not checked. */ 158 private Scope excludeScope; 159 160 /** Control the minimal amount of lines in method to allow no documentation.*/ 161 private int minLineCount = DEFAULT_MIN_LINE_COUNT; 162 163 /** 164 * Control whether to allow missing Javadoc on accessor methods for 165 * properties (setters and getters). 166 */ 167 private boolean allowMissingPropertyJavadoc; 168 169 /** Ignore method whose names are matching specified regex. */ 170 private Pattern ignoreMethodNamesRegex; 171 172 /** Configure the list of annotations that allow missed documentation. */ 173 private List<String> allowedAnnotations = Collections.singletonList("Override"); 174 175 /** 176 * Setter to configure the list of annotations that allow missed documentation. 177 * @param userAnnotations user's value. 178 */ 179 public void setAllowedAnnotations(String... userAnnotations) { 180 allowedAnnotations = Arrays.asList(userAnnotations); 181 } 182 183 /** 184 * Setter to ignore method whose names are matching specified regex. 185 * @param pattern a pattern. 186 */ 187 public void setIgnoreMethodNamesRegex(Pattern pattern) { 188 ignoreMethodNamesRegex = pattern; 189 } 190 191 /** 192 * Setter to control the minimal amount of lines in method to allow no documentation. 193 * @param value user's value. 194 */ 195 public void setMinLineCount(int value) { 196 minLineCount = value; 197 } 198 199 /** 200 * Setter to control whether to allow missing Javadoc on accessor methods for properties 201 * (setters and getters). 202 * 203 * @param flag a {@code Boolean} value 204 */ 205 public void setAllowMissingPropertyJavadoc(final boolean flag) { 206 allowMissingPropertyJavadoc = flag; 207 } 208 209 /** 210 * Setter to specify the visibility scope where Javadoc comments are checked. 211 * 212 * @param scope a scope. 213 */ 214 public void setScope(Scope scope) { 215 this.scope = scope; 216 } 217 218 /** 219 * Setter to specify the visibility scope where Javadoc comments are not checked. 220 * 221 * @param excludeScope a scope. 222 */ 223 public void setExcludeScope(Scope excludeScope) { 224 this.excludeScope = excludeScope; 225 } 226 227 @Override 228 public final int[] getRequiredTokens() { 229 return CommonUtil.EMPTY_INT_ARRAY; 230 } 231 232 @Override 233 public int[] getDefaultTokens() { 234 return getAcceptableTokens(); 235 } 236 237 @Override 238 public int[] getAcceptableTokens() { 239 return new int[] { 240 TokenTypes.METHOD_DEF, 241 TokenTypes.CTOR_DEF, 242 TokenTypes.ANNOTATION_FIELD_DEF, 243 }; 244 } 245 246 @Override 247 public final void visitToken(DetailAST ast) { 248 final Scope theScope = calculateScope(ast); 249 if (shouldCheck(ast, theScope)) { 250 final FileContents contents = getFileContents(); 251 final TextBlock textBlock = contents.getJavadocBefore(ast.getLineNo()); 252 253 if (textBlock == null && !isMissingJavadocAllowed(ast)) { 254 log(ast, MSG_JAVADOC_MISSING); 255 } 256 } 257 } 258 259 /** 260 * Some javadoc. 261 * @param methodDef Some javadoc. 262 * @return Some javadoc. 263 */ 264 private static int getMethodsNumberOfLine(DetailAST methodDef) { 265 final int numberOfLines; 266 final DetailAST lcurly = methodDef.getLastChild(); 267 final DetailAST rcurly = lcurly.getLastChild(); 268 269 if (lcurly.getFirstChild() == rcurly) { 270 numberOfLines = 1; 271 } 272 else { 273 numberOfLines = rcurly.getLineNo() - lcurly.getLineNo() - 1; 274 } 275 return numberOfLines; 276 } 277 278 /** 279 * Checks if a missing Javadoc is allowed by the check's configuration. 280 * @param ast the tree node for the method or constructor. 281 * @return True if this method or constructor doesn't need Javadoc. 282 */ 283 private boolean isMissingJavadocAllowed(final DetailAST ast) { 284 return allowMissingPropertyJavadoc 285 && (CheckUtil.isSetterMethod(ast) || CheckUtil.isGetterMethod(ast)) 286 || matchesSkipRegex(ast) 287 || isContentsAllowMissingJavadoc(ast); 288 } 289 290 /** 291 * Checks if the Javadoc can be missing if the method or constructor is 292 * below the minimum line count or has a special annotation. 293 * 294 * @param ast the tree node for the method or constructor. 295 * @return True if this method or constructor doesn't need Javadoc. 296 */ 297 private boolean isContentsAllowMissingJavadoc(DetailAST ast) { 298 return (ast.getType() == TokenTypes.METHOD_DEF || ast.getType() == TokenTypes.CTOR_DEF) 299 && (getMethodsNumberOfLine(ast) <= minLineCount 300 || AnnotationUtil.containsAnnotation(ast, allowedAnnotations)); 301 } 302 303 /** 304 * Checks if the given method name matches the regex. In that case 305 * we skip enforcement of javadoc for this method 306 * @param methodDef {@link TokenTypes#METHOD_DEF METHOD_DEF} 307 * @return true if given method name matches the regex. 308 */ 309 private boolean matchesSkipRegex(DetailAST methodDef) { 310 boolean result = false; 311 if (ignoreMethodNamesRegex != null) { 312 final DetailAST ident = methodDef.findFirstToken(TokenTypes.IDENT); 313 final String methodName = ident.getText(); 314 315 final Matcher matcher = ignoreMethodNamesRegex.matcher(methodName); 316 if (matcher.matches()) { 317 result = true; 318 } 319 } 320 return result; 321 } 322 323 /** 324 * Whether we should check this node. 325 * 326 * @param ast a given node. 327 * @param nodeScope the scope of the node. 328 * @return whether we should check a given node. 329 */ 330 private boolean shouldCheck(final DetailAST ast, final Scope nodeScope) { 331 final Scope surroundingScope = ScopeUtil.getSurroundingScope(ast); 332 333 return (excludeScope == null 334 || nodeScope != excludeScope 335 && surroundingScope != excludeScope) 336 && nodeScope.isIn(scope) 337 && surroundingScope.isIn(scope); 338 } 339 340 /** 341 * Returns the scope for the method/constructor at the specified AST. If 342 * the method is in an interface or annotation block, the scope is assumed 343 * to be public. 344 * 345 * @param ast the token of the method/constructor 346 * @return the scope of the method/constructor 347 */ 348 private static Scope calculateScope(final DetailAST ast) { 349 final Scope scope; 350 351 if (ScopeUtil.isInInterfaceOrAnnotationBlock(ast)) { 352 scope = Scope.PUBLIC; 353 } 354 else { 355 final DetailAST mods = ast.findFirstToken(TokenTypes.MODIFIERS); 356 scope = ScopeUtil.getScopeFromMods(mods); 357 } 358 return scope; 359 } 360 361}