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 * Default value is {@code false}. 067 * </li> 068 * </ul> 069 * <p> 070 * To configure the check: 071 * </p> 072 * <pre> 073 * <module name="MissingOverride"/> 074 * </pre> 075 * <p>Example:</p> 076 * <pre> 077 * class Test extends SuperClass { 078 * 079 * /** {@inheritDoc} */ 080 * @Override 081 * public void test1() { // OK 082 * 083 * } 084 * 085 * /** {@inheritDoc} */ 086 * public void test2() { // violation, should be annotated with @Override 087 * 088 * } 089 * 090 * /** {@inheritDoc} */ 091 * private void test3() { // violation, using the @inheritDoc tag on private method 092 * 093 * } 094 * 095 * /** {@inheritDoc} */ 096 * public static void test4() { // violation, using the @inheritDoc tag on static method 097 * 098 * } 099 * } 100 * </pre> 101 * <p> 102 * To configure the check for the {@code javaFiveCompatibility} mode: 103 * </p> 104 * <pre> 105 * <module name="MissingOverride"> 106 * <property name="javaFiveCompatibility" 107 * value="true"/> 108 * </module> 109 * </pre> 110 * <p>Example:</p> 111 * <pre> 112 * class Test1 { 113 * 114 * /** {@inheritDoc} */ 115 * public void equals() { // violation, should be annotated with @Override 116 * 117 * } 118 * } 119 * 120 * interface Test2 { 121 * 122 * /** {@inheritDoc} */ 123 * void test(); // violation, should be annotated with @Override 124 * } 125 * 126 * class Test3 extends SuperClass { 127 * 128 * /** {@inheritDoc} */ 129 * public void test() { // OK, is ignored because class extends other class 130 * 131 * } 132 * } 133 * 134 * class Test4 implements SuperInterface { 135 * 136 * /** {@inheritDoc} */ 137 * public void test() { // OK, is ignored because class implements interface 138 * 139 * } 140 * } 141 * 142 * class Test5 { 143 * Runnable r = new Runnable() { 144 * /** {@inheritDoc} */ 145 * public void run() { // OK, is ignored because class is anonymous class 146 * 147 * } 148 * }; 149 * } 150 * </pre> 151 * 152 * @since 5.0 153 */ 154@StatelessCheck 155public final class MissingOverrideCheck extends AbstractCheck { 156 157 /** 158 * A key is pointing to the warning message text in "messages.properties" 159 * file. 160 */ 161 public static final String MSG_KEY_TAG_NOT_VALID_ON = "tag.not.valid.on"; 162 163 /** 164 * A key is pointing to the warning message text in "messages.properties" 165 * file. 166 */ 167 public static final String MSG_KEY_ANNOTATION_MISSING_OVERRIDE = 168 "annotation.missing.override"; 169 170 /** {@link Override Override} annotation name. */ 171 private static final String OVERRIDE = "Override"; 172 173 /** Fully-qualified {@link Override Override} annotation name. */ 174 private static final String FQ_OVERRIDE = "java.lang." + OVERRIDE; 175 176 /** Compiled regexp to match Javadoc tags with no argument and {}. */ 177 private static final Pattern MATCH_INHERIT_DOC = 178 CommonUtil.createPattern("\\{\\s*@(inheritDoc)\\s*\\}"); 179 180 /** 181 * Enable java 5 compatibility mode. 182 */ 183 private boolean javaFiveCompatibility; 184 185 /** 186 * Setter to enable java 5 compatibility mode. 187 * 188 * @param compatibility compatibility or not 189 */ 190 public void setJavaFiveCompatibility(final boolean compatibility) { 191 javaFiveCompatibility = compatibility; 192 } 193 194 @Override 195 public int[] getDefaultTokens() { 196 return getRequiredTokens(); 197 } 198 199 @Override 200 public int[] getAcceptableTokens() { 201 return getRequiredTokens(); 202 } 203 204 @Override 205 public int[] getRequiredTokens() { 206 return new int[] 207 {TokenTypes.METHOD_DEF, }; 208 } 209 210 // -@cs[CyclomaticComplexity] Too complex to break apart. 211 @Override 212 public void visitToken(final DetailAST ast) { 213 final TextBlock javadoc = 214 getFileContents().getJavadocBefore(ast.getLineNo()); 215 216 final boolean containsTag = containsJavadocTag(javadoc); 217 if (containsTag && !JavadocTagInfo.INHERIT_DOC.isValidOn(ast)) { 218 log(ast, MSG_KEY_TAG_NOT_VALID_ON, 219 JavadocTagInfo.INHERIT_DOC.getText()); 220 } 221 else { 222 boolean check = true; 223 224 if (javaFiveCompatibility) { 225 final DetailAST defOrNew = ast.getParent().getParent(); 226 227 if (defOrNew.findFirstToken(TokenTypes.EXTENDS_CLAUSE) != null 228 || defOrNew.findFirstToken(TokenTypes.IMPLEMENTS_CLAUSE) != null 229 || defOrNew.getType() == TokenTypes.LITERAL_NEW) { 230 check = false; 231 } 232 } 233 234 if (check 235 && containsTag 236 && !AnnotationUtil.containsAnnotation(ast, OVERRIDE) 237 && !AnnotationUtil.containsAnnotation(ast, FQ_OVERRIDE)) { 238 log(ast, MSG_KEY_ANNOTATION_MISSING_OVERRIDE); 239 } 240 } 241 } 242 243 /** 244 * Checks to see if the text block contains a inheritDoc tag. 245 * 246 * @param javadoc the javadoc of the AST 247 * @return true if contains the tag 248 */ 249 private static boolean containsJavadocTag(final TextBlock javadoc) { 250 boolean javadocTag = false; 251 252 if (javadoc != null) { 253 final String[] lines = javadoc.getText(); 254 255 for (final String line : lines) { 256 final Matcher matchInheritDoc = 257 MATCH_INHERIT_DOC.matcher(line); 258 259 if (matchInheritDoc.find()) { 260 javadocTag = true; 261 break; 262 } 263 } 264 } 265 return javadocTag; 266 } 267 268}