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 @FileStatefulCheck
76 public class UnusedLambdaParameterShouldBeUnnamedCheck extends AbstractCheck {
77
78
79
80
81
82 public static final String MSG_UNUSED_LAMBDA_PARAMETER = "unused.lambda.parameter";
83
84
85
86
87
88
89 private static final int[] INVALID_LAMBDA_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<LambdaParameterDetails> lambdaParameters = 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.LAMBDA,
115 TokenTypes.IDENT,
116 };
117 }
118
119 @Override
120 public void beginTree(DetailAST rootAST) {
121 lambdaParameters.clear();
122 }
123
124 @Override
125 public void visitToken(DetailAST ast) {
126 if (ast.getType() == TokenTypes.LAMBDA) {
127 final DetailAST parameters = ast.findFirstToken(TokenTypes.PARAMETERS);
128 if (parameters != null) {
129
130 TokenUtil.forEachChild(parameters, TokenTypes.PARAMETER_DEF, parameter -> {
131 final DetailAST identifierAst = parameter.findFirstToken(TokenTypes.IDENT);
132 final LambdaParameterDetails lambdaParameter =
133 new LambdaParameterDetails(ast, identifierAst);
134 lambdaParameters.push(lambdaParameter);
135 });
136 }
137 else if (ast.getChildCount() != 0) {
138
139 final LambdaParameterDetails lambdaParameter =
140 new LambdaParameterDetails(ast, ast.findFirstToken(TokenTypes.IDENT));
141 lambdaParameters.push(lambdaParameter);
142 }
143 }
144 else if (isLambdaParameterIdentifierCandidate(ast) && !isLeftHandOfAssignment(ast)) {
145
146 lambdaParameters.stream()
147 .filter(parameter -> parameter.getName().equals(ast.getText()))
148 .findFirst()
149 .ifPresent(LambdaParameterDetails::registerAsUsed);
150 }
151 }
152
153 @Override
154 public void leaveToken(DetailAST ast) {
155 while (lambdaParameters.peek() != null
156 && ast.equals(lambdaParameters.peek().enclosingLambda)) {
157
158 final Optional<LambdaParameterDetails> unusedLambdaParameter =
159 Optional.ofNullable(lambdaParameters.peek())
160 .filter(parameter -> !parameter.isUsed())
161 .filter(parameter -> !"_".equals(parameter.getName()));
162
163 unusedLambdaParameter.ifPresent(parameter -> {
164 log(parameter.getIdentifierAst(),
165 MSG_UNUSED_LAMBDA_PARAMETER,
166 parameter.getName());
167 });
168 lambdaParameters.pop();
169 }
170 }
171
172
173
174
175
176
177
178
179 private static boolean isLambdaParameterIdentifierCandidate(DetailAST identifierAst) {
180
181 final boolean isLambdaParameterDeclaration =
182 identifierAst.getParent().getType() == TokenTypes.LAMBDA
183 || identifierAst.getParent().getType() == TokenTypes.PARAMETER_DEF;
184
185 return !isLambdaParameterDeclaration
186 && (hasValidParentToken(identifierAst) || isMethodInvocation(identifierAst));
187 }
188
189
190
191
192
193
194
195
196 private static boolean hasValidParentToken(DetailAST identifierAst) {
197 return !TokenUtil.isOfType(identifierAst.getParent(), INVALID_LAMBDA_PARAM_IDENT_PARENTS);
198 }
199
200
201
202
203
204
205
206
207
208 private static boolean isMethodInvocation(DetailAST identAst) {
209 final DetailAST parent = identAst.getParent();
210 return parent.getType() == TokenTypes.DOT
211 && identAst.equals(parent.getFirstChild());
212 }
213
214
215
216
217
218
219
220 private static boolean isLeftHandOfAssignment(DetailAST identAst) {
221 final DetailAST parent = identAst.getParent();
222 return parent.getType() == TokenTypes.ASSIGN
223 && !identAst.equals(parent.getLastChild());
224 }
225
226
227
228
229 private static final class LambdaParameterDetails {
230
231
232
233
234
235 private final DetailAST enclosingLambda;
236
237
238
239
240
241 private final DetailAST identifierAst;
242
243
244
245
246 private boolean used;
247
248
249
250
251
252
253
254 private LambdaParameterDetails(DetailAST enclosingLambda, DetailAST identifierAst) {
255 this.enclosingLambda = enclosingLambda;
256 this.identifierAst = identifierAst;
257 }
258
259
260
261
262 private void registerAsUsed() {
263 used = true;
264 }
265
266
267
268
269
270
271 private String getName() {
272 return identifierAst.getText();
273 }
274
275
276
277
278
279
280
281 private DetailAST getIdentifierAst() {
282 return identifierAst;
283 }
284
285
286
287
288
289
290 private boolean isUsed() {
291 return used;
292 }
293 }
294 }