1 ///////////////////////////////////////////////////////////////////////////////////////////////
2 // checkstyle: Checks Java source code and other text files for adherence to a set of rules.
3 // Copyright (C) 2001-2025 the original author or authors.
4 //
5 // This library is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU Lesser General Public
7 // License as published by the Free Software Foundation; either
8 // version 2.1 of the License, or (at your option) any later version.
9 //
10 // This library is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 // Lesser General Public License for more details.
14 //
15 // You should have received a copy of the GNU Lesser General Public
16 // License along with this library; if not, write to the Free Software
17 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 ///////////////////////////////////////////////////////////////////////////////////////////////
19
20 package com.puppycrawl.tools.checkstyle.checks.annotation;
21
22 import java.util.Objects;
23 import java.util.Optional;
24 import java.util.regex.Pattern;
25 import java.util.stream.Stream;
26
27 import com.puppycrawl.tools.checkstyle.StatelessCheck;
28 import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
29 import com.puppycrawl.tools.checkstyle.api.DetailAST;
30 import com.puppycrawl.tools.checkstyle.api.TokenTypes;
31 import com.puppycrawl.tools.checkstyle.checks.javadoc.JavadocTagInfo;
32 import com.puppycrawl.tools.checkstyle.utils.AnnotationUtil;
33 import com.puppycrawl.tools.checkstyle.utils.CommonUtil;
34 import com.puppycrawl.tools.checkstyle.utils.JavadocUtil;
35
36 /**
37 * <div>
38 * Verifies that the {@code @Override} annotation is present
39 * when the {@code @inheritDoc} javadoc tag is present.
40 * </div>
41 *
42 * <p>
43 * Rationale: The @Override annotation helps
44 * compiler tools ensure that an override is actually occurring. It is
45 * quite easy to accidentally overload a method or hide a static method
46 * and using the @Override annotation points out these problems.
47 * </p>
48 *
49 * <p>
50 * This check will log a violation if using the @inheritDoc tag on a method that
51 * is not valid (ex: private, or static method).
52 * </p>
53 *
54 * <p>
55 * There is a slight difference between the @Override annotation in Java 5 versus
56 * Java 6 and above. In Java 5, any method overridden from an interface cannot
57 * be annotated with @Override. In Java 6 this behavior is allowed.
58 * </p>
59 *
60 * <p>
61 * As a result of the aforementioned difference between Java 5 and Java 6, a
62 * property called {@code javaFiveCompatibility} is available. This
63 * property will only check classes, interfaces, etc. that do not contain the
64 * extends or implements keyword or are not anonymous classes. This means it
65 * only checks methods overridden from {@code java.lang.Object}.
66 * <b>Java 5 Compatibility mode severely limits this check. It is recommended to
67 * only use it on Java 5 source.</b>
68 * </p>
69 *
70 * @since 5.0
71 */
72 @StatelessCheck
73 public final class MissingOverrideCheck extends AbstractCheck {
74
75 /**
76 * A key is pointing to the warning message text in "messages.properties"
77 * file.
78 */
79 public static final String MSG_KEY_TAG_NOT_VALID_ON = "tag.not.valid.on";
80
81 /**
82 * A key is pointing to the warning message text in "messages.properties"
83 * file.
84 */
85 public static final String MSG_KEY_ANNOTATION_MISSING_OVERRIDE =
86 "annotation.missing.override";
87
88 /** Compiled regexp to match Javadoc tags with no argument and {}. */
89 private static final Pattern MATCH_INHERIT_DOC =
90 CommonUtil.createPattern("\\{\\s*@(inheritDoc)\\s*\\}");
91
92 /**
93 * Enable java 5 compatibility mode.
94 */
95 private boolean javaFiveCompatibility;
96
97 /**
98 * Setter to enable java 5 compatibility mode.
99 *
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 }