001////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code for adherence to a set of rules.
003// Copyright (C) 2001-2021 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 * &lt;module name="MissingJavadocMethod"/&gt;
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 *   &#47;**
138 *    * Some description here.
139 *    *&#47;
140 *   public void test2() {} // OK
141 *
142 *   &#64;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 * &lt;module name="MissingJavadocMethod"&gt;
158 *   &lt;property name="scope" value="private"/&gt;
159 * &lt;/module&gt;
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 * &lt;module name="MissingJavadocMethod"&gt;
174 *   &lt;property name="scope" value="private"/&gt;
175 *   &lt;property name="excludeScope" value="protected"/&gt;
176 * &lt;/module&gt;
177 * </pre>
178 * <p>Example:</p>
179 * <pre>
180 * public class Test {
181 *   private void test1() {} // violation, the private method is missing javadoc
182 *   &#47;**
183 *    * Some description here
184 *    *&#47;
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 * &lt;module name="MissingJavadocMethod"&gt;
195 *   &lt;property name="ignoreMethodNamesRegex" value="^foo.*$"/&gt;
196 * &lt;/module&gt;
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 * &lt;module name="MissingJavadocMethod"&gt;
212 *   &lt;property name="allowMissingPropertyJavadoc" value="true"/&gt;
213 * &lt;/module&gt;
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 * &lt;module name="MissingJavadocMethod"&gt;
231 *   &lt;property name="allowedAnnotations" value="Override,Deprecated"/&gt;
232 * &lt;/module&gt;
233 * </pre>
234 * <p>Example:</p>
235 * <pre>
236 * public class Test {
237 *   public void test() {} // violation, method is missing javadoc
238 *   &#64;Override
239 *   public void test1() {} // OK
240 *   &#64;Deprecated
241 *   public void test2() {} // OK
242 *   &#64;SuppressWarnings
243 *   public void test3() {} // violation, method is missing javadoc
244 *   &#47;**
245 *    * Some description here.
246 *    *&#47;
247 *   &#64;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    @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}