001/////////////////////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code and other text files for adherence to a set of rules. 003// Copyright (C) 2001-2022 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.annotation; 021 022import java.util.BitSet; 023 024import com.puppycrawl.tools.checkstyle.StatelessCheck; 025import com.puppycrawl.tools.checkstyle.api.DetailAST; 026import com.puppycrawl.tools.checkstyle.api.DetailNode; 027import com.puppycrawl.tools.checkstyle.api.JavadocTokenTypes; 028import com.puppycrawl.tools.checkstyle.api.TokenTypes; 029import com.puppycrawl.tools.checkstyle.checks.javadoc.AbstractJavadocCheck; 030import com.puppycrawl.tools.checkstyle.checks.javadoc.JavadocTagInfo; 031import com.puppycrawl.tools.checkstyle.utils.AnnotationUtil; 032import com.puppycrawl.tools.checkstyle.utils.TokenUtil; 033 034/** 035 * <p> 036 * Verifies that the annotation {@code @Deprecated} and the Javadoc tag 037 * {@code @deprecated} are both present when either of them is present. 038 * </p> 039 * <p> 040 * Both ways of flagging deprecation serve their own purpose. 041 * The @Deprecated annotation is used for compilers and development tools. 042 * The @deprecated javadoc tag is used to document why something is deprecated 043 * and what, if any, alternatives exist. 044 * </p> 045 * <p> 046 * In order to properly mark something as deprecated both forms of 047 * deprecation should be present. 048 * </p> 049 * <p> 050 * Package deprecation is an exception to the rule of always using the 051 * javadoc tag and annotation to deprecate. It is not clear if the javadoc 052 * tool will support it or not as newer versions keep flip-flopping on if 053 * it is supported or will cause an error. See 054 * <a href="https://bugs.openjdk.org/browse/JDK-8160601">JDK-8160601</a>. 055 * The deprecated javadoc tag is currently the only way to say why the package 056 * is deprecated and what to use instead. Until this is resolved, if you don't 057 * want to print violations on package-info, you can use a 058 * <a href="https://checkstyle.org/config_filters.html">filter</a> to ignore 059 * these files until the javadoc tool faithfully supports it. An example config 060 * using SuppressionSingleFilter is: 061 * </p> 062 * <pre> 063 * <!-- required till https://bugs.openjdk.org/browse/JDK-8160601 --> 064 * <module name="SuppressionSingleFilter"> 065 * <property name="checks" value="MissingDeprecatedCheck"/> 066 * <property name="files" value="package-info\.java"/> 067 * </module> 068 * </pre> 069 * <ul> 070 * <li> 071 * Property {@code violateExecutionOnNonTightHtml} - Control when to 072 * print violations if the Javadoc being examined by this check violates the 073 * tight html rules defined at 074 * <a href="https://checkstyle.org/writingjavadocchecks.html#Tight-HTML_rules"> 075 * Tight-HTML Rules</a>. 076 * Type is {@code boolean}. 077 * Default value is {@code false}. 078 * </li> 079 * </ul> 080 * <p> 081 * To configure the check: 082 * </p> 083 * <pre> 084 * <module name="MissingDeprecated"/> 085 * </pre> 086 * <p> 087 * Example: 088 * </p> 089 * <pre> 090 * @Deprecated 091 * public static final int MY_CONST = 13; // ok 092 * 093 * /** This javadoc is missing deprecated tag. */ 094 * @Deprecated 095 * public static final int COUNTER = 10; // violation 096 * 097 * /** 098 * * @deprecated 099 * * <p></p> 100 * */ 101 * @Deprecated 102 * public static final int NUM = 123456; // ok 103 * 104 * /** 105 * * @deprecated 106 * * <p> 107 * */ 108 * @Deprecated 109 * public static final int CONST = 12; // ok 110 * </pre> 111 * <p> 112 * To configure the check such that it prints violation 113 * messages if tight HTML rules are not obeyed 114 * </p> 115 * <pre> 116 * <module name="MissingDeprecated"> 117 * <property name="violateExecutionOnNonTightHtml" value="true"/> 118 * </module> 119 * </pre> 120 * <p> 121 * Example: 122 * </p> 123 * <pre> 124 * @Deprecated 125 * public static final int MY_CONST = 13; // ok 126 * 127 * /** This javadoc is missing deprecated tag. */ 128 * @Deprecated 129 * public static final int COUNTER = 10; // violation 130 * 131 * /** 132 * * @deprecated 133 * * <p></p> 134 * */ 135 * @Deprecated 136 * public static final int NUM = 123456; // ok 137 * 138 * /** 139 * * @deprecated 140 * * <p> 141 * */ 142 * @Deprecated 143 * public static final int CONST = 12; // violation, tight HTML rules not obeyed 144 * </pre> 145 * <p> 146 * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker} 147 * </p> 148 * <p> 149 * Violation Message Keys: 150 * </p> 151 * <ul> 152 * <li> 153 * {@code annotation.missing.deprecated} 154 * </li> 155 * <li> 156 * {@code javadoc.duplicateTag} 157 * </li> 158 * <li> 159 * {@code javadoc.missed.html.close} 160 * </li> 161 * <li> 162 * {@code javadoc.parse.rule.error} 163 * </li> 164 * <li> 165 * {@code javadoc.wrong.singleton.html.tag} 166 * </li> 167 * </ul> 168 * 169 * @since 5.0 170 */ 171@StatelessCheck 172public final class MissingDeprecatedCheck extends AbstractJavadocCheck { 173 174 /** 175 * A key is pointing to the warning message text in "messages.properties" 176 * file. 177 */ 178 public static final String MSG_KEY_ANNOTATION_MISSING_DEPRECATED = 179 "annotation.missing.deprecated"; 180 181 /** 182 * A key is pointing to the warning message text in "messages.properties" 183 * file. 184 */ 185 public static final String MSG_KEY_JAVADOC_DUPLICATE_TAG = 186 "javadoc.duplicateTag"; 187 188 /** {@link Deprecated Deprecated} annotation name. */ 189 private static final String DEPRECATED = "Deprecated"; 190 191 /** Fully-qualified {@link Deprecated Deprecated} annotation name. */ 192 private static final String FQ_DEPRECATED = "java.lang." + DEPRECATED; 193 194 /** Token types to find parent of. */ 195 private static final BitSet TYPES_HASH_SET = TokenUtil.asBitSet( 196 TokenTypes.TYPE, TokenTypes.MODIFIERS, TokenTypes.ANNOTATION, 197 TokenTypes.ANNOTATIONS, TokenTypes.ARRAY_DECLARATOR, 198 TokenTypes.TYPE_PARAMETERS, TokenTypes.DOT); 199 200 @Override 201 public int[] getDefaultJavadocTokens() { 202 return getRequiredJavadocTokens(); 203 } 204 205 @Override 206 public int[] getRequiredJavadocTokens() { 207 return new int[] { 208 JavadocTokenTypes.JAVADOC, 209 }; 210 } 211 212 @Override 213 public void visitJavadocToken(DetailNode ast) { 214 final DetailAST parentAst = getParent(getBlockCommentAst()); 215 216 final boolean containsAnnotation = 217 AnnotationUtil.containsAnnotation(parentAst, DEPRECATED) 218 || AnnotationUtil.containsAnnotation(parentAst, FQ_DEPRECATED); 219 220 final boolean containsJavadocTag = containsDeprecatedTag(ast); 221 222 if (containsAnnotation ^ containsJavadocTag) { 223 log(parentAst.getLineNo(), MSG_KEY_ANNOTATION_MISSING_DEPRECATED); 224 } 225 } 226 227 /** 228 * Checks to see if the javadoc contains a deprecated tag. 229 * 230 * @param javadoc the javadoc of the AST 231 * @return true if contains the tag 232 */ 233 private boolean containsDeprecatedTag(DetailNode javadoc) { 234 boolean found = false; 235 for (DetailNode child : javadoc.getChildren()) { 236 if (child.getType() == JavadocTokenTypes.JAVADOC_TAG 237 && child.getChildren()[0].getType() == JavadocTokenTypes.DEPRECATED_LITERAL) { 238 if (found) { 239 log(child.getLineNumber(), MSG_KEY_JAVADOC_DUPLICATE_TAG, 240 JavadocTagInfo.DEPRECATED.getText()); 241 } 242 found = true; 243 } 244 } 245 return found; 246 } 247 248 /** 249 * Returns the parent node of the comment. 250 * 251 * @param commentBlock child node. 252 * @return parent node. 253 */ 254 private static DetailAST getParent(DetailAST commentBlock) { 255 DetailAST result = commentBlock.getParent(); 256 257 if (TokenUtil.isRootNode(result)) { 258 result = commentBlock.getNextSibling(); 259 } 260 261 while (true) { 262 final int type = result.getType(); 263 if (TYPES_HASH_SET.get(type)) { 264 result = result.getParent(); 265 } 266 else if (type == TokenTypes.SINGLE_LINE_COMMENT) { 267 result = result.getNextSibling(); 268 } 269 else { 270 break; 271 } 272 } 273 274 return result; 275 } 276 277}