View Javadoc
1   ///////////////////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code and other text files for adherence to a set of rules.
3   // Copyright (C) 2001-2025 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.regex.Pattern;
24  
25  import com.puppycrawl.tools.checkstyle.StatelessCheck;
26  import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
27  import com.puppycrawl.tools.checkstyle.api.DetailAST;
28  import com.puppycrawl.tools.checkstyle.api.FileContents;
29  import com.puppycrawl.tools.checkstyle.api.TextBlock;
30  import com.puppycrawl.tools.checkstyle.api.TokenTypes;
31  import com.puppycrawl.tools.checkstyle.checks.naming.AccessModifierOption;
32  import com.puppycrawl.tools.checkstyle.utils.CheckUtil;
33  import com.puppycrawl.tools.checkstyle.utils.ScopeUtil;
34  import com.puppycrawl.tools.checkstyle.utils.UnmodifiableCollectionUtil;
35  
36  /**
37   * <div>
38   * Checks that a variable has a Javadoc comment. Ignores {@code serialVersionUID} fields.
39   * </div>
40   * <ul>
41   * <li>
42   * Property {@code accessModifiers} - Specify the set of access modifiers used to determine which
43   * fields should be checked. This includes both explicitly declared modifiers and implicit ones,
44   * such as package-private for fields without an explicit modifier.
45   * It also accounts for special cases where fields have implicit modifiers,
46   * such as {@code public static final} for interface fields and {@code public static}
47   * for enum constants. Only fields matching the specified modifiers will be analyzed.
48   * Type is {@code com.puppycrawl.tools.checkstyle.checks.naming.AccessModifierOption[]}.
49   * Default value is {@code public, protected, package, private}.
50   * </li>
51   * <li>
52   * Property {@code ignoreNamePattern} - Specify the regexp to define variable names to ignore.
53   * Type is {@code java.util.regex.Pattern}.
54   * Default value is {@code null}.
55   * </li>
56   * <li>
57   * Property {@code tokens} - tokens to check
58   * Type is {@code java.lang.String[]}.
59   * Validation type is {@code tokenSet}.
60   * Default value is:
61   * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#ENUM_CONSTANT_DEF">
62   * ENUM_CONSTANT_DEF</a>.
63   * </li>
64   * </ul>
65   *
66   * <p>
67   * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker}
68   * </p>
69   *
70   * <p>
71   * Violation Message Keys:
72   * </p>
73   * <ul>
74   * <li>
75   * {@code javadoc.missing}
76   * </li>
77   * </ul>
78   *
79   * @since 3.0
80   */
81  @StatelessCheck
82  public class JavadocVariableCheck
83      extends AbstractCheck {
84  
85      /**
86       * A key is pointing to the warning message text in "messages.properties"
87       * file.
88       */
89  
90      public static final String MSG_JAVADOC_MISSING = "javadoc.missing";
91      /**
92       * Specify the set of access modifiers used to determine which fields should be checked.
93       *  This includes both explicitly declared modifiers and implicit ones, such as package-private
94       *  for fields without an explicit modifier. It also accounts for special cases where fields
95       *  have implicit modifiers, such as {@code public static final} for interface fields and
96       *  {@code public static} for enum constants.
97       *  Only fields matching the specified modifiers will be analyzed.
98       */
99      private AccessModifierOption[] accessModifiers = {
100         AccessModifierOption.PUBLIC,
101         AccessModifierOption.PROTECTED,
102         AccessModifierOption.PACKAGE,
103         AccessModifierOption.PRIVATE,
104     };
105 
106     /** Specify the regexp to define variable names to ignore. */
107     private Pattern ignoreNamePattern;
108 
109     /**
110      * Setter to specify the set of access modifiers used to determine which fields should be
111      * checked. This includes both explicitly declared modifiers and implicit ones, such as
112      * package-private for fields without an explicit modifier. It also accounts for special
113      * cases where fields have implicit modifiers, such as {@code public static final}
114      * for interface fields and {@code public static} for enum constants.
115      * Only fields matching the specified modifiers will be analyzed.
116      *
117      * @param accessModifiers access modifiers of fields to check.
118      * @since 10.22.0
119      */
120     public void setAccessModifiers(AccessModifierOption... accessModifiers) {
121         this.accessModifiers =
122             UnmodifiableCollectionUtil.copyOfArray(accessModifiers, accessModifiers.length);
123     }
124 
125     /**
126      * Setter to specify the regexp to define variable names to ignore.
127      *
128      * @param pattern a pattern.
129      * @since 5.8
130      */
131     public void setIgnoreNamePattern(Pattern pattern) {
132         ignoreNamePattern = pattern;
133     }
134 
135     @Override
136     public int[] getDefaultTokens() {
137         return getAcceptableTokens();
138     }
139 
140     @Override
141     public int[] getAcceptableTokens() {
142         return new int[] {
143             TokenTypes.VARIABLE_DEF,
144             TokenTypes.ENUM_CONSTANT_DEF,
145         };
146     }
147 
148     /*
149      * Skipping enum values is requested.
150      * Checkstyle's issue #1669: https://github.com/checkstyle/checkstyle/issues/1669
151      */
152     @Override
153     public int[] getRequiredTokens() {
154         return new int[] {
155             TokenTypes.VARIABLE_DEF,
156         };
157     }
158 
159     // suppress deprecation until https://github.com/checkstyle/checkstyle/issues/11166
160     @SuppressWarnings("deprecation")
161     @Override
162     public void visitToken(DetailAST ast) {
163         if (shouldCheck(ast)) {
164             final FileContents contents = getFileContents();
165             final TextBlock textBlock =
166                 contents.getJavadocBefore(ast.getLineNo());
167 
168             if (textBlock == null) {
169                 log(ast, MSG_JAVADOC_MISSING);
170             }
171         }
172     }
173 
174     /**
175      * Decides whether the variable name of an AST is in the ignore list.
176      *
177      * @param ast the AST to check
178      * @return true if the variable name of ast is in the ignore list.
179      */
180     private boolean isIgnored(DetailAST ast) {
181         final String name = ast.findFirstToken(TokenTypes.IDENT).getText();
182         return ignoreNamePattern != null && ignoreNamePattern.matcher(name).matches()
183             || "serialVersionUID".equals(name);
184     }
185 
186     /**
187      * Checks whether a method has the correct access modifier to be checked.
188      *
189      * @param accessModifier the access modifier of the method.
190      * @return whether the method matches the expected access modifier.
191      */
192     private boolean matchAccessModifiers(AccessModifierOption accessModifier) {
193         return Arrays.stream(accessModifiers)
194             .anyMatch(modifier -> modifier == accessModifier);
195     }
196 
197     /**
198      * Whether we should check this node.
199      *
200      * @param ast a given node.
201      * @return whether we should check a given node.
202      */
203     private boolean shouldCheck(final DetailAST ast) {
204         boolean result = false;
205         if (!ScopeUtil.isInCodeBlock(ast) && !isIgnored(ast)) {
206             final AccessModifierOption accessModifier =
207                     CheckUtil.getAccessModifierFromModifiersToken(ast);
208             result = matchAccessModifiers(accessModifier);
209         }
210         return result;
211     }
212 }