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