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