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 }