1 ///////////////////////////////////////////////////////////////////////////////////////////////
2 // checkstyle: Checks Java source code and other text files for adherence to a set of rules.
3 // Copyright (C) 2001-2026 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 * {@snippet lang="text" :
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 * }
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 /**
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/writing-javadoc-checks.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 }