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