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