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, or where the nesting types accessibility is more restrictive and hides the 48 * nested field. Only fields matching the specified modifiers will be analyzed. 49 * Type is {@code com.puppycrawl.tools.checkstyle.checks.naming.AccessModifierOption[]}. 50 * Default value is {@code public, protected, package, private}. 51 * </li> 52 * <li> 53 * Property {@code ignoreNamePattern} - Specify the regexp to define variable names to ignore. 54 * Type is {@code java.util.regex.Pattern}. 55 * Default value is {@code null}. 56 * </li> 57 * <li> 58 * Property {@code tokens} - tokens to check 59 * Type is {@code java.lang.String[]}. 60 * Validation type is {@code tokenSet}. 61 * Default value is: 62 * <a href="https://checkstyle.org/apidocs/com/puppycrawl/tools/checkstyle/api/TokenTypes.html#ENUM_CONSTANT_DEF"> 63 * ENUM_CONSTANT_DEF</a>. 64 * </li> 65 * </ul> 66 * 67 * <p> 68 * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker} 69 * </p> 70 * 71 * <p> 72 * Violation Message Keys: 73 * </p> 74 * <ul> 75 * <li> 76 * {@code javadoc.missing} 77 * </li> 78 * </ul> 79 * 80 * @since 3.0 81 */ 82 @StatelessCheck 83 public class JavadocVariableCheck 84 extends AbstractCheck { 85 86 /** 87 * A key is pointing to the warning message text in "messages.properties" 88 * file. 89 */ 90 91 public static final String MSG_JAVADOC_MISSING = "javadoc.missing"; 92 /** 93 * Specify the set of access modifiers used to determine which fields should be checked. 94 * This includes both explicitly declared modifiers and implicit ones, such as package-private 95 * for fields without an explicit modifier. It also accounts for special cases where fields 96 * have implicit modifiers, such as {@code public static final} for interface fields and 97 * {@code public static} for enum constants, or where the nesting types accessibility is more 98 * restrictive and hides the nested field. 99 * Only fields matching the specified modifiers will be analyzed. 100 */ 101 private AccessModifierOption[] accessModifiers = { 102 AccessModifierOption.PUBLIC, 103 AccessModifierOption.PROTECTED, 104 AccessModifierOption.PACKAGE, 105 AccessModifierOption.PRIVATE, 106 }; 107 108 /** Specify the regexp to define variable names to ignore. */ 109 private Pattern ignoreNamePattern; 110 111 /** 112 * Setter to specify the set of access modifiers used to determine which fields should be 113 * checked. This includes both explicitly declared modifiers and implicit ones, such as 114 * package-private for fields without an explicit modifier. It also accounts for special 115 * cases where fields have implicit modifiers, such as {@code public static final} 116 * for interface fields and {@code public static} for enum constants, or where the nesting 117 * types accessibility is more restrictive and hides the nested field. 118 * Only fields matching the specified modifiers will be analyzed. 119 * 120 * @param accessModifiers access modifiers of fields to check. 121 * @since 10.22.0 122 */ 123 public void setAccessModifiers(AccessModifierOption... accessModifiers) { 124 this.accessModifiers = 125 UnmodifiableCollectionUtil.copyOfArray(accessModifiers, accessModifiers.length); 126 } 127 128 /** 129 * Setter to specify the regexp to define variable names to ignore. 130 * 131 * @param pattern a pattern. 132 * @since 5.8 133 */ 134 public void setIgnoreNamePattern(Pattern pattern) { 135 ignoreNamePattern = pattern; 136 } 137 138 @Override 139 public int[] getDefaultTokens() { 140 return getAcceptableTokens(); 141 } 142 143 @Override 144 public int[] getAcceptableTokens() { 145 return new int[] { 146 TokenTypes.VARIABLE_DEF, 147 TokenTypes.ENUM_CONSTANT_DEF, 148 }; 149 } 150 151 /* 152 * Skipping enum values is requested. 153 * Checkstyle's issue #1669: https://github.com/checkstyle/checkstyle/issues/1669 154 */ 155 @Override 156 public int[] getRequiredTokens() { 157 return new int[] { 158 TokenTypes.VARIABLE_DEF, 159 }; 160 } 161 162 // suppress deprecation until https://github.com/checkstyle/checkstyle/issues/11166 163 @SuppressWarnings("deprecation") 164 @Override 165 public void visitToken(DetailAST ast) { 166 if (shouldCheck(ast)) { 167 final FileContents contents = getFileContents(); 168 final TextBlock textBlock = 169 contents.getJavadocBefore(ast.getLineNo()); 170 171 if (textBlock == null) { 172 log(ast, MSG_JAVADOC_MISSING); 173 } 174 } 175 } 176 177 /** 178 * Decides whether the variable name of an AST is in the ignore list. 179 * 180 * @param ast the AST to check 181 * @return true if the variable name of ast is in the ignore list. 182 */ 183 private boolean isIgnored(DetailAST ast) { 184 final String name = ast.findFirstToken(TokenTypes.IDENT).getText(); 185 return ignoreNamePattern != null && ignoreNamePattern.matcher(name).matches() 186 || "serialVersionUID".equals(name); 187 } 188 189 /** 190 * Checks whether a method has the correct access modifier to be checked. 191 * 192 * @param accessModifier the access modifier of the method. 193 * @return whether the method matches the expected access modifier. 194 */ 195 private boolean matchAccessModifiers(AccessModifierOption accessModifier) { 196 return Arrays.stream(accessModifiers) 197 .anyMatch(modifier -> modifier == accessModifier); 198 } 199 200 /** 201 * Whether we should check this node. 202 * 203 * @param ast a given node. 204 * @return whether we should check a given node. 205 */ 206 private boolean shouldCheck(final DetailAST ast) { 207 boolean result = false; 208 if (!ScopeUtil.isInCodeBlock(ast) && !isIgnored(ast)) { 209 final AccessModifierOption accessModifier = 210 getAccessModifierFromModifiersTokenWithPrivateEnumSupport(ast); 211 result = matchAccessModifiers(accessModifier); 212 } 213 return result; 214 } 215 216 /** 217 * A derivative of {@link CheckUtil#getAccessModifierFromModifiersToken(DetailAST)} that 218 * considers enum definitions' visibility when evaluating the accessibility of an enum 219 * constant. 220 * <br> 221 * <a href="https://github.com/checkstyle/checkstyle/pull/16787/files#r2073671898">Implemented 222 * separately</a> to reduce scope of fix for 223 * <a href="https://github.com/checkstyle/checkstyle/issues/16786">issue #16786</a> until a 224 * wider solution can be developed. 225 * 226 * @param ast the token of the method/constructor. 227 * @return the access modifier of the method/constructor. 228 */ 229 public static AccessModifierOption getAccessModifierFromModifiersTokenWithPrivateEnumSupport( 230 DetailAST ast) { 231 // In some scenarios we want to investigate a parent AST instead 232 DetailAST selectedAst = ast; 233 234 if (selectedAst.getType() == TokenTypes.ENUM_CONSTANT_DEF) { 235 // Enum constants don't have modifiers 236 // implicitly public but validate against parent(s) 237 while (selectedAst.getType() != TokenTypes.ENUM_DEF) { 238 selectedAst = selectedAst.getParent(); 239 } 240 } 241 242 return CheckUtil.getAccessModifierFromModifiersToken(selectedAst); 243 } 244 }