001//////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code for adherence to a set of rules. 003// Copyright (C) 2001-2020 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 com.puppycrawl.tools.checkstyle.StatelessCheck; 023import com.puppycrawl.tools.checkstyle.api.DetailAST; 024import com.puppycrawl.tools.checkstyle.api.DetailNode; 025import com.puppycrawl.tools.checkstyle.api.JavadocTokenTypes; 026import com.puppycrawl.tools.checkstyle.api.TokenTypes; 027import com.puppycrawl.tools.checkstyle.checks.javadoc.AbstractJavadocCheck; 028import com.puppycrawl.tools.checkstyle.checks.javadoc.JavadocTagInfo; 029import com.puppycrawl.tools.checkstyle.utils.AnnotationUtil; 030 031/** 032 * <p> 033 * Verifies that the annotation {@code @Deprecated} and the Javadoc tag 034 * {@code @deprecated} are both present when either of them is present. 035 * </p> 036 * <p> 037 * Both ways of flagging deprecation serve their own purpose. 038 * The @Deprecated annotation is used for compilers and development tools. 039 * The @deprecated javadoc tag is used to document why something is deprecated 040 * and what, if any, alternatives exist. 041 * </p> 042 * <p> 043 * In order to properly mark something as deprecated both forms of 044 * deprecation should be present. 045 * </p> 046 * <p> 047 * Package deprecation is a exception to the rule of always using the 048 * javadoc tag and annotation to deprecate. It is not clear if the javadoc 049 * tool will support it or not as newer versions keep flip flopping on if 050 * it is supported or will cause an error. See 051 * <a href="https://bugs.openjdk.java.net/browse/JDK-8160601">JDK-8160601</a>. 052 * The deprecated javadoc tag is currently the only way to say why the package 053 * is deprecated and what to use instead. Until this is resolved, if you don't 054 * want to print violations on package-info, you can use a 055 * <a href="https://checkstyle.org/config_filters.html">filter</a> to ignore 056 * these files until the javadoc tool faithfully supports it. An example config 057 * using SuppressionSingleFilter is: 058 * </p> 059 * <pre> 060 * <!-- required till https://bugs.openjdk.java.net/browse/JDK-8160601 --> 061 * <module name="SuppressionSingleFilter"> 062 * <property name="checks" value="MissingDeprecatedCheck"/> 063 * <property name="files" value="package-info\.java"/> 064 * </module> 065 * </pre> 066 * <ul> 067 * <li> 068 * Property {@code violateExecutionOnNonTightHtml} - Control when to 069 * print violations if the Javadoc being examined by this check violates the 070 * tight html rules defined at 071 * <a href="https://checkstyle.org/writingjavadocchecks.html#Tight-HTML_rules"> 072 * Tight-HTML Rules</a>. 073 * Default value is {@code false}. 074 * </li> 075 * </ul> 076 * <p> 077 * To configure the check: 078 * </p> 079 * <pre> 080 * <module name="MissingDeprecated"/> 081 * </pre> 082 * <p> 083 * Examples of validating source code: 084 * </p> 085 * <pre> 086 * @deprecated 087 * public static final int MY_CONST = 123456; // no violation 088 * 089 * /** This javadoc is missing deprecated tag. */ 090 * @deprecated 091 * public static final int COUNTER = 10; // violation 092 * </pre> 093 * 094 * @since 5.0 095 */ 096@StatelessCheck 097public final class MissingDeprecatedCheck extends AbstractJavadocCheck { 098 099 /** 100 * A key is pointing to the warning message text in "messages.properties" 101 * file. 102 */ 103 public static final String MSG_KEY_ANNOTATION_MISSING_DEPRECATED = 104 "annotation.missing.deprecated"; 105 106 /** 107 * A key is pointing to the warning message text in "messages.properties" 108 * file. 109 */ 110 public static final String MSG_KEY_JAVADOC_DUPLICATE_TAG = 111 "javadoc.duplicateTag"; 112 113 /** {@link Deprecated Deprecated} annotation name. */ 114 private static final String DEPRECATED = "Deprecated"; 115 116 /** Fully-qualified {@link Deprecated Deprecated} annotation name. */ 117 private static final String FQ_DEPRECATED = "java.lang." + DEPRECATED; 118 119 @Override 120 public int[] getDefaultJavadocTokens() { 121 return getRequiredJavadocTokens(); 122 } 123 124 @Override 125 public int[] getRequiredJavadocTokens() { 126 return new int[] { 127 JavadocTokenTypes.JAVADOC, 128 }; 129 } 130 131 @Override 132 public void visitJavadocToken(DetailNode ast) { 133 final DetailAST parentAst = getParent(getBlockCommentAst()); 134 135 final boolean containsAnnotation = 136 AnnotationUtil.containsAnnotation(parentAst, DEPRECATED) 137 || AnnotationUtil.containsAnnotation(parentAst, FQ_DEPRECATED); 138 139 final boolean containsJavadocTag = containsDeprecatedTag(ast); 140 141 if (containsAnnotation ^ containsJavadocTag) { 142 log(parentAst.getLineNo(), MSG_KEY_ANNOTATION_MISSING_DEPRECATED); 143 } 144 } 145 146 /** 147 * Checks to see if the javadoc contains a deprecated tag. 148 * 149 * @param javadoc the javadoc of the AST 150 * @return true if contains the tag 151 */ 152 private boolean containsDeprecatedTag(DetailNode javadoc) { 153 boolean found = false; 154 for (DetailNode child : javadoc.getChildren()) { 155 if (child.getType() == JavadocTokenTypes.JAVADOC_TAG 156 && child.getChildren()[0].getType() == JavadocTokenTypes.DEPRECATED_LITERAL) { 157 if (found) { 158 log(child.getLineNumber(), MSG_KEY_JAVADOC_DUPLICATE_TAG, 159 JavadocTagInfo.DEPRECATED.getText()); 160 } 161 found = true; 162 } 163 } 164 return found; 165 } 166 167 /** 168 * Returns the parent node of the comment. 169 * @param commentBlock child node. 170 * @return parent node. 171 */ 172 private static DetailAST getParent(DetailAST commentBlock) { 173 DetailAST result = commentBlock.getParent(); 174 175 if (result == null) { 176 result = commentBlock.getNextSibling(); 177 } 178 179 while (true) { 180 final int type = result.getType(); 181 if (type == TokenTypes.TYPE || type == TokenTypes.MODIFIERS 182 || type == TokenTypes.ANNOTATION || type == TokenTypes.ANNOTATIONS 183 || type == TokenTypes.ARRAY_DECLARATOR || type == TokenTypes.TYPE_PARAMETERS 184 || type == TokenTypes.DOT) { 185 result = result.getParent(); 186 } 187 else { 188 break; 189 } 190 } 191 192 return result; 193 } 194 195}