1 ///////////////////////////////////////////////////////////////////////////////////////////////
2 // checkstyle: Checks Java source code and other text files for adherence to a set of rules.
3 // Copyright (C) 2001-2026 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.coding;
21
22 import java.util.ArrayDeque;
23 import java.util.Deque;
24
25 import com.puppycrawl.tools.checkstyle.FileStatefulCheck;
26 import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
27 import com.puppycrawl.tools.checkstyle.api.DetailAST;
28 import com.puppycrawl.tools.checkstyle.api.TokenTypes;
29 import com.puppycrawl.tools.checkstyle.utils.ScopeUtil;
30
31 /**
32 * <div>
33 * Abstract class for checking that an overriding method with no parameters
34 * invokes the super method.
35 * </div>
36 */
37 @FileStatefulCheck
38 public abstract class AbstractSuperCheck
39 extends AbstractCheck {
40
41 /**
42 * A key is pointing to the warning message text in "messages.properties"
43 * file.
44 */
45 public static final String MSG_KEY = "missing.super.call";
46
47 /** Stack of methods. */
48 private final Deque<MethodNode> methodStack = new ArrayDeque<>();
49
50 /**
51 * Returns the name of the overriding method.
52 *
53 * @return the name of the overriding method.
54 */
55 protected abstract String getMethodName();
56
57 @Override
58 public int[] getAcceptableTokens() {
59 return getRequiredTokens();
60 }
61
62 @Override
63 public int[] getDefaultTokens() {
64 return getRequiredTokens();
65 }
66
67 @Override
68 public int[] getRequiredTokens() {
69 return new int[] {
70 TokenTypes.METHOD_DEF,
71 TokenTypes.LITERAL_SUPER,
72 };
73 }
74
75 @Override
76 public void beginTree(DetailAST rootAST) {
77 methodStack.clear();
78 }
79
80 @Override
81 public void visitToken(DetailAST ast) {
82 if (isOverridingMethod(ast)) {
83 methodStack.add(new MethodNode(ast));
84 }
85 else if (isSuperCall(ast)) {
86 final MethodNode methodNode = methodStack.getLast();
87 methodNode.setCallingSuper();
88 }
89 }
90
91 /**
92 * Determines whether a 'super' literal is a call to the super method
93 * for this check.
94 *
95 * @param literalSuperAst the AST node of a 'super' literal.
96 * @return true if ast is a call to the super method for this check.
97 */
98 private boolean isSuperCall(DetailAST literalSuperAst) {
99 boolean superCall = false;
100
101 if (!isSameNameMethod(literalSuperAst)) {
102 final DetailAST parent = literalSuperAst.getParent();
103 if (parent.getType() == TokenTypes.METHOD_REF
104 || !hasArguments(parent)) {
105 superCall = isSuperCallInOverridingMethod(parent);
106 }
107 }
108 return superCall;
109 }
110
111 /**
112 * Determines whether a super call in overriding method.
113 *
114 * @param ast The AST node of a 'dot operator' in 'super' call.
115 * @return true if super call in overriding method.
116 */
117 private boolean isSuperCallInOverridingMethod(DetailAST ast) {
118 boolean inOverridingMethod = false;
119 DetailAST dotAst = ast;
120
121 while (dotAst.getType() != TokenTypes.CTOR_DEF
122 && dotAst.getType() != TokenTypes.INSTANCE_INIT) {
123 if (dotAst.getType() == TokenTypes.METHOD_DEF) {
124 inOverridingMethod = isOverridingMethod(dotAst);
125 break;
126 }
127 dotAst = dotAst.getParent();
128 }
129 return inOverridingMethod;
130 }
131
132 /**
133 * Does method have any arguments.
134 *
135 * @param methodCallDotAst DOT DetailAST
136 * @return true if any parameters found
137 */
138 private static boolean hasArguments(DetailAST methodCallDotAst) {
139 final DetailAST argumentsList = methodCallDotAst.getNextSibling();
140 return argumentsList.hasChildren();
141 }
142
143 /**
144 * Is same name of method.
145 *
146 * @param ast method AST
147 * @return true if method name is the same
148 */
149 private boolean isSameNameMethod(DetailAST ast) {
150 DetailAST sibling = ast.getNextSibling();
151 // ignore type parameters
152 if (sibling != null
153 && sibling.getType() == TokenTypes.TYPE_ARGUMENTS) {
154 sibling = sibling.getNextSibling();
155 }
156 return sibling == null || !getMethodName().equals(sibling.getText());
157 }
158
159 @Override
160 public void leaveToken(DetailAST ast) {
161 if (isOverridingMethod(ast)) {
162 final MethodNode methodNode =
163 methodStack.removeLast();
164 if (!methodNode.isCallingSuper()) {
165 final DetailAST methodAST = methodNode.getMethod();
166 final DetailAST nameAST =
167 methodAST.findFirstToken(TokenTypes.IDENT);
168 log(nameAST, MSG_KEY, nameAST.getText());
169 }
170 }
171 }
172
173 /**
174 * Determines whether an AST is a method definition for this check,
175 * without any parameters.
176 *
177 * @param ast the method definition AST.
178 * @return true if the method of ast is a method for this check.
179 */
180 private boolean isOverridingMethod(DetailAST ast) {
181 boolean overridingMethod = false;
182
183 if (ast.getType() == TokenTypes.METHOD_DEF
184 && !ScopeUtil.isInInterfaceOrAnnotationBlock(ast)) {
185 final DetailAST nameAST = ast.findFirstToken(TokenTypes.IDENT);
186 final String name = nameAST.getText();
187 final DetailAST modifiersAST = ast.findFirstToken(TokenTypes.MODIFIERS);
188
189 if (getMethodName().equals(name)
190 && modifiersAST.findFirstToken(TokenTypes.LITERAL_NATIVE) == null) {
191 final DetailAST params = ast.findFirstToken(TokenTypes.PARAMETERS);
192 overridingMethod = !params.hasChildren();
193 }
194 }
195 return overridingMethod;
196 }
197
198 /**
199 * Stack node for a method definition and a record of
200 * whether the method has a call to the super method.
201 */
202 private static final class MethodNode {
203
204 /** Method definition. */
205 private final DetailAST method;
206
207 /** True if the overriding method calls the super method. */
208 private boolean callingSuper;
209
210 /**
211 * Constructs a stack node for a method definition.
212 *
213 * @param ast AST for the method definition.
214 */
215 private MethodNode(DetailAST ast) {
216 method = ast;
217 }
218
219 /**
220 * Records that the overriding method has a call to the super method.
221 */
222 /* package */ void setCallingSuper() {
223 callingSuper = true;
224 }
225
226 /**
227 * Determines whether the overriding method has a call to the super
228 * method.
229 *
230 * @return true if the overriding method has a call to the super method.
231 */
232 /* package */ boolean isCallingSuper() {
233 return callingSuper;
234 }
235
236 /**
237 * Returns the overriding method definition AST.
238 *
239 * @return the overriding method definition AST.
240 */
241 /* package */ DetailAST getMethod() {
242 return method;
243 }
244
245 }
246
247 }