1 //////////////////////////////////////////////////////////////////////////////// 2 // checkstyle: Checks Java source code for adherence to a set of rules. 3 // Copyright (C) 2001-2019 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.regex.Matcher; 23 import java.util.regex.Pattern; 24 25 import com.puppycrawl.tools.checkstyle.StatelessCheck; 26 import com.puppycrawl.tools.checkstyle.api.AbstractCheck; 27 import com.puppycrawl.tools.checkstyle.api.DetailAST; 28 import com.puppycrawl.tools.checkstyle.api.TextBlock; 29 import com.puppycrawl.tools.checkstyle.api.TokenTypes; 30 import com.puppycrawl.tools.checkstyle.checks.javadoc.JavadocTagInfo; 31 import com.puppycrawl.tools.checkstyle.utils.AnnotationUtil; 32 import com.puppycrawl.tools.checkstyle.utils.CommonUtil; 33 34 /** 35 * <p> 36 * Verifies that the @Override annotation is present 37 * when the @inheritDoc javadoc tag is present. 38 * </p> 39 * <p> 40 * Rationale: The @Override annotation helps 41 * compiler tools ensure that an override is actually occurring. It is 42 * quite easy to accidentally overload a method or hide a static method 43 * and using the @Override annotation points out these problems. 44 * </p> 45 * <p> 46 * This check will log a violation if using the @inheritDoc tag on a method that 47 * is not valid (ex: private, or static method). 48 * </p> 49 * <p> 50 * There is a slight difference between the @Override annotation in Java 5 versus 51 * Java 6 and above. In Java 5, any method overridden from an interface cannot 52 * be annotated with @Override. In Java 6 this behavior is allowed. 53 * </p> 54 * <p> 55 * As a result of the aforementioned difference between Java 5 and Java 6, a 56 * property called {@code javaFiveCompatibility} is available. This 57 * property will only check classes, interfaces, etc. that do not contain the 58 * extends or implements keyword or are not anonymous classes. This means it 59 * only checks methods overridden from {@code java.lang.Object}. 60 * <b>Java 5 Compatibility mode severely limits this check. It is recommended to 61 * only use it on Java 5 source.</b> 62 * </p> 63 * <ul> 64 * <li> 65 * Property {@code javaFiveCompatibility} - Enable java 5 compatibility mode. 66 * Default value is {@code false}. 67 * </li> 68 * </ul> 69 * <p> 70 * To configure the check: 71 * </p> 72 * <pre> 73 * <module name="MissingOverride"/> 74 * </pre> 75 * <p> 76 * To configure the check for the {@code javaFiveCompatibility} mode: 77 * </p> 78 * <pre> 79 * <module name="MissingOverride"> 80 * <property name="javaFiveCompatibility" 81 * value="true"/> 82 * </module> 83 * </pre> 84 * 85 * @since 5.0 86 */ 87 @StatelessCheck 88 public final class MissingOverrideCheck extends AbstractCheck { 89 90 /** 91 * A key is pointing to the warning message text in "messages.properties" 92 * file. 93 */ 94 public static final String MSG_KEY_TAG_NOT_VALID_ON = "tag.not.valid.on"; 95 96 /** 97 * A key is pointing to the warning message text in "messages.properties" 98 * file. 99 */ 100 public static final String MSG_KEY_ANNOTATION_MISSING_OVERRIDE = 101 "annotation.missing.override"; 102 103 /** {@link Override Override} annotation name. */ 104 private static final String OVERRIDE = "Override"; 105 106 /** Fully-qualified {@link Override Override} annotation name. */ 107 private static final String FQ_OVERRIDE = "java.lang." + OVERRIDE; 108 109 /** Compiled regexp to match Javadoc tags with no argument and {}. */ 110 private static final Pattern MATCH_INHERIT_DOC = 111 CommonUtil.createPattern("\\{\\s*@(inheritDoc)\\s*\\}"); 112 113 /** 114 * Enable java 5 compatibility mode. 115 */ 116 private boolean javaFiveCompatibility; 117 118 /** 119 * Setter to enable java 5 compatibility mode. 120 * @param compatibility compatibility or not 121 */ 122 public void setJavaFiveCompatibility(final boolean compatibility) { 123 javaFiveCompatibility = compatibility; 124 } 125 126 @Override 127 public int[] getDefaultTokens() { 128 return getRequiredTokens(); 129 } 130 131 @Override 132 public int[] getAcceptableTokens() { 133 return getRequiredTokens(); 134 } 135 136 @Override 137 public int[] getRequiredTokens() { 138 return new int[] 139 {TokenTypes.METHOD_DEF, }; 140 } 141 142 // -@cs[CyclomaticComplexity] Too complex to break apart. 143 @Override 144 public void visitToken(final DetailAST ast) { 145 final TextBlock javadoc = 146 getFileContents().getJavadocBefore(ast.getLineNo()); 147 148 final boolean containsTag = containsJavadocTag(javadoc); 149 if (containsTag && !JavadocTagInfo.INHERIT_DOC.isValidOn(ast)) { 150 log(ast.getLineNo(), MSG_KEY_TAG_NOT_VALID_ON, 151 JavadocTagInfo.INHERIT_DOC.getText()); 152 } 153 else { 154 boolean check = true; 155 156 if (javaFiveCompatibility) { 157 final DetailAST defOrNew = ast.getParent().getParent(); 158 159 if (defOrNew.findFirstToken(TokenTypes.EXTENDS_CLAUSE) != null 160 || defOrNew.findFirstToken(TokenTypes.IMPLEMENTS_CLAUSE) != null 161 || defOrNew.getType() == TokenTypes.LITERAL_NEW) { 162 check = false; 163 } 164 } 165 166 if (check 167 && containsTag 168 && !AnnotationUtil.containsAnnotation(ast, OVERRIDE) 169 && !AnnotationUtil.containsAnnotation(ast, FQ_OVERRIDE)) { 170 log(ast.getLineNo(), MSG_KEY_ANNOTATION_MISSING_OVERRIDE); 171 } 172 } 173 } 174 175 /** 176 * Checks to see if the text block contains a inheritDoc tag. 177 * 178 * @param javadoc the javadoc of the AST 179 * @return true if contains the tag 180 */ 181 private static boolean containsJavadocTag(final TextBlock javadoc) { 182 boolean javadocTag = false; 183 184 if (javadoc != null) { 185 final String[] lines = javadoc.getText(); 186 187 for (final String line : lines) { 188 final Matcher matchInheritDoc = 189 MATCH_INHERIT_DOC.matcher(line); 190 191 if (matchInheritDoc.find()) { 192 javadocTag = true; 193 break; 194 } 195 } 196 } 197 return javadocTag; 198 } 199 200 }