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
55 protected abstract String getMethodName();
56
57 @Override
58 public int[] getAcceptableTokens() {
59 return getRequiredTokens();
60 }
61
62 @Override
63 public int[] getDefaultTokens() {
64 return getRequiredTokens();
65 }
66
67 @Override
68 public int[] getRequiredTokens() {
69 return new int[] {
70 TokenTypes.METHOD_DEF,
71 TokenTypes.LITERAL_SUPER,
72 };
73 }
74
75 @Override
76 public void beginTree(DetailAST rootAST) {
77 methodStack.clear();
78 }
79
80 @Override
81 public void visitToken(DetailAST ast) {
82 if (isOverridingMethod(ast)) {
83 methodStack.add(new MethodNode(ast));
84 }
85 else if (isSuperCall(ast)) {
86 final MethodNode methodNode = methodStack.getLast();
87 methodNode.setCallingSuper();
88 }
89 }
90
91
92
93
94
95
96
97
98 private boolean isSuperCall(DetailAST literalSuperAst) {
99 boolean superCall = false;
100
101 if (!isSameNameMethod(literalSuperAst)) {
102 final DetailAST parent = literalSuperAst.getParent();
103 if (parent.getType() == TokenTypes.METHOD_REF
104 || !hasArguments(parent)) {
105 superCall = isSuperCallInOverridingMethod(parent);
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
138 private static boolean hasArguments(DetailAST methodCallDotAst) {
139 final DetailAST argumentsList = methodCallDotAst.getNextSibling();
140 return argumentsList.hasChildren();
141 }
142
143
144
145
146
147
148
149 private boolean isSameNameMethod(DetailAST ast) {
150 DetailAST sibling = ast.getNextSibling();
151
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, MSG_KEY, nameAST.getText());
169 }
170 }
171 }
172
173
174
175
176
177
178
179
180 private boolean isOverridingMethod(DetailAST ast) {
181 boolean overridingMethod = false;
182
183 if (ast.getType() == TokenTypes.METHOD_DEF
184 && !ScopeUtil.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.hasChildren();
193 }
194 }
195 return overridingMethod;
196 }
197
198
199
200
201
202 private static final class MethodNode {
203
204
205 private final DetailAST method;
206
207
208 private boolean callingSuper;
209
210
211
212
213
214
215 private MethodNode(DetailAST ast) {
216 method = ast;
217 }
218
219
220
221
222 public void setCallingSuper() {
223 callingSuper = true;
224 }
225
226
227
228
229
230
231
232 public boolean isCallingSuper() {
233 return callingSuper;
234 }
235
236
237
238
239
240
241 public DetailAST getMethod() {
242 return method;
243 }
244
245 }
246
247 }