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