001/////////////////////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code and other text files for adherence to a set of rules. 003// Copyright (C) 2001-2026 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.JavadocCommentsTokenTypes; 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 * <div> 036 * Verifies that the annotation {@code @Deprecated} and the Javadoc tag 037 * {@code @deprecated} are both present when either of them is present. 038 * </div> 039 * 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 * 047 * <p> 048 * In order to properly mark something as deprecated both forms of 049 * deprecation should be present. 050 * </p> 051 * 052 * <p> 053 * Package deprecation is an exception to the rule of always using the 054 * javadoc tag and annotation to deprecate. It is not clear if the javadoc 055 * tool will support it or not as newer versions keep flip-flopping on if 056 * it is supported or will cause an error. See 057 * <a href="https://bugs.openjdk.org/browse/JDK-8160601">JDK-8160601</a>. 058 * The deprecated javadoc tag is currently the only way to say why the package 059 * is deprecated and what to use instead. Until this is resolved, if you don't 060 * want to print violations on package-info, you can use a 061 * <a href="https://checkstyle.org/filters/index.html">filter</a> to ignore 062 * these files until the javadoc tool faithfully supports it. An example config 063 * using SuppressionSingleFilter is: 064 * </p> 065 * <div class="wrapper"><pre class="prettyprint"><code class="language-xml"> 066 * <!-- required till https://bugs.openjdk.org/browse/JDK-8160601 --> 067 * <module name="SuppressionSingleFilter"> 068 * <property name="checks" value="MissingDeprecatedCheck"/> 069 * <property name="files" value="package-info\.java"/> 070 * </module> 071 * </code></pre></div> 072 * 073 * @since 5.0 074 */ 075@StatelessCheck 076public final class MissingDeprecatedCheck extends AbstractJavadocCheck { 077 078 /** 079 * A key is pointing to the warning message text in "messages.properties" 080 * file. 081 */ 082 public static final String MSG_KEY_ANNOTATION_MISSING_DEPRECATED = 083 "annotation.missing.deprecated"; 084 085 /** 086 * A key is pointing to the warning message text in "messages.properties" 087 * file. 088 */ 089 public static final String MSG_KEY_JAVADOC_DUPLICATE_TAG = 090 "javadoc.duplicateTag"; 091 092 /** {@link Deprecated Deprecated} annotation name. */ 093 private static final String DEPRECATED = "Deprecated"; 094 095 /** Fully-qualified {@link Deprecated Deprecated} annotation name. */ 096 private static final String FQ_DEPRECATED = "java.lang." + DEPRECATED; 097 098 /** Token types to find parent of. */ 099 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 /** 105 * Creates a new {@code MissingDeprecatedCheck} instance. 106 */ 107 public MissingDeprecatedCheck() { 108 // no code by default 109 } 110 111 @Override 112 public int[] getDefaultJavadocTokens() { 113 return getRequiredJavadocTokens(); 114 } 115 116 @Override 117 public int[] getRequiredJavadocTokens() { 118 return new int[] { 119 JavadocCommentsTokenTypes.JAVADOC_CONTENT, 120 }; 121 } 122 123 /** 124 * Setter to control when to print violations if the Javadoc being examined by this check 125 * violates the tight html rules defined at 126 * <a href="https://checkstyle.org/writingjavadocchecks.html#Tight-HTML_rules"> 127 * Tight-HTML Rules</a>. 128 * 129 * @param shouldReportViolation value to which the field shall be set to 130 * @since 8.3 131 * @propertySince 8.24 132 */ 133 @Override 134 public void setViolateExecutionOnNonTightHtml(boolean shouldReportViolation) { 135 super.setViolateExecutionOnNonTightHtml(shouldReportViolation); 136 } 137 138 @Override 139 public void visitJavadocToken(DetailNode ast) { 140 final DetailAST parentAst = getParent(getBlockCommentAst()); 141 142 final boolean containsAnnotation = 143 AnnotationUtil.containsAnnotation(parentAst, DEPRECATED) 144 || AnnotationUtil.containsAnnotation(parentAst, FQ_DEPRECATED); 145 146 final boolean containsJavadocTag = containsDeprecatedTag(ast); 147 148 if (containsAnnotation ^ containsJavadocTag) { 149 log(parentAst.getLineNo(), MSG_KEY_ANNOTATION_MISSING_DEPRECATED); 150 } 151 } 152 153 /** 154 * Checks to see if the javadoc contains a deprecated tag. 155 * 156 * @param javadoc the javadoc of the AST 157 * @return true if contains the tag 158 */ 159 private boolean containsDeprecatedTag(DetailNode javadoc) { 160 boolean found = false; 161 DetailNode node = javadoc.getFirstChild(); 162 while (node != null) { 163 if (node.getType() == JavadocCommentsTokenTypes.JAVADOC_BLOCK_TAG 164 && node.getFirstChild().getType() 165 == JavadocCommentsTokenTypes.DEPRECATED_BLOCK_TAG) { 166 if (found) { 167 log(node.getLineNumber(), MSG_KEY_JAVADOC_DUPLICATE_TAG, 168 JavadocTagInfo.DEPRECATED.getText()); 169 } 170 found = true; 171 } 172 node = node.getNextSibling(); 173 } 174 return found; 175 } 176 177 /** 178 * Returns the parent node of the comment. 179 * 180 * @param commentBlock child node. 181 * @return parent node. 182 */ 183 private static DetailAST getParent(DetailAST commentBlock) { 184 DetailAST result = commentBlock.getParent(); 185 186 if (TokenUtil.isRootNode(result)) { 187 result = commentBlock.getNextSibling(); 188 } 189 190 while (true) { 191 final int type = result.getType(); 192 if (TYPES_HASH_SET.get(type)) { 193 result = result.getParent(); 194 } 195 else if (type == TokenTypes.SINGLE_LINE_COMMENT) { 196 result = result.getNextSibling(); 197 } 198 else { 199 break; 200 } 201 } 202 203 return result; 204 } 205 206}