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