001/////////////////////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code and other text files for adherence to a set of rules. 003// Copyright (C) 2001-2024 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.Objects; 023import java.util.Optional; 024import java.util.regex.Pattern; 025import java.util.stream.Stream; 026 027import com.puppycrawl.tools.checkstyle.StatelessCheck; 028import com.puppycrawl.tools.checkstyle.api.AbstractCheck; 029import com.puppycrawl.tools.checkstyle.api.DetailAST; 030import com.puppycrawl.tools.checkstyle.api.TokenTypes; 031import com.puppycrawl.tools.checkstyle.checks.javadoc.JavadocTagInfo; 032import com.puppycrawl.tools.checkstyle.utils.AnnotationUtil; 033import com.puppycrawl.tools.checkstyle.utils.CommonUtil; 034import com.puppycrawl.tools.checkstyle.utils.JavadocUtil; 035 036/** 037 * <div> 038 * Verifies that the {@code @Override} annotation is present 039 * when the {@code @inheritDoc} javadoc tag is present. 040 * </div> 041 * 042 * <p> 043 * Rationale: The @Override annotation helps 044 * compiler tools ensure that an override is actually occurring. It is 045 * quite easy to accidentally overload a method or hide a static method 046 * and using the @Override annotation points out these problems. 047 * </p> 048 * 049 * <p> 050 * This check will log a violation if using the @inheritDoc tag on a method that 051 * is not valid (ex: private, or static method). 052 * </p> 053 * 054 * <p> 055 * There is a slight difference between the @Override annotation in Java 5 versus 056 * Java 6 and above. In Java 5, any method overridden from an interface cannot 057 * be annotated with @Override. In Java 6 this behavior is allowed. 058 * </p> 059 * 060 * <p> 061 * As a result of the aforementioned difference between Java 5 and Java 6, a 062 * property called {@code javaFiveCompatibility} is available. This 063 * property will only check classes, interfaces, etc. that do not contain the 064 * extends or implements keyword or are not anonymous classes. This means it 065 * only checks methods overridden from {@code java.lang.Object}. 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 * <ul> 070 * <li> 071 * Property {@code javaFiveCompatibility} - Enable java 5 compatibility mode. 072 * Type is {@code boolean}. 073 * Default value is {@code false}. 074 * </li> 075 * </ul> 076 * 077 * <p> 078 * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker} 079 * </p> 080 * 081 * <p> 082 * Violation Message Keys: 083 * </p> 084 * <ul> 085 * <li> 086 * {@code annotation.missing.override} 087 * </li> 088 * <li> 089 * {@code tag.not.valid.on} 090 * </li> 091 * </ul> 092 * 093 * @since 5.0 094 */ 095@StatelessCheck 096public final class MissingOverrideCheck extends AbstractCheck { 097 098 /** 099 * A key is pointing to the warning message text in "messages.properties" 100 * file. 101 */ 102 public static final String MSG_KEY_TAG_NOT_VALID_ON = "tag.not.valid.on"; 103 104 /** 105 * A key is pointing to the warning message text in "messages.properties" 106 * file. 107 */ 108 public static final String MSG_KEY_ANNOTATION_MISSING_OVERRIDE = 109 "annotation.missing.override"; 110 111 /** Compiled regexp to match Javadoc tags with no argument and {}. */ 112 private static final Pattern MATCH_INHERIT_DOC = 113 CommonUtil.createPattern("\\{\\s*@(inheritDoc)\\s*\\}"); 114 115 /** 116 * Enable java 5 compatibility mode. 117 */ 118 private boolean javaFiveCompatibility; 119 120 /** 121 * Setter to enable java 5 compatibility mode. 122 * 123 * @param compatibility compatibility or not 124 * @since 5.0 125 */ 126 public void setJavaFiveCompatibility(final boolean compatibility) { 127 javaFiveCompatibility = compatibility; 128 } 129 130 @Override 131 public int[] getDefaultTokens() { 132 return getRequiredTokens(); 133 } 134 135 @Override 136 public int[] getAcceptableTokens() { 137 return getRequiredTokens(); 138 } 139 140 @Override 141 public boolean isCommentNodesRequired() { 142 return true; 143 } 144 145 @Override 146 public int[] getRequiredTokens() { 147 return new int[] 148 {TokenTypes.METHOD_DEF, }; 149 } 150 151 @Override 152 public void visitToken(final DetailAST ast) { 153 final boolean containsTag = containsInheritDocTag(ast); 154 if (containsTag && !JavadocTagInfo.INHERIT_DOC.isValidOn(ast)) { 155 log(ast, 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 && !AnnotationUtil.hasOverrideAnnotation(ast)) { 174 log(ast, MSG_KEY_ANNOTATION_MISSING_OVERRIDE); 175 } 176 } 177 } 178 179 /** 180 * Checks to see if the ast contains a inheritDoc tag. 181 * 182 * @param ast method AST node 183 * @return true if contains the tag 184 */ 185 private static boolean containsInheritDocTag(DetailAST ast) { 186 final DetailAST modifiers = ast.getFirstChild(); 187 final DetailAST startNode; 188 if (modifiers.hasChildren()) { 189 startNode = Optional.ofNullable(ast.getFirstChild() 190 .findFirstToken(TokenTypes.ANNOTATION)) 191 .orElse(modifiers); 192 } 193 else { 194 startNode = ast.findFirstToken(TokenTypes.TYPE); 195 } 196 final Optional<String> javadoc = 197 Stream.iterate(startNode.getLastChild(), Objects::nonNull, 198 DetailAST::getPreviousSibling) 199 .filter(node -> node.getType() == TokenTypes.BLOCK_COMMENT_BEGIN) 200 .map(DetailAST::getFirstChild) 201 .map(DetailAST::getText) 202 .filter(JavadocUtil::isJavadocComment) 203 .findFirst(); 204 return javadoc.isPresent() 205 && MATCH_INHERIT_DOC.matcher(javadoc.orElseThrow()).find(); 206 } 207 208}