1 /////////////////////////////////////////////////////////////////////////////////////////////// 2 // checkstyle: Checks Java source code and other text files for adherence to a set of rules. 3 // Copyright (C) 2001-2024 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.annotation; 21 22 import java.util.BitSet; 23 24 import com.puppycrawl.tools.checkstyle.StatelessCheck; 25 import com.puppycrawl.tools.checkstyle.api.DetailAST; 26 import com.puppycrawl.tools.checkstyle.api.DetailNode; 27 import com.puppycrawl.tools.checkstyle.api.JavadocTokenTypes; 28 import com.puppycrawl.tools.checkstyle.api.TokenTypes; 29 import com.puppycrawl.tools.checkstyle.checks.javadoc.AbstractJavadocCheck; 30 import com.puppycrawl.tools.checkstyle.checks.javadoc.JavadocTagInfo; 31 import com.puppycrawl.tools.checkstyle.utils.AnnotationUtil; 32 import com.puppycrawl.tools.checkstyle.utils.TokenUtil; 33 34 /** 35 * <p> 36 * Verifies that the annotation {@code @Deprecated} and the Javadoc tag 37 * {@code @deprecated} are both present when either of them is present. 38 * </p> 39 * <p> 40 * Both ways of flagging deprecation serve their own purpose. 41 * The @Deprecated annotation is used for compilers and development tools. 42 * The @deprecated javadoc tag is used to document why something is deprecated 43 * and what, if any, alternatives exist. 44 * </p> 45 * <p> 46 * In order to properly mark something as deprecated both forms of 47 * deprecation should be present. 48 * </p> 49 * <p> 50 * Package deprecation is an exception to the rule of always using the 51 * javadoc tag and annotation to deprecate. It is not clear if the javadoc 52 * tool will support it or not as newer versions keep flip-flopping on if 53 * it is supported or will cause an error. See 54 * <a href="https://bugs.openjdk.org/browse/JDK-8160601">JDK-8160601</a>. 55 * The deprecated javadoc tag is currently the only way to say why the package 56 * is deprecated and what to use instead. Until this is resolved, if you don't 57 * want to print violations on package-info, you can use a 58 * <a href="https://checkstyle.org/filters/index.html">filter</a> to ignore 59 * these files until the javadoc tool faithfully supports it. An example config 60 * using SuppressionSingleFilter is: 61 * </p> 62 * <pre> 63 * <!-- required till https://bugs.openjdk.org/browse/JDK-8160601 --> 64 * <module name="SuppressionSingleFilter"> 65 * <property name="checks" value="MissingDeprecatedCheck"/> 66 * <property name="files" value="package-info\.java"/> 67 * </module> 68 * </pre> 69 * <ul> 70 * <li> 71 * Property {@code violateExecutionOnNonTightHtml} - Control when to 72 * print violations if the Javadoc being examined by this check violates the 73 * tight html rules defined at 74 * <a href="https://checkstyle.org/writingjavadocchecks.html#Tight-HTML_rules"> 75 * Tight-HTML Rules</a>. 76 * Type is {@code boolean}. 77 * Default value is {@code false}. 78 * </li> 79 * </ul> 80 * <p> 81 * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker} 82 * </p> 83 * <p> 84 * Violation Message Keys: 85 * </p> 86 * <ul> 87 * <li> 88 * {@code annotation.missing.deprecated} 89 * </li> 90 * <li> 91 * {@code javadoc.duplicateTag} 92 * </li> 93 * <li> 94 * {@code javadoc.missed.html.close} 95 * </li> 96 * <li> 97 * {@code javadoc.parse.rule.error} 98 * </li> 99 * <li> 100 * {@code javadoc.unclosedHtml} 101 * </li> 102 * <li> 103 * {@code javadoc.wrong.singleton.html.tag} 104 * </li> 105 * </ul> 106 * 107 * @since 5.0 108 */ 109 @StatelessCheck 110 public final class MissingDeprecatedCheck extends AbstractJavadocCheck { 111 112 /** 113 * A key is pointing to the warning message text in "messages.properties" 114 * file. 115 */ 116 public static final String MSG_KEY_ANNOTATION_MISSING_DEPRECATED = 117 "annotation.missing.deprecated"; 118 119 /** 120 * A key is pointing to the warning message text in "messages.properties" 121 * file. 122 */ 123 public static final String MSG_KEY_JAVADOC_DUPLICATE_TAG = 124 "javadoc.duplicateTag"; 125 126 /** {@link Deprecated Deprecated} annotation name. */ 127 private static final String DEPRECATED = "Deprecated"; 128 129 /** Fully-qualified {@link Deprecated Deprecated} annotation name. */ 130 private static final String FQ_DEPRECATED = "java.lang." + DEPRECATED; 131 132 /** Token types to find parent of. */ 133 private static final BitSet TYPES_HASH_SET = TokenUtil.asBitSet( 134 TokenTypes.TYPE, TokenTypes.MODIFIERS, TokenTypes.ANNOTATION, 135 TokenTypes.ANNOTATIONS, TokenTypes.ARRAY_DECLARATOR, 136 TokenTypes.TYPE_PARAMETERS, TokenTypes.DOT); 137 138 @Override 139 public int[] getDefaultJavadocTokens() { 140 return getRequiredJavadocTokens(); 141 } 142 143 @Override 144 public int[] getRequiredJavadocTokens() { 145 return new int[] { 146 JavadocTokenTypes.JAVADOC, 147 }; 148 } 149 150 @Override 151 public void visitJavadocToken(DetailNode ast) { 152 final DetailAST parentAst = getParent(getBlockCommentAst()); 153 154 final boolean containsAnnotation = 155 AnnotationUtil.containsAnnotation(parentAst, DEPRECATED) 156 || AnnotationUtil.containsAnnotation(parentAst, FQ_DEPRECATED); 157 158 final boolean containsJavadocTag = containsDeprecatedTag(ast); 159 160 if (containsAnnotation ^ containsJavadocTag) { 161 log(parentAst.getLineNo(), MSG_KEY_ANNOTATION_MISSING_DEPRECATED); 162 } 163 } 164 165 /** 166 * Checks to see if the javadoc contains a deprecated tag. 167 * 168 * @param javadoc the javadoc of the AST 169 * @return true if contains the tag 170 */ 171 private boolean containsDeprecatedTag(DetailNode javadoc) { 172 boolean found = false; 173 for (DetailNode child : javadoc.getChildren()) { 174 if (child.getType() == JavadocTokenTypes.JAVADOC_TAG 175 && child.getChildren()[0].getType() == JavadocTokenTypes.DEPRECATED_LITERAL) { 176 if (found) { 177 log(child.getLineNumber(), MSG_KEY_JAVADOC_DUPLICATE_TAG, 178 JavadocTagInfo.DEPRECATED.getText()); 179 } 180 found = true; 181 } 182 } 183 return found; 184 } 185 186 /** 187 * Returns the parent node of the comment. 188 * 189 * @param commentBlock child node. 190 * @return parent node. 191 */ 192 private static DetailAST getParent(DetailAST commentBlock) { 193 DetailAST result = commentBlock.getParent(); 194 195 if (TokenUtil.isRootNode(result)) { 196 result = commentBlock.getNextSibling(); 197 } 198 199 while (true) { 200 final int type = result.getType(); 201 if (TYPES_HASH_SET.get(type)) { 202 result = result.getParent(); 203 } 204 else if (type == TokenTypes.SINGLE_LINE_COMMENT) { 205 result = result.getNextSibling(); 206 } 207 else { 208 break; 209 } 210 } 211 212 return result; 213 } 214 215 }