1   
2   
3   
4   
5   
6   
7   
8   
9   
10  
11  
12  
13  
14  
15  
16  
17  
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  
33  
34  
35  
36  
37  @FileStatefulCheck
38  public abstract class AbstractSuperCheck
39          extends AbstractCheck {
40  
41      
42  
43  
44  
45      public static final String MSG_KEY = "missing.super.call";
46  
47      
48      private final Deque<MethodNode> methodStack = new LinkedList<>();
49  
50      
51  
52  
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  
92  
93  
94  
95  
96      private boolean isSuperCall(DetailAST literalSuperAst) {
97          boolean superCall = false;
98  
99          if (literalSuperAst.getType() == TokenTypes.LITERAL_SUPER) {
100             
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 
113 
114 
115 
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 
134 
135 
136 
137     private static boolean hasArguments(DetailAST methodCallDotAst) {
138         final DetailAST argumentsList = methodCallDotAst.getNextSibling();
139         return argumentsList.getChildCount() > 0;
140     }
141 
142     
143 
144 
145 
146 
147     private boolean isSameNameMethod(DetailAST ast) {
148         DetailAST sibling = ast.getNextSibling();
149         
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 
173 
174 
175 
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 
197 
198 
199     private static class MethodNode {
200 
201         
202         private final DetailAST method;
203 
204         
205         private boolean callingSuper;
206 
207         
208 
209 
210 
211          MethodNode(DetailAST ast) {
212             method = ast;
213             callingSuper = false;
214         }
215 
216         
217 
218 
219         public void setCallingSuper() {
220             callingSuper = true;
221         }
222 
223         
224 
225 
226 
227 
228         public boolean isCallingSuper() {
229             return callingSuper;
230         }
231 
232         
233 
234 
235 
236         public DetailAST getMethod() {
237             return method;
238         }
239 
240     }
241 
242 }