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 * &lt;module name="MissingJavadocMethod"/&gt;
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 *   &#47;**
135 *    * Some description here.
136 *    *&#47;
137 *   public void test2() {} // OK
138 *
139 *   &#64;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 * &lt;module name="MissingJavadocMethod"&gt;
155 *   &lt;property name="scope" value="private"/&gt;
156 * &lt;/module&gt;
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 * &lt;module name="MissingJavadocMethod"&gt;
171 *   &lt;property name="scope" value="private"/&gt;
172 *   &lt;property name="excludeScope" value="protected"/&gt;
173 * &lt;/module&gt;
174 * </pre>
175 * <p>Example:</p>
176 * <pre>
177 * public class Test {
178 *   private void test1() {} // violation, the private method is missing javadoc
179 *   &#47;**
180 *    * Some description here
181 *    *&#47;
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 * &lt;module name="MissingJavadocMethod"&gt;
192 *   &lt;property name="ignoreMethodNamesRegex" value="^foo.*$"/&gt;
193 * &lt;/module&gt;
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 * &lt;module name="MissingJavadocMethod"&gt;
209 *   &lt;property name="allowMissingPropertyJavadoc" value="true"/&gt;
210 * &lt;/module&gt;
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 * &lt;module name="MissingJavadocMethod"&gt;
228 *   &lt;property name="allowedAnnotations" value="Override,Deprecated"/&gt;
229 * &lt;/module&gt;
230 * </pre>
231 * <p>Example:</p>
232 * <pre>
233 * public class Test {
234 *   public void test() {} // violation, method is missing javadoc
235 *   &#64;Override
236 *   public void test1() {} // OK
237 *   &#64;Deprecated
238 *   public void test2() {} // OK
239 *   &#64;SuppressWarnings
240 *   public void test3() {} // violation, method is missing javadoc
241 *   &#47;**
242 *    * Some description here.
243 *    *&#47;
244 *   &#64;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}