001///////////////////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code and other text files for adherence to a set of rules.
003// Copyright (C) 2001-2023 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.Set;
023
024import com.puppycrawl.tools.checkstyle.StatelessCheck;
025import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
026import com.puppycrawl.tools.checkstyle.api.DetailAST;
027import com.puppycrawl.tools.checkstyle.api.FileContents;
028import com.puppycrawl.tools.checkstyle.api.Scope;
029import com.puppycrawl.tools.checkstyle.api.TextBlock;
030import com.puppycrawl.tools.checkstyle.api.TokenTypes;
031import com.puppycrawl.tools.checkstyle.utils.AnnotationUtil;
032import com.puppycrawl.tools.checkstyle.utils.CommonUtil;
033import com.puppycrawl.tools.checkstyle.utils.ScopeUtil;
034
035/**
036 * <p>
037 * Checks for missing Javadoc comments for class, enum, interface, and annotation interface
038 * definitions. The scope to verify is specified using the {@code Scope} class and defaults
039 * to {@code Scope.PUBLIC}. To verify another scope, set property scope to one of the
040 * {@code Scope} constants.
041 * </p>
042 * <ul>
043 * <li>
044 * Property {@code scope} - specify the visibility scope where Javadoc comments are checked.
045 * Type is {@code com.puppycrawl.tools.checkstyle.api.Scope}.
046 * Default value is {@code public}.
047 * </li>
048 * <li>
049 * Property {@code excludeScope} - specify the visibility scope where Javadoc comments are not
050 * checked.
051 * Type is {@code com.puppycrawl.tools.checkstyle.api.Scope}.
052 * Default value is {@code null}.
053 * </li>
054 * <li>
055 * Property {@code skipAnnotations} - specify annotations that allow missed
056 * documentation. If annotation is present in target sources in multiple forms of qualified
057 * name, all forms should be listed in this property.
058 * Type is {@code java.lang.String[]}.
059 * Default value is {@code Generated}.
060 * </li>
061 * <li>
062 * Property {@code tokens} - tokens to check
063 * Type is {@code java.lang.String[]}.
064 * Validation type is {@code tokenSet}.
065 * Default value is:
066 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#INTERFACE_DEF">
067 * INTERFACE_DEF</a>,
068 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#CLASS_DEF">
069 * CLASS_DEF</a>,
070 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#ENUM_DEF">
071 * ENUM_DEF</a>,
072 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#ANNOTATION_DEF">
073 * ANNOTATION_DEF</a>,
074 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#RECORD_DEF">
075 * RECORD_DEF</a>.
076 * </li>
077 * </ul>
078 * <p>
079 * To configure the default check to make sure all public class, enum, interface, and annotation
080 * interface, definitions have javadocs:
081 * </p>
082 * <pre>
083 * &lt;module name="MissingJavadocType"/&gt;
084 * </pre>
085 * <p>
086 * Example:
087 * </p>
088 * <pre>
089 * public class PublicClass {} // violation
090 * private class PublicClass {}
091 * protected class PublicClass {}
092 * class PackagePrivateClass {}
093 * </pre>
094 * <p>
095 * To configure the check for {@code private} scope:
096 * </p>
097 * <pre>
098 * &lt;module name="MissingJavadocType"&gt;
099 *   &lt;property name="scope" value="private"/&gt;
100 * &lt;/module&gt;
101 * </pre>
102 * <p>
103 * Example:
104 * </p>
105 * <pre>
106 * public class PublicClass {} // violation
107 * private class PublicClass {} // violation
108 * protected class PublicClass {} // violation
109 * class PackagePrivateClass {} // violation
110 * </pre>
111 * <p>
112 * To configure the check for {@code private} classes only:
113 * </p>
114 * <pre>
115 * &lt;module name="MissingJavadocType"&gt;
116 *   &lt;property name="scope" value="private"/&gt;
117 *   &lt;property name="excludeScope" value="package"/&gt;
118 * &lt;/module&gt;
119 * </pre>
120 * <p>
121 * Example:
122 * </p>
123 * <pre>
124 * public class PublicClass {}
125 * private class PublicClass {} // violation
126 * protected class PublicClass {}
127 * class PackagePrivateClass {}
128 * </pre>
129 * <p>
130 * To configure a check that allows missing comments for classes annotated
131 * with {@code @SpringBootApplication} and {@code @Configuration}:
132 * </p>
133 * <pre>
134 * &lt;module name="MissingJavadocType"&gt;
135 *   &lt;property name="skipAnnotations" value="SpringBootApplication,Configuration"/&gt;
136 * &lt;/module&gt;
137 * </pre>
138 * <p>
139 * Example:
140 * </p>
141 * <pre>
142 * &#64;SpringBootApplication // no violations about missing comment on class
143 * public class Application {}
144 *
145 * &#64;Configuration // no violations about missing comment on class
146 * class DatabaseConfiguration {}
147 * </pre>
148 * <p>
149 * To configure a check that allows missing comments for classes annotated with {@code @Annotation}
150 * and {@code @MyClass.Annotation}:
151 * </p>
152 * <pre>
153 * &lt;module name="MissingJavadocType"&gt;
154 *   &lt;property name="skipAnnotations" value="Annotation,MyClass.Annotation"/&gt;
155 * &lt;/module&gt;
156 * </pre>
157 * <p>
158 * Example:
159 * </p>
160 * <pre>
161 * &#64;Annotation // ok
162 * class Class1 {}
163 *
164 * &#64;MyClass.Annotation // ok
165 * class Class2 {}
166 *
167 * &#64;com.mycompany.MyClass.Annotation // violation, as this form is missed in config
168 * class Class3 {}
169 * </pre>
170 * <p>
171 * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker}
172 * </p>
173 * <p>
174 * Violation Message Keys:
175 * </p>
176 * <ul>
177 * <li>
178 * {@code javadoc.missing}
179 * </li>
180 * </ul>
181 *
182 * @since 8.20
183 */
184@StatelessCheck
185public class MissingJavadocTypeCheck extends AbstractCheck {
186
187    /**
188     * A key is pointing to the warning message text in "messages.properties"
189     * file.
190     */
191    public static final String MSG_JAVADOC_MISSING = "javadoc.missing";
192
193    /** Specify the visibility scope where Javadoc comments are checked. */
194    private Scope scope = Scope.PUBLIC;
195    /** Specify the visibility scope where Javadoc comments are not checked. */
196    private Scope excludeScope;
197
198    /**
199     * Specify annotations that allow missed documentation.
200     * If annotation is present in target sources in multiple forms of qualified
201     * name, all forms should be listed in this property.
202     */
203    private Set<String> skipAnnotations = Set.of("Generated");
204
205    /**
206     * Setter to specify the visibility scope where Javadoc comments are checked.
207     *
208     * @param scope a scope.
209     */
210    public void setScope(Scope scope) {
211        this.scope = scope;
212    }
213
214    /**
215     * Setter to specify the visibility scope where Javadoc comments are not checked.
216     *
217     * @param excludeScope a scope.
218     */
219    public void setExcludeScope(Scope excludeScope) {
220        this.excludeScope = excludeScope;
221    }
222
223    /**
224     * Setter to specify annotations that allow missed documentation.
225     * If annotation is present in target sources in multiple forms of qualified
226     * name, all forms should be listed in this property.
227     *
228     * @param userAnnotations user's value.
229     */
230    public void setSkipAnnotations(String... userAnnotations) {
231        skipAnnotations = Set.of(userAnnotations);
232    }
233
234    @Override
235    public int[] getDefaultTokens() {
236        return getAcceptableTokens();
237    }
238
239    @Override
240    public int[] getAcceptableTokens() {
241        return new int[] {
242            TokenTypes.INTERFACE_DEF,
243            TokenTypes.CLASS_DEF,
244            TokenTypes.ENUM_DEF,
245            TokenTypes.ANNOTATION_DEF,
246            TokenTypes.RECORD_DEF,
247        };
248    }
249
250    @Override
251    public int[] getRequiredTokens() {
252        return CommonUtil.EMPTY_INT_ARRAY;
253    }
254
255    // suppress deprecation until https://github.com/checkstyle/checkstyle/issues/11166
256    @SuppressWarnings("deprecation")
257    @Override
258    public void visitToken(DetailAST ast) {
259        if (shouldCheck(ast)) {
260            final FileContents contents = getFileContents();
261            final int lineNo = ast.getLineNo();
262            final TextBlock textBlock = contents.getJavadocBefore(lineNo);
263            if (textBlock == null) {
264                log(ast, MSG_JAVADOC_MISSING);
265            }
266        }
267    }
268
269    /**
270     * Whether we should check this node.
271     *
272     * @param ast a given node.
273     * @return whether we should check a given node.
274     */
275    private boolean shouldCheck(final DetailAST ast) {
276        final Scope customScope = ScopeUtil.getScope(ast);
277        final Scope surroundingScope = ScopeUtil.getSurroundingScope(ast);
278
279        return customScope.isIn(scope)
280            && (surroundingScope == null || surroundingScope.isIn(scope))
281            && (excludeScope == null
282                || !customScope.isIn(excludeScope)
283                || surroundingScope != null
284                && !surroundingScope.isIn(excludeScope))
285            && !AnnotationUtil.containsAnnotation(ast, skipAnnotations);
286    }
287
288}