001//////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code for adherence to a set of rules. 003// Copyright (C) 2001-2018 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.AnnotationUtility; 032import com.puppycrawl.tools.checkstyle.utils.CommonUtils; 033 034/** 035 * <p> 036 * This class is used to verify that the {@link Override Override} 037 * annotation is present when the inheritDoc javadoc tag is present. 038 * </p> 039 * 040 * <p> 041 * Rationale: The {@link Override Override} annotation helps 042 * compiler tools ensure that an override is actually occurring. It is 043 * quite easy to accidentally overload a method or hide a static method 044 * and using the {@link Override Override} annotation points 045 * out these problems. 046 * </p> 047 * 048 * <p> 049 * This check will log a violation if using the inheritDoc tag on a method that 050 * is not valid (ex: private, or static method). 051 * </p> 052 * 053 * <p> 054 * There is a slight difference between the Override annotation in Java 5 versus 055 * Java 6 and above. In Java 5, any method overridden from an interface cannot 056 * be annotated with Override. In Java 6 this behavior is allowed. 057 * </p> 058 * 059 * <p> 060 * As a result of the aforementioned difference between Java 5 and Java 6, a 061 * property called {@code javaFiveCompatibility } is available. This 062 * property will only check classes, interfaces, etc. that do not contain the 063 * extends or implements keyword or are not anonymous classes. This means it 064 * only checks methods overridden from {@code java.lang.Object} 065 * 066 * <b>Java 5 Compatibility mode severely limits this check. It is recommended to 067 * only use it on Java 5 source</b> 068 * </p> 069 * 070 * <pre> 071 * <module name="MissingOverride"> 072 * <property name="javaFiveCompatibility" 073 * value="true"/> 074 * </module> 075 * </pre> 076 * 077 * @author Travis Schneeberger 078 */ 079@StatelessCheck 080public final class MissingOverrideCheck extends AbstractCheck { 081 082 /** 083 * A key is pointing to the warning message text in "messages.properties" 084 * file. 085 */ 086 public static final String MSG_KEY_TAG_NOT_VALID_ON = "tag.not.valid.on"; 087 088 /** 089 * A key is pointing to the warning message text in "messages.properties" 090 * file. 091 */ 092 public static final String MSG_KEY_ANNOTATION_MISSING_OVERRIDE = 093 "annotation.missing.override"; 094 095 /** {@link Override Override} annotation name. */ 096 private static final String OVERRIDE = "Override"; 097 098 /** Fully-qualified {@link Override Override} annotation name. */ 099 private static final String FQ_OVERRIDE = "java.lang." + OVERRIDE; 100 101 /** Compiled regexp to match Javadoc tags with no argument and {}. */ 102 private static final Pattern MATCH_INHERIT_DOC = 103 CommonUtils.createPattern("\\{\\s*@(inheritDoc)\\s*\\}"); 104 105 /** 106 * Java 5 compatibility option. 107 * @see #setJavaFiveCompatibility(boolean) 108 */ 109 private boolean javaFiveCompatibility; 110 111 /** 112 * Sets Java 5 compatibility mode. 113 * 114 * <p> 115 * In Java 5, this check could flag code that is not valid for the Override 116 * annotation even though it is a proper override. See the class 117 * documentation for more information. 118 * </p> 119 * 120 * <p> 121 * Set this to true to turn on Java 5 compatibility mode. Set this to 122 * false to turn off Java 5 compatibility mode. 123 * </p> 124 * 125 * @param compatibility compatibility or not 126 */ 127 public void setJavaFiveCompatibility(final boolean compatibility) { 128 javaFiveCompatibility = compatibility; 129 } 130 131 @Override 132 public int[] getDefaultTokens() { 133 return getRequiredTokens(); 134 } 135 136 @Override 137 public int[] getAcceptableTokens() { 138 return getRequiredTokens(); 139 } 140 141 @Override 142 public int[] getRequiredTokens() { 143 return new int[] 144 {TokenTypes.METHOD_DEF, }; 145 } 146 147 // -@cs[CyclomaticComplexity] Too complex to break apart. 148 @Override 149 public void visitToken(final DetailAST ast) { 150 final TextBlock javadoc = 151 getFileContents().getJavadocBefore(ast.getLineNo()); 152 153 final boolean containsTag = containsJavadocTag(javadoc); 154 if (containsTag && !JavadocTagInfo.INHERIT_DOC.isValidOn(ast)) { 155 log(ast.getLineNo(), MSG_KEY_TAG_NOT_VALID_ON, 156 JavadocTagInfo.INHERIT_DOC.getText()); 157 } 158 else { 159 boolean check = true; 160 161 if (javaFiveCompatibility) { 162 final DetailAST defOrNew = ast.getParent().getParent(); 163 164 if (defOrNew.findFirstToken(TokenTypes.EXTENDS_CLAUSE) != null 165 || defOrNew.findFirstToken(TokenTypes.IMPLEMENTS_CLAUSE) != null 166 || defOrNew.getType() == TokenTypes.LITERAL_NEW) { 167 check = false; 168 } 169 } 170 171 if (check 172 && containsTag 173 && !AnnotationUtility.containsAnnotation(ast, OVERRIDE) 174 && !AnnotationUtility.containsAnnotation(ast, FQ_OVERRIDE)) { 175 log(ast.getLineNo(), MSG_KEY_ANNOTATION_MISSING_OVERRIDE); 176 } 177 } 178 } 179 180 /** 181 * Checks to see if the text block contains a inheritDoc tag. 182 * 183 * @param javadoc the javadoc of the AST 184 * @return true if contains the tag 185 */ 186 private static boolean containsJavadocTag(final TextBlock javadoc) { 187 boolean javadocTag = false; 188 189 if (javadoc != null) { 190 final String[] lines = javadoc.getText(); 191 192 for (final String line : lines) { 193 final Matcher matchInheritDoc = 194 MATCH_INHERIT_DOC.matcher(line); 195 196 if (matchInheritDoc.find()) { 197 javadocTag = true; 198 break; 199 } 200 } 201 } 202 return javadocTag; 203 } 204 205}