001//////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code for adherence to a set of rules. 003// Copyright (C) 2001-2019 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 both the @Deprecated annotation is present 034 * and the @deprecated javadoc tag are present when either one 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} - If turned on, will 069 * print violations if the Javadoc being examined by this check violates the 070 * tight html rules defined at 071 * <a href="writingjavadocchecks.html#Tight-HTML_rules">Tight-HTML Rules</a>. 072 * Default value is {@code false}. 073 * </li> 074 * </ul> 075 * <p> 076 * To configure the check: 077 * </p> 078 * <pre> 079 * <module name="MissingDeprecated"/> 080 * </pre> 081 * <p> 082 * Examples of validating source code: 083 * </p> 084 * <pre> 085 * @deprecated 086 * public static final int MY_CONST = 123456; // no violation 087 * 088 * /** This javadoc is missing deprecated tag. */ 089 * @deprecated 090 * public static final int COUNTER = 10; // violation 091 * </pre> 092 * 093 * @since 5.0 094 */ 095@StatelessCheck 096public final class MissingDeprecatedCheck extends AbstractJavadocCheck { 097 098 /** 099 * A key is pointing to the warning message text in "messages.properties" 100 * file. 101 */ 102 public static final String MSG_KEY_ANNOTATION_MISSING_DEPRECATED = 103 "annotation.missing.deprecated"; 104 105 /** 106 * A key is pointing to the warning message text in "messages.properties" 107 * file. 108 */ 109 public static final String MSG_KEY_JAVADOC_DUPLICATE_TAG = 110 "javadoc.duplicateTag"; 111 112 /** {@link Deprecated Deprecated} annotation name. */ 113 private static final String DEPRECATED = "Deprecated"; 114 115 /** Fully-qualified {@link Deprecated Deprecated} annotation name. */ 116 private static final String FQ_DEPRECATED = "java.lang." + DEPRECATED; 117 118 @Override 119 public int[] getDefaultJavadocTokens() { 120 return getRequiredJavadocTokens(); 121 } 122 123 @Override 124 public int[] getRequiredJavadocTokens() { 125 return new int[] { 126 JavadocTokenTypes.JAVADOC, 127 }; 128 } 129 130 @Override 131 public void visitJavadocToken(DetailNode ast) { 132 final DetailAST parentAst = getParent(getBlockCommentAst()); 133 134 final boolean containsAnnotation = 135 AnnotationUtil.containsAnnotation(parentAst, DEPRECATED) 136 || AnnotationUtil.containsAnnotation(parentAst, FQ_DEPRECATED); 137 138 final boolean containsJavadocTag = containsDeprecatedTag(ast); 139 140 if (containsAnnotation ^ containsJavadocTag) { 141 log(parentAst.getLineNo(), MSG_KEY_ANNOTATION_MISSING_DEPRECATED); 142 } 143 } 144 145 /** 146 * Checks to see if the javadoc contains a deprecated tag. 147 * 148 * @param javadoc the javadoc of the AST 149 * @return true if contains the tag 150 */ 151 private boolean containsDeprecatedTag(DetailNode javadoc) { 152 boolean found = false; 153 for (DetailNode child : javadoc.getChildren()) { 154 if (child.getType() == JavadocTokenTypes.JAVADOC_TAG 155 && child.getChildren()[0].getType() == JavadocTokenTypes.DEPRECATED_LITERAL) { 156 if (found) { 157 log(child.getLineNumber(), MSG_KEY_JAVADOC_DUPLICATE_TAG, 158 JavadocTagInfo.DEPRECATED.getText()); 159 } 160 found = true; 161 } 162 } 163 return found; 164 } 165 166 /** 167 * Returns the parent node of the comment. 168 * @param commentBlock child node. 169 * @return parent node. 170 */ 171 private static DetailAST getParent(DetailAST commentBlock) { 172 DetailAST result = commentBlock.getParent(); 173 174 if (result == null) { 175 result = commentBlock.getNextSibling(); 176 } 177 178 while (true) { 179 final int type = result.getType(); 180 if (type == TokenTypes.TYPE || type == TokenTypes.MODIFIERS 181 || type == TokenTypes.ANNOTATION || type == TokenTypes.ANNOTATIONS 182 || type == TokenTypes.ARRAY_DECLARATOR || type == TokenTypes.TYPE_PARAMETERS 183 || type == TokenTypes.DOT) { 184 result = result.getParent(); 185 } 186 else { 187 break; 188 } 189 } 190 191 return result; 192 } 193 194}