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> 076 * To configure the check for the {@code javaFiveCompatibility} mode: 077 * </p> 078 * <pre> 079 * <module name="MissingOverride"> 080 * <property name="javaFiveCompatibility" 081 * value="true"/> 082 * </module> 083 * </pre> 084 * 085 * @since 5.0 086 */ 087@StatelessCheck 088public final class MissingOverrideCheck extends AbstractCheck { 089 090 /** 091 * A key is pointing to the warning message text in "messages.properties" 092 * file. 093 */ 094 public static final String MSG_KEY_TAG_NOT_VALID_ON = "tag.not.valid.on"; 095 096 /** 097 * A key is pointing to the warning message text in "messages.properties" 098 * file. 099 */ 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}