View Javadoc
1   ////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code for adherence to a set of rules.
3   // Copyright (C) 2001-2018 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             if (dotAst.getType() == TokenTypes.METHOD_DEF) {
126                 inOverridingMethod = isOverridingMethod(dotAst);
127                 break;
128             }
129             dotAst = dotAst.getParent();
130         }
131         return inOverridingMethod;
132     }
133 
134     /**
135      * Does method have any arguments.
136      * @param methodCallDotAst DOT DetailAST
137      * @return true if any parameters found
138      */
139     private static boolean hasArguments(DetailAST methodCallDotAst) {
140         final DetailAST argumentsList = methodCallDotAst.getNextSibling();
141         return argumentsList.getChildCount() > 0;
142     }
143 
144     /**
145      * Is same name of method.
146      * @param ast method AST
147      * @return true if method name is the same
148      */
149     private boolean isSameNameMethod(DetailAST ast) {
150         AST 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.getLineNo(), nameAST.getColumnNo(),
169                     MSG_KEY, nameAST.getText());
170             }
171         }
172     }
173 
174     /**
175      * Determines whether an AST is a method definition for this check,
176      * with 0 parameters.
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                 && !ScopeUtils.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.getChildCount() == 0;
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      * @author Rick Giles
202      */
203     private static class MethodNode {
204 
205         /** Method definition. */
206         private final DetailAST method;
207 
208         /** True if the overriding method calls the super method. */
209         private boolean callingSuper;
210 
211         /**
212          * Constructs a stack node for a method definition.
213          * @param ast AST for the method definition.
214          */
215         MethodNode(DetailAST ast) {
216             method = ast;
217             callingSuper = false;
218         }
219 
220         /**
221          * Records that the overriding method has a call to the super method.
222          */
223         public void setCallingSuper() {
224             callingSuper = true;
225         }
226 
227         /**
228          * Determines whether the overriding method has a call to the super
229          * method.
230          * @return true if the overriding method has a call to the super method.
231          */
232         public boolean isCallingSuper() {
233             return callingSuper;
234         }
235 
236         /**
237          * Returns the overriding method definition AST.
238          * @return the overriding method definition AST.
239          */
240         public DetailAST getMethod() {
241             return method;
242         }
243 
244     }
245 
246 }