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 * &lt;module name="MissingJavadocMethod"/&gt;
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 *   &#47;**
122 *    * Some description here.
123 *    *&#47;
124 *   public void test2() {} // OK
125 *
126 *   &#64;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 * &lt;module name="MissingJavadocMethod"&gt;
142 *   &lt;property name="scope" value="private"/&gt;
143 * &lt;/module&gt;
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 * &lt;module name="MissingJavadocMethod"&gt;
158 *   &lt;property name="scope" value="private"/&gt;
159 *   &lt;property name="excludeScope" value="protected"/&gt;
160 * &lt;/module&gt;
161 * </pre>
162 * <p>Example:</p>
163 * <pre>
164 * public class Test {
165 *   private void test1() {} // violation, the private method is missing javadoc
166 *   &#47;**
167 *    * Some description here
168 *    *&#47;
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 * &lt;module name="MissingJavadocMethod"&gt;
179 *   &lt;property name="ignoreMethodNamesRegex" value="^foo.*$"/&gt;
180 * &lt;/module&gt;
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 * &lt;module name="MissingJavadocMethod"&gt;
196 *   &lt;property name="allowMissingPropertyJavadoc" value="true"/&gt;
197 * &lt;/module&gt;
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 * &lt;module name="MissingJavadocMethod"&gt;
215 *   &lt;property name="allowedAnnotations" value="Override,Deprecated"/&gt;
216 * &lt;/module&gt;
217 * </pre>
218 * <p>Example:</p>
219 * <pre>
220 * public class Test {
221 *   public void test() {} // violation, method is missing javadoc
222 *   &#64;Override
223 *   public void test1() {} // OK
224 *   &#64;Deprecated
225 *   public void test2() {} // OK
226 *   &#64;SuppressWarnings
227 *   public void test3() {} // violation, method is missing javadoc
228 *   &#47;**
229 *    * Some description here.
230 *    *&#47;
231 *   &#64;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}