001/////////////////////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code and other text files for adherence to a set of rules. 003// Copyright (C) 2001-2026 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 if (containsTag 136 && !AnnotationUtil.hasOverrideAnnotation(ast) 137 && (!javaFiveCompatibility || doesOverrideOnlyObjectMethods(ast))) { 138 log(ast, MSG_KEY_ANNOTATION_MISSING_OVERRIDE); 139 } 140 } 141 142 /** 143 * Checks whether the method's enclosing type can only be overriding methods 144 * declared in {@code java.lang.Object}. This is the case when the enclosing type 145 * does not extend or implement anything and is not an anonymous class. A top-level 146 * method in a compact source file has no enclosing type node ({@code defOrNew} is 147 * null); its implicit class satisfies this condition as well. 148 * 149 * @param ast method AST node 150 * @return true if the method's enclosing type can only override 151 * {@code java.lang.Object} methods 152 */ 153 private static boolean doesOverrideOnlyObjectMethods(DetailAST ast) { 154 final DetailAST defOrNew = ast.getParent().getParent(); 155 return defOrNew == null 156 || defOrNew.findFirstToken(TokenTypes.EXTENDS_CLAUSE) == null 157 && defOrNew.findFirstToken(TokenTypes.IMPLEMENTS_CLAUSE) == null 158 && defOrNew.getType() != TokenTypes.LITERAL_NEW; 159 } 160 161 /** 162 * Checks to see if the ast contains a inheritDoc tag. 163 * 164 * @param ast method AST node 165 * @return true if contains the tag 166 */ 167 private static boolean containsInheritDocTag(DetailAST ast) { 168 final DetailAST modifiers = ast.getFirstChild(); 169 final DetailAST startNode; 170 if (modifiers.hasChildren()) { 171 startNode = Optional.ofNullable(ast.getFirstChild() 172 .findFirstToken(TokenTypes.ANNOTATION)) 173 .orElse(modifiers); 174 } 175 else { 176 startNode = ast.findFirstToken(TokenTypes.TYPE); 177 } 178 final Optional<String> javadoc = 179 Stream.iterate(startNode.getLastChild(), Objects::nonNull, 180 DetailAST::getPreviousSibling) 181 .filter(node -> node.getType() == TokenTypes.BLOCK_COMMENT_BEGIN) 182 .map(DetailAST::getFirstChild) 183 .map(DetailAST::getText) 184 .filter(JavadocUtil::isJavadocComment) 185 .findFirst(); 186 return javadoc.isPresent() 187 && MATCH_INHERIT_DOC.matcher(javadoc.orElseThrow()).find(); 188 } 189 190}