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