View Javadoc
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       * Creates a new {@code AbstractSuperCheck} instance.
52       */
53      protected AbstractSuperCheck() {
54          // no code by default
55      }
56  
57      /**
58       * Returns the name of the overriding method.
59       *
60       * @return the name of the overriding method.
61       */
62      protected abstract String getMethodName();
63  
64      @Override
65      public int[] getAcceptableTokens() {
66          return getRequiredTokens();
67      }
68  
69      @Override
70      public int[] getDefaultTokens() {
71          return getRequiredTokens();
72      }
73  
74      @Override
75      public int[] getRequiredTokens() {
76          return new int[] {
77              TokenTypes.METHOD_DEF,
78              TokenTypes.LITERAL_SUPER,
79          };
80      }
81  
82      @Override
83      public void beginTree(DetailAST rootAST) {
84          methodStack.clear();
85      }
86  
87      @Override
88      public void visitToken(DetailAST ast) {
89          if (isOverridingMethod(ast)) {
90              methodStack.add(new MethodNode(ast));
91          }
92          else if (isSuperCall(ast)) {
93              final MethodNode methodNode = methodStack.getLast();
94              methodNode.setCallingSuper();
95          }
96      }
97  
98      /**
99       * Determines whether a 'super' literal is a call to the super method
100      * for this check.
101      *
102      * @param literalSuperAst the AST node of a 'super' literal.
103      * @return true if ast is a call to the super method for this check.
104      */
105     private boolean isSuperCall(DetailAST literalSuperAst) {
106         boolean superCall = false;
107 
108         if (!isSameNameMethod(literalSuperAst)) {
109             final DetailAST parent = literalSuperAst.getParent();
110             if (parent.getType() == TokenTypes.METHOD_REF
111                 || !hasArguments(parent)) {
112                 superCall = isSuperCallInOverridingMethod(parent);
113             }
114         }
115         return superCall;
116     }
117 
118     /**
119      * Determines whether a super call in overriding method.
120      *
121      * @param ast The AST node of a 'dot operator' in 'super' call.
122      * @return true if super call in overriding method.
123      */
124     private boolean isSuperCallInOverridingMethod(DetailAST ast) {
125         boolean inOverridingMethod = false;
126         DetailAST dotAst = ast;
127 
128         while (dotAst.getType() != TokenTypes.CTOR_DEF
129                 && dotAst.getType() != TokenTypes.INSTANCE_INIT) {
130             if (dotAst.getType() == TokenTypes.METHOD_DEF) {
131                 inOverridingMethod = isOverridingMethod(dotAst);
132                 break;
133             }
134             dotAst = dotAst.getParent();
135         }
136         return inOverridingMethod;
137     }
138 
139     /**
140      * Does method have any arguments.
141      *
142      * @param methodCallDotAst DOT DetailAST
143      * @return true if any parameters found
144      */
145     private static boolean hasArguments(DetailAST methodCallDotAst) {
146         final DetailAST argumentsList = methodCallDotAst.getNextSibling();
147         return argumentsList.hasChildren();
148     }
149 
150     /**
151      * Is same name of method.
152      *
153      * @param ast method AST
154      * @return true if method name is the same
155      */
156     private boolean isSameNameMethod(DetailAST ast) {
157         DetailAST sibling = ast.getNextSibling();
158         // ignore type parameters
159         if (sibling != null
160             && sibling.getType() == TokenTypes.TYPE_ARGUMENTS) {
161             sibling = sibling.getNextSibling();
162         }
163         return sibling == null || !getMethodName().equals(sibling.getText());
164     }
165 
166     @Override
167     public void leaveToken(DetailAST ast) {
168         if (isOverridingMethod(ast)) {
169             final MethodNode methodNode =
170                 methodStack.removeLast();
171             if (!methodNode.isCallingSuper()) {
172                 final DetailAST methodAST = methodNode.getMethod();
173                 final DetailAST nameAST =
174                     methodAST.findFirstToken(TokenTypes.IDENT);
175                 log(nameAST, MSG_KEY, nameAST.getText());
176             }
177         }
178     }
179 
180     /**
181      * Determines whether an AST is a method definition for this check,
182      * without any parameters.
183      *
184      * @param ast the method definition AST.
185      * @return true if the method of ast is a method for this check.
186      */
187     private boolean isOverridingMethod(DetailAST ast) {
188         boolean overridingMethod = false;
189 
190         if (ast.getType() == TokenTypes.METHOD_DEF
191                 && !ScopeUtil.isInInterfaceOrAnnotationBlock(ast)) {
192             final DetailAST nameAST = ast.findFirstToken(TokenTypes.IDENT);
193             final String name = nameAST.getText();
194             final DetailAST modifiersAST = ast.findFirstToken(TokenTypes.MODIFIERS);
195 
196             if (getMethodName().equals(name)
197                     && modifiersAST.findFirstToken(TokenTypes.LITERAL_NATIVE) == null) {
198                 final DetailAST params = ast.findFirstToken(TokenTypes.PARAMETERS);
199                 overridingMethod = !params.hasChildren();
200             }
201         }
202         return overridingMethod;
203     }
204 
205     /**
206      * Stack node for a method definition and a record of
207      * whether the method has a call to the super method.
208      */
209     private static final class MethodNode {
210 
211         /** Method definition. */
212         private final DetailAST method;
213 
214         /** True if the overriding method calls the super method. */
215         private boolean callingSuper;
216 
217         /**
218          * Constructs a stack node for a method definition.
219          *
220          * @param ast AST for the method definition.
221          */
222         private MethodNode(DetailAST ast) {
223             method = ast;
224         }
225 
226         /**
227          * Records that the overriding method has a call to the super method.
228          */
229         /* package */ void setCallingSuper() {
230             callingSuper = true;
231         }
232 
233         /**
234          * Determines whether the overriding method has a call to the super
235          * method.
236          *
237          * @return true if the overriding method has a call to the super method.
238          */
239         /* package */ boolean isCallingSuper() {
240             return callingSuper;
241         }
242 
243         /**
244          * Returns the overriding method definition AST.
245          *
246          * @return the overriding method definition AST.
247          */
248         /* package */ DetailAST getMethod() {
249             return method;
250         }
251 
252     }
253 
254 }