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 java.util.regex.Matcher; 023import java.util.regex.Pattern; 024 025import com.puppycrawl.tools.checkstyle.StatelessCheck; 026import com.puppycrawl.tools.checkstyle.api.AbstractCheck; 027import com.puppycrawl.tools.checkstyle.api.DetailAST; 028import com.puppycrawl.tools.checkstyle.api.TextBlock; 029import com.puppycrawl.tools.checkstyle.api.TokenTypes; 030import com.puppycrawl.tools.checkstyle.checks.javadoc.JavadocTagInfo; 031import com.puppycrawl.tools.checkstyle.utils.AnnotationUtil; 032import com.puppycrawl.tools.checkstyle.utils.CommonUtil; 033 034/** 035 * <p> 036 * Verifies that the {@code @Override} annotation is present 037 * when the {@code @inheritDoc} javadoc tag is present. 038 * </p> 039 * <p> 040 * Rationale: The @Override annotation helps 041 * compiler tools ensure that an override is actually occurring. It is 042 * quite easy to accidentally overload a method or hide a static method 043 * and using the @Override annotation points out these problems. 044 * </p> 045 * <p> 046 * This check will log a violation if using the @inheritDoc tag on a method that 047 * is not valid (ex: private, or static method). 048 * </p> 049 * <p> 050 * There is a slight difference between the @Override annotation in Java 5 versus 051 * Java 6 and above. In Java 5, any method overridden from an interface cannot 052 * be annotated with @Override. In Java 6 this behavior is allowed. 053 * </p> 054 * <p> 055 * As a result of the aforementioned difference between Java 5 and Java 6, a 056 * property called {@code javaFiveCompatibility} is available. This 057 * property will only check classes, interfaces, etc. that do not contain the 058 * extends or implements keyword or are not anonymous classes. This means it 059 * only checks methods overridden from {@code java.lang.Object}. 060 * <b>Java 5 Compatibility mode severely limits this check. It is recommended to 061 * only use it on Java 5 source.</b> 062 * </p> 063 * <ul> 064 * <li> 065 * Property {@code javaFiveCompatibility} - Enable java 5 compatibility mode. 066 * Type is {@code boolean}. 067 * Default value is {@code false}. 068 * </li> 069 * </ul> 070 * <p> 071 * To configure the check: 072 * </p> 073 * <pre> 074 * <module name="MissingOverride"/> 075 * </pre> 076 * <p>Example:</p> 077 * <pre> 078 * class Test extends SuperClass { 079 * 080 * /** {@inheritDoc} */ 081 * @Override 082 * public void test1() { // OK 083 * 084 * } 085 * 086 * /** {@inheritDoc} */ 087 * public void test2() { // violation, should be annotated with @Override 088 * 089 * } 090 * 091 * /** {@inheritDoc} */ 092 * private void test3() { // violation, using the @inheritDoc tag on private method 093 * 094 * } 095 * 096 * /** {@inheritDoc} */ 097 * public static void test4() { // violation, using the @inheritDoc tag on static method 098 * 099 * } 100 * } 101 * </pre> 102 * <p> 103 * To configure the check for the {@code javaFiveCompatibility} mode: 104 * </p> 105 * <pre> 106 * <module name="MissingOverride"> 107 * <property name="javaFiveCompatibility" 108 * value="true"/> 109 * </module> 110 * </pre> 111 * <p>Example:</p> 112 * <pre> 113 * class Test1 { 114 * 115 * /** {@inheritDoc} */ 116 * public void equals() { // violation, should be annotated with @Override 117 * 118 * } 119 * } 120 * 121 * interface Test2 { 122 * 123 * /** {@inheritDoc} */ 124 * void test(); // violation, should be annotated with @Override 125 * } 126 * 127 * class Test3 extends SuperClass { 128 * 129 * /** {@inheritDoc} */ 130 * public void test() { // OK, is ignored because class extends other class 131 * 132 * } 133 * } 134 * 135 * class Test4 implements SuperInterface { 136 * 137 * /** {@inheritDoc} */ 138 * public void test() { // OK, is ignored because class implements interface 139 * 140 * } 141 * } 142 * 143 * class Test5 { 144 * Runnable r = new Runnable() { 145 * /** {@inheritDoc} */ 146 * public void run() { // OK, is ignored because class is anonymous class 147 * 148 * } 149 * }; 150 * } 151 * </pre> 152 * <p> 153 * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker} 154 * </p> 155 * <p> 156 * Violation Message Keys: 157 * </p> 158 * <ul> 159 * <li> 160 * {@code annotation.missing.override} 161 * </li> 162 * <li> 163 * {@code tag.not.valid.on} 164 * </li> 165 * </ul> 166 * 167 * @since 5.0 168 */ 169@StatelessCheck 170public final class MissingOverrideCheck extends AbstractCheck { 171 172 /** 173 * A key is pointing to the warning message text in "messages.properties" 174 * file. 175 */ 176 public static final String MSG_KEY_TAG_NOT_VALID_ON = "tag.not.valid.on"; 177 178 /** 179 * A key is pointing to the warning message text in "messages.properties" 180 * file. 181 */ 182 public static final String MSG_KEY_ANNOTATION_MISSING_OVERRIDE = 183 "annotation.missing.override"; 184 185 /** {@link Override Override} annotation name. */ 186 private static final String OVERRIDE = "Override"; 187 188 /** Fully-qualified {@link Override Override} annotation name. */ 189 private static final String FQ_OVERRIDE = "java.lang." + OVERRIDE; 190 191 /** Compiled regexp to match Javadoc tags with no argument and {}. */ 192 private static final Pattern MATCH_INHERIT_DOC = 193 CommonUtil.createPattern("\\{\\s*@(inheritDoc)\\s*\\}"); 194 195 /** 196 * Enable java 5 compatibility mode. 197 */ 198 private boolean javaFiveCompatibility; 199 200 /** 201 * Setter to enable java 5 compatibility mode. 202 * 203 * @param compatibility compatibility or not 204 */ 205 public void setJavaFiveCompatibility(final boolean compatibility) { 206 javaFiveCompatibility = compatibility; 207 } 208 209 @Override 210 public int[] getDefaultTokens() { 211 return getRequiredTokens(); 212 } 213 214 @Override 215 public int[] getAcceptableTokens() { 216 return getRequiredTokens(); 217 } 218 219 @Override 220 public int[] getRequiredTokens() { 221 return new int[] 222 {TokenTypes.METHOD_DEF, }; 223 } 224 225 // -@cs[CyclomaticComplexity] Too complex to break apart. 226 @Override 227 public void visitToken(final DetailAST ast) { 228 final TextBlock javadoc = 229 getFileContents().getJavadocBefore(ast.getLineNo()); 230 231 final boolean containsTag = containsJavadocTag(javadoc); 232 if (containsTag && !JavadocTagInfo.INHERIT_DOC.isValidOn(ast)) { 233 log(ast, MSG_KEY_TAG_NOT_VALID_ON, 234 JavadocTagInfo.INHERIT_DOC.getText()); 235 } 236 else { 237 boolean check = true; 238 239 if (javaFiveCompatibility) { 240 final DetailAST defOrNew = ast.getParent().getParent(); 241 242 if (defOrNew.findFirstToken(TokenTypes.EXTENDS_CLAUSE) != null 243 || defOrNew.findFirstToken(TokenTypes.IMPLEMENTS_CLAUSE) != null 244 || defOrNew.getType() == TokenTypes.LITERAL_NEW) { 245 check = false; 246 } 247 } 248 249 if (check 250 && containsTag 251 && !AnnotationUtil.containsAnnotation(ast, OVERRIDE) 252 && !AnnotationUtil.containsAnnotation(ast, FQ_OVERRIDE)) { 253 log(ast, MSG_KEY_ANNOTATION_MISSING_OVERRIDE); 254 } 255 } 256 } 257 258 /** 259 * Checks to see if the text block contains a inheritDoc tag. 260 * 261 * @param javadoc the javadoc of the AST 262 * @return true if contains the tag 263 */ 264 private static boolean containsJavadocTag(final TextBlock javadoc) { 265 boolean javadocTag = false; 266 267 if (javadoc != null) { 268 final String[] lines = javadoc.getText(); 269 270 for (final String line : lines) { 271 final Matcher matchInheritDoc = 272 MATCH_INHERIT_DOC.matcher(line); 273 274 if (matchInheritDoc.find()) { 275 javadocTag = true; 276 break; 277 } 278 } 279 } 280 return javadocTag; 281 } 282 283}