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