1   ////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code for adherence to a set of rules.
3   // Copyright (C) 2001-2019 the original author or authors.
4   //
5   // This library is free software; you can redistribute it and/or
6   // modify it under the terms of the GNU Lesser General Public
7   // License as published by the Free Software Foundation; either
8   // version 2.1 of the License, or (at your option) any later version.
9   //
10  // This library is distributed in the hope that it will be useful,
11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  // Lesser General Public License for more details.
14  //
15  // You should have received a copy of the GNU Lesser General Public
16  // License along with this library; if not, write to the Free Software
17  // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  ////////////////////////////////////////////////////////////////////////////////
19  
20  package com.puppycrawl.tools.checkstyle.checks.javadoc;
21  
22  import java.util.Arrays;
23  import java.util.Collections;
24  import java.util.List;
25  
26  import com.puppycrawl.tools.checkstyle.StatelessCheck;
27  import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
28  import com.puppycrawl.tools.checkstyle.api.DetailAST;
29  import com.puppycrawl.tools.checkstyle.api.FileContents;
30  import com.puppycrawl.tools.checkstyle.api.Scope;
31  import com.puppycrawl.tools.checkstyle.api.TextBlock;
32  import com.puppycrawl.tools.checkstyle.api.TokenTypes;
33  import com.puppycrawl.tools.checkstyle.utils.AnnotationUtil;
34  import com.puppycrawl.tools.checkstyle.utils.CommonUtil;
35  import com.puppycrawl.tools.checkstyle.utils.ScopeUtil;
36  
37  /**
38   * <p>
39   * Checks for missing Javadoc comments for class, enum, interface, and annotation interface
40   * definitions. The scope to verify is specified using the {@code Scope} class and defaults
41   * to {@code Scope.PUBLIC}. To verify another scope, set property scope to one of the
42   * {@code Scope} constants.
43   * </p>
44   * <ul>
45   * <li>
46   * Property {@code scope} - specify the visibility scope where Javadoc comments are checked.
47   * Default value is {@code public}.
48   * </li>
49   * <li>
50   * Property {@code excludeScope} - specify the visibility scope where Javadoc comments are not
51   * checked. Default value is {@code null}.
52   * </li>
53   * <li>
54   * Property {@code skipAnnotations} - specify the list of annotations that allow missed
55   * documentation. Only short names are allowed, e.g. {@code Generated}. Default value is
56   * {@code Generated}.
57   * </li>
58   * <li>
59   * Property {@code tokens} - tokens to check Default value is:
60   * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#INTERFACE_DEF">INTERFACE_DEF</a>,
61   * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#CLASS_DEF">CLASS_DEF</a>,
62   * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#ENUM_DEF">ENUM_DEF</a>,
63   * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#ANNOTATION_DEF">ANNOTATION_DEF</a>.
64   * </li>
65   * </ul>
66   * <p>
67   * To configure the default check to make sure all public class, enum, interface, and annotation
68   * interface, definitions have javadocs:
69   * </p>
70   * <pre>
71   * &lt;module name="MissingJavadocType"/&gt;
72   * </pre>
73   * <p>
74   * Example:
75   * </p>
76   * <pre>
77   * public class PublicClass {} // violation
78   * private class PublicClass {}
79   * protected class PublicClass {}
80   * class PackagePrivateClass {}
81   * </pre>
82   * <p>
83   * To configure the check for {@code private} scope:
84   * </p>
85   * <pre>
86   * &lt;module name="MissingJavadocType"&gt;
87   *   &lt;property name="scope" value="private"/&gt;
88   * &lt;/module&gt;
89   * </pre>
90   * <p>
91   * Example:
92   * </p>
93   * <pre>
94   * public class PublicClass {} // violation
95   * private class PublicClass {} // violation
96   * protected class PublicClass {} // violation
97   * class PackagePrivateClass {} // violation
98   * </pre>
99   * <p>
100  * To configure the check for {@code private} classes only:
101  * </p>
102  * <pre>
103  * &lt;module name="MissingJavadocType"&gt;
104  *   &lt;property name="scope" value="private"/&gt;
105  *   &lt;property name="excludeScope" value="package"/&gt;
106  * &lt;/module&gt;
107  * </pre>
108  * <p>
109  * Example:
110  * </p>
111  * <pre>
112  * public class PublicClass {}
113  * private class PublicClass {} // violation
114  * protected class PublicClass {}
115  * class PackagePrivateClass {}
116  * </pre>
117  * <p>
118  * Example that allows missing comments for classes annotated with {@code @SpringBootApplication}
119  * and {@code @Configuration}:
120  * </p>
121  * <pre>
122  * &#64;SpringBootApplication // no violations about missing comment on class
123  * public class Application {}
124  *
125  * &#64;Configuration // no violations about missing comment on class
126  * class DatabaseConfiguration {}
127  * </pre>
128  * <p>
129  * Use following configuration:
130  * </p>
131  * <pre>
132  * &lt;module name="MissingJavadocType"&gt;
133  *   &lt;property name="skipAnnotations" value="SpringBootApplication,Configuration"/&gt;
134  * &lt;/module&gt;
135  * </pre>
136  * @since 8.20
137  */
138 @StatelessCheck
139 public class MissingJavadocTypeCheck extends AbstractCheck {
140 
141     /**
142      * A key is pointing to the warning message text in "messages.properties"
143      * file.
144      */
145     public static final String MSG_JAVADOC_MISSING = "javadoc.missing";
146 
147     /** Specify the visibility scope where Javadoc comments are checked. */
148     private Scope scope = Scope.PUBLIC;
149     /** Specify the visibility scope where Javadoc comments are not checked. */
150     private Scope excludeScope;
151 
152     /**
153      * Specify the list of annotations that allow missed documentation.
154      * Only short names are allowed, e.g. {@code Generated}.
155      */
156     private List<String> skipAnnotations = Collections.singletonList("Generated");
157 
158     /**
159      * Setter to specify the visibility scope where Javadoc comments are checked.
160      * @param scope a scope.
161      */
162     public void setScope(Scope scope) {
163         this.scope = scope;
164     }
165 
166     /**
167      * Setter to specify the visibility scope where Javadoc comments are not checked.
168      * @param excludeScope a scope.
169      */
170     public void setExcludeScope(Scope excludeScope) {
171         this.excludeScope = excludeScope;
172     }
173 
174     /**
175      * Setter to specify the list of annotations that allow missed documentation.
176      * Only short names are allowed, e.g. {@code Generated}.
177      * @param userAnnotations user's value.
178      */
179     public void setSkipAnnotations(String... userAnnotations) {
180         skipAnnotations = Arrays.asList(userAnnotations);
181     }
182 
183     @Override
184     public int[] getDefaultTokens() {
185         return getAcceptableTokens();
186     }
187 
188     @Override
189     public int[] getAcceptableTokens() {
190         return new int[] {
191             TokenTypes.INTERFACE_DEF,
192             TokenTypes.CLASS_DEF,
193             TokenTypes.ENUM_DEF,
194             TokenTypes.ANNOTATION_DEF,
195         };
196     }
197 
198     @Override
199     public int[] getRequiredTokens() {
200         return CommonUtil.EMPTY_INT_ARRAY;
201     }
202 
203     @Override
204     public void visitToken(DetailAST ast) {
205         if (shouldCheck(ast)) {
206             final FileContents contents = getFileContents();
207             final int lineNo = ast.getLineNo();
208             final TextBlock textBlock = contents.getJavadocBefore(lineNo);
209             if (textBlock == null) {
210                 log(lineNo, MSG_JAVADOC_MISSING);
211             }
212         }
213     }
214 
215     /**
216      * Whether we should check this node.
217      * @param ast a given node.
218      * @return whether we should check a given node.
219      */
220     private boolean shouldCheck(final DetailAST ast) {
221         final Scope customScope;
222 
223         if (ScopeUtil.isInInterfaceOrAnnotationBlock(ast)) {
224             customScope = Scope.PUBLIC;
225         }
226         else {
227             final DetailAST mods = ast.findFirstToken(TokenTypes.MODIFIERS);
228             customScope = ScopeUtil.getScopeFromMods(mods);
229         }
230         final Scope surroundingScope = ScopeUtil.getSurroundingScope(ast);
231 
232         return customScope.isIn(scope)
233             && (surroundingScope == null || surroundingScope.isIn(scope))
234             && (excludeScope == null
235                 || !customScope.isIn(excludeScope)
236                 || surroundingScope != null
237                 && !surroundingScope.isIn(excludeScope))
238             && !AnnotationUtil.containsAnnotation(ast, skipAnnotations);
239     }
240 
241 }