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