1   ////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code for adherence to a set of rules.
3   // Copyright (C) 2001-2019 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.Deque;
23  import java.util.LinkedList;
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   * <p>
33   * Abstract class for checking that an overriding method with no parameters
34   * invokes the super method.
35   * </p>
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 LinkedList<>();
49  
50      /**
51       * Returns the name of the overriding method.
52       * @return the name of the overriding method.
53       */
54      protected abstract String getMethodName();
55  
56      @Override
57      public int[] getAcceptableTokens() {
58          return getRequiredTokens();
59      }
60  
61      @Override
62      public int[] getDefaultTokens() {
63          return getRequiredTokens();
64      }
65  
66      @Override
67      public int[] getRequiredTokens() {
68          return new int[] {
69              TokenTypes.METHOD_DEF,
70              TokenTypes.LITERAL_SUPER,
71          };
72      }
73  
74      @Override
75      public void beginTree(DetailAST rootAST) {
76          methodStack.clear();
77      }
78  
79      @Override
80      public void visitToken(DetailAST ast) {
81          if (isOverridingMethod(ast)) {
82              methodStack.add(new MethodNode(ast));
83          }
84          else if (isSuperCall(ast)) {
85              final MethodNode methodNode = methodStack.getLast();
86              methodNode.setCallingSuper();
87          }
88      }
89  
90      /**
91       * Determines whether a 'super' literal is a call to the super method
92       * for this check.
93       * @param literalSuperAst the AST node of a 'super' literal.
94       * @return true if ast is a call to the super method for this check.
95       */
96      private boolean isSuperCall(DetailAST literalSuperAst) {
97          boolean superCall = false;
98  
99          if (literalSuperAst.getType() == TokenTypes.LITERAL_SUPER) {
100             // dot operator?
101             final DetailAST dotAst = literalSuperAst.getParent();
102 
103             if (!isSameNameMethod(literalSuperAst)
104                 && !hasArguments(dotAst)) {
105                 superCall = isSuperCallInOverridingMethod(dotAst);
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      * @param methodCallDotAst DOT DetailAST
135      * @return true if any parameters found
136      */
137     private static boolean hasArguments(DetailAST methodCallDotAst) {
138         final DetailAST argumentsList = methodCallDotAst.getNextSibling();
139         return argumentsList.getChildCount() > 0;
140     }
141 
142     /**
143      * Is same name of method.
144      * @param ast method AST
145      * @return true if method name is the same
146      */
147     private boolean isSameNameMethod(DetailAST ast) {
148         DetailAST sibling = ast.getNextSibling();
149         // ignore type parameters
150         if (sibling != null
151             && sibling.getType() == TokenTypes.TYPE_ARGUMENTS) {
152             sibling = sibling.getNextSibling();
153         }
154         return sibling == null || !getMethodName().equals(sibling.getText());
155     }
156 
157     @Override
158     public void leaveToken(DetailAST ast) {
159         if (isOverridingMethod(ast)) {
160             final MethodNode methodNode =
161                 methodStack.removeLast();
162             if (!methodNode.isCallingSuper()) {
163                 final DetailAST methodAST = methodNode.getMethod();
164                 final DetailAST nameAST =
165                     methodAST.findFirstToken(TokenTypes.IDENT);
166                 log(nameAST, MSG_KEY, nameAST.getText());
167             }
168         }
169     }
170 
171     /**
172      * Determines whether an AST is a method definition for this check,
173      * with 0 parameters.
174      * @param ast the method definition AST.
175      * @return true if the method of ast is a method for this check.
176      */
177     private boolean isOverridingMethod(DetailAST ast) {
178         boolean overridingMethod = false;
179 
180         if (ast.getType() == TokenTypes.METHOD_DEF
181                 && !ScopeUtil.isInInterfaceOrAnnotationBlock(ast)) {
182             final DetailAST nameAST = ast.findFirstToken(TokenTypes.IDENT);
183             final String name = nameAST.getText();
184             final DetailAST modifiersAST = ast.findFirstToken(TokenTypes.MODIFIERS);
185 
186             if (getMethodName().equals(name)
187                     && modifiersAST.findFirstToken(TokenTypes.LITERAL_NATIVE) == null) {
188                 final DetailAST params = ast.findFirstToken(TokenTypes.PARAMETERS);
189                 overridingMethod = params.getChildCount() == 0;
190             }
191         }
192         return overridingMethod;
193     }
194 
195     /**
196      * Stack node for a method definition and a record of
197      * whether the method has a call to the super method.
198      */
199     private static class MethodNode {
200 
201         /** Method definition. */
202         private final DetailAST method;
203 
204         /** True if the overriding method calls the super method. */
205         private boolean callingSuper;
206 
207         /**
208          * Constructs a stack node for a method definition.
209          * @param ast AST for the method definition.
210          */
211         /* package */ MethodNode(DetailAST ast) {
212             method = ast;
213             callingSuper = false;
214         }
215 
216         /**
217          * Records that the overriding method has a call to the super method.
218          */
219         public void setCallingSuper() {
220             callingSuper = true;
221         }
222 
223         /**
224          * Determines whether the overriding method has a call to the super
225          * method.
226          * @return true if the overriding method has a call to the super method.
227          */
228         public boolean isCallingSuper() {
229             return callingSuper;
230         }
231 
232         /**
233          * Returns the overriding method definition AST.
234          * @return the overriding method definition AST.
235          */
236         public DetailAST getMethod() {
237             return method;
238         }
239 
240     }
241 
242 }