001/////////////////////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code and other text files for adherence to a set of rules. 003// Copyright (C) 2001-2025 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 * 070 * @since 5.0 071 */ 072@StatelessCheck 073public final class MissingOverrideCheck extends AbstractCheck { 074 075 /** 076 * A key is pointing to the warning message text in "messages.properties" 077 * file. 078 */ 079 public static final String MSG_KEY_TAG_NOT_VALID_ON = "tag.not.valid.on"; 080 081 /** 082 * A key is pointing to the warning message text in "messages.properties" 083 * file. 084 */ 085 public static final String MSG_KEY_ANNOTATION_MISSING_OVERRIDE = 086 "annotation.missing.override"; 087 088 /** Compiled regexp to match Javadoc tags with no argument and {}. */ 089 private static final Pattern MATCH_INHERIT_DOC = 090 CommonUtil.createPattern("\\{\\s*@(inheritDoc)\\s*\\}"); 091 092 /** 093 * Enable java 5 compatibility mode. 094 */ 095 private boolean javaFiveCompatibility; 096 097 /** 098 * Setter to enable java 5 compatibility mode. 099 * 100 * @param compatibility compatibility or not 101 * @since 5.0 102 */ 103 public void setJavaFiveCompatibility(final boolean compatibility) { 104 javaFiveCompatibility = compatibility; 105 } 106 107 @Override 108 public int[] getDefaultTokens() { 109 return getRequiredTokens(); 110 } 111 112 @Override 113 public int[] getAcceptableTokens() { 114 return getRequiredTokens(); 115 } 116 117 @Override 118 public boolean isCommentNodesRequired() { 119 return true; 120 } 121 122 @Override 123 public int[] getRequiredTokens() { 124 return new int[] 125 {TokenTypes.METHOD_DEF, }; 126 } 127 128 @Override 129 public void visitToken(final DetailAST ast) { 130 final boolean containsTag = containsInheritDocTag(ast); 131 if (containsTag && !JavadocTagInfo.INHERIT_DOC.isValidOn(ast)) { 132 log(ast, MSG_KEY_TAG_NOT_VALID_ON, 133 JavadocTagInfo.INHERIT_DOC.getText()); 134 } 135 else { 136 boolean check = true; 137 138 if (javaFiveCompatibility) { 139 final DetailAST defOrNew = ast.getParent().getParent(); 140 141 if (defOrNew.findFirstToken(TokenTypes.EXTENDS_CLAUSE) != null 142 || defOrNew.findFirstToken(TokenTypes.IMPLEMENTS_CLAUSE) != null 143 || defOrNew.getType() == TokenTypes.LITERAL_NEW) { 144 check = false; 145 } 146 } 147 148 if (check 149 && containsTag 150 && !AnnotationUtil.hasOverrideAnnotation(ast)) { 151 log(ast, MSG_KEY_ANNOTATION_MISSING_OVERRIDE); 152 } 153 } 154 } 155 156 /** 157 * Checks to see if the ast contains a inheritDoc tag. 158 * 159 * @param ast method AST node 160 * @return true if contains the tag 161 */ 162 private static boolean containsInheritDocTag(DetailAST ast) { 163 final DetailAST modifiers = ast.getFirstChild(); 164 final DetailAST startNode; 165 if (modifiers.hasChildren()) { 166 startNode = Optional.ofNullable(ast.getFirstChild() 167 .findFirstToken(TokenTypes.ANNOTATION)) 168 .orElse(modifiers); 169 } 170 else { 171 startNode = ast.findFirstToken(TokenTypes.TYPE); 172 } 173 final Optional<String> javadoc = 174 Stream.iterate(startNode.getLastChild(), Objects::nonNull, 175 DetailAST::getPreviousSibling) 176 .filter(node -> node.getType() == TokenTypes.BLOCK_COMMENT_BEGIN) 177 .map(DetailAST::getFirstChild) 178 .map(DetailAST::getText) 179 .filter(JavadocUtil::isJavadocComment) 180 .findFirst(); 181 return javadoc.isPresent() 182 && MATCH_INHERIT_DOC.matcher(javadoc.orElseThrow()).find(); 183 } 184 185}