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.ArrayDeque;
23 import java.util.Deque;
24 import java.util.Optional;
25
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.TokenUtil;
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72 @FileStatefulCheck
73 public class UnusedCatchParameterShouldBeUnnamedCheck extends AbstractCheck {
74
75
76
77
78
79 public static final String MSG_UNUSED_CATCH_PARAMETER = "unused.catch.parameter";
80
81
82
83
84 private static final int[] INVALID_CATCH_PARAM_IDENT_PARENTS = {
85 TokenTypes.DOT,
86 TokenTypes.LITERAL_NEW,
87 TokenTypes.METHOD_CALL,
88 TokenTypes.TYPE,
89 };
90
91
92
93
94 private final Deque<CatchParameterDetails> catchParameters = new ArrayDeque<>();
95
96 @Override
97 public int[] getDefaultTokens() {
98 return getRequiredTokens();
99 }
100
101 @Override
102 public int[] getAcceptableTokens() {
103 return getRequiredTokens();
104 }
105
106 @Override
107 public int[] getRequiredTokens() {
108 return new int[] {
109 TokenTypes.LITERAL_CATCH,
110 TokenTypes.IDENT,
111 };
112 }
113
114 @Override
115 public void beginTree(DetailAST rootAST) {
116 catchParameters.clear();
117 }
118
119 @Override
120 public void visitToken(DetailAST ast) {
121 if (ast.getType() == TokenTypes.LITERAL_CATCH) {
122 final CatchParameterDetails catchParameter = new CatchParameterDetails(ast);
123 catchParameters.push(catchParameter);
124 }
125 else if (isCatchParameterIdentifierCandidate(ast) && !isLeftHandOfAssignment(ast)) {
126
127 catchParameters.stream()
128 .filter(parameter -> parameter.getName().equals(ast.getText()))
129 .findFirst()
130 .ifPresent(CatchParameterDetails::registerAsUsed);
131 }
132 }
133
134 @Override
135 public void leaveToken(DetailAST ast) {
136 if (ast.getType() == TokenTypes.LITERAL_CATCH) {
137 final Optional<CatchParameterDetails> unusedCatchParameter =
138 Optional.ofNullable(catchParameters.peek())
139 .filter(parameter -> !parameter.isUsed())
140 .filter(parameter -> !"_".equals(parameter.getName()));
141
142 unusedCatchParameter.ifPresent(parameter -> {
143 log(parameter.getParameterDefinition(),
144 MSG_UNUSED_CATCH_PARAMETER,
145 parameter.getName());
146 });
147 catchParameters.pop();
148 }
149 }
150
151
152
153
154
155
156
157
158 private static boolean isCatchParameterIdentifierCandidate(DetailAST identifierAst) {
159
160 final boolean isCatchParameterDeclaration =
161 identifierAst.getParent().getParent().getType() == TokenTypes.LITERAL_CATCH;
162
163 final boolean hasValidParentToken =
164 !TokenUtil.isOfType(identifierAst.getParent(), INVALID_CATCH_PARAM_IDENT_PARENTS);
165
166 final boolean isMethodInvocation = isMethodInvocation(identifierAst);
167
168 return !isCatchParameterDeclaration && (hasValidParentToken || isMethodInvocation);
169 }
170
171
172
173
174
175
176
177
178
179 private static boolean isMethodInvocation(DetailAST identAst) {
180 final DetailAST parent = identAst.getParent();
181 return parent.getType() == TokenTypes.DOT
182 && identAst.equals(parent.getFirstChild());
183 }
184
185
186
187
188
189
190
191 private static boolean isLeftHandOfAssignment(DetailAST identAst) {
192 final DetailAST parent = identAst.getParent();
193 return parent.getType() == TokenTypes.ASSIGN
194 && !identAst.equals(parent.getLastChild());
195 }
196
197
198
199
200 private static final class CatchParameterDetails {
201
202
203
204
205 private final String name;
206
207
208
209
210 private final DetailAST parameterDefinition;
211
212
213
214
215 private boolean used;
216
217
218
219
220
221
222 private CatchParameterDetails(DetailAST enclosingCatchClause) {
223 parameterDefinition =
224 enclosingCatchClause.findFirstToken(TokenTypes.PARAMETER_DEF);
225 name = parameterDefinition.findFirstToken(TokenTypes.IDENT).getText();
226 }
227
228
229
230
231 private void registerAsUsed() {
232 used = true;
233 }
234
235
236
237
238
239
240 private String getName() {
241 return name;
242 }
243
244
245
246
247
248
249 private boolean isUsed() {
250 return used;
251 }
252
253
254
255
256
257
258
259 private DetailAST getParameterDefinition() {
260 return parameterDefinition;
261 }
262 }
263 }