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