View Javadoc
1   ///////////////////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code and other text files for adherence to a set of rules.
3   // Copyright (C) 2001-2026 the original author or authors.
4   //
5   // This library is free software; you can redistribute it and/or
6   // modify it under the terms of the GNU Lesser General Public
7   // License as published by the Free Software Foundation; either
8   // version 2.1 of the License, or (at your option) any later version.
9   //
10  // This library is distributed in the hope that it will be useful,
11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  // Lesser General Public License for more details.
14  //
15  // You should have received a copy of the GNU Lesser General Public
16  // License along with this library; if not, write to the Free Software
17  // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  ///////////////////////////////////////////////////////////////////////////////////////////////
19  
20  package com.puppycrawl.tools.checkstyle.checks.coding;
21  
22  import static com.google.common.truth.Truth.assertWithMessage;
23  import static com.puppycrawl.tools.checkstyle.checks.coding.UnusedLocalVariableCheck.MSG_UNUSED_LOCAL_VARIABLE;
24  import static com.puppycrawl.tools.checkstyle.checks.coding.UnusedLocalVariableCheck.MSG_UNUSED_NAMED_LOCAL_VARIABLE;
25  
26  import java.io.File;
27  import java.util.Collection;
28  import java.util.Map;
29  import java.util.Objects;
30  import java.util.Optional;
31  import java.util.function.Predicate;
32  
33  import org.junit.jupiter.api.Test;
34  
35  import com.puppycrawl.tools.checkstyle.AbstractModuleTestSupport;
36  import com.puppycrawl.tools.checkstyle.JavaParser;
37  import com.puppycrawl.tools.checkstyle.api.DetailAST;
38  import com.puppycrawl.tools.checkstyle.api.TokenTypes;
39  import com.puppycrawl.tools.checkstyle.internal.utils.TestUtil;
40  import com.puppycrawl.tools.checkstyle.utils.CommonUtil;
41  
42  public class UnusedLocalVariableCheckTest extends AbstractModuleTestSupport {
43  
44      @Override
45      public String getPackageLocation() {
46          return "com/puppycrawl/tools/checkstyle/checks/coding/unusedlocalvariable";
47      }
48  
49      @Test
50      public void testGetRequiredTokens() {
51          final UnusedLocalVariableCheck checkObj =
52                  new UnusedLocalVariableCheck();
53          final int[] actual = checkObj.getRequiredTokens();
54          final int[] expected = {
55              TokenTypes.DOT,
56              TokenTypes.VARIABLE_DEF,
57              TokenTypes.IDENT,
58              TokenTypes.SLIST,
59              TokenTypes.LITERAL_FOR,
60              TokenTypes.OBJBLOCK,
61              TokenTypes.CLASS_DEF,
62              TokenTypes.INTERFACE_DEF,
63              TokenTypes.ANNOTATION_DEF,
64              TokenTypes.PACKAGE_DEF,
65              TokenTypes.LITERAL_NEW,
66              TokenTypes.METHOD_DEF,
67              TokenTypes.CTOR_DEF,
68              TokenTypes.STATIC_INIT,
69              TokenTypes.INSTANCE_INIT,
70              TokenTypes.COMPILATION_UNIT,
71              TokenTypes.LAMBDA,
72              TokenTypes.ENUM_DEF,
73              TokenTypes.RECORD_DEF,
74              TokenTypes.COMPACT_CTOR_DEF,
75              TokenTypes.PATTERN_VARIABLE_DEF,
76          };
77          assertWithMessage("Required tokens are invalid")
78                  .that(actual)
79                  .isEqualTo(expected);
80      }
81  
82      @Test
83      public void testGetAcceptableTokens() {
84          final UnusedLocalVariableCheck typeParameterNameCheckObj =
85                  new UnusedLocalVariableCheck();
86          final int[] actual = typeParameterNameCheckObj.getAcceptableTokens();
87          final int[] expected = {
88              TokenTypes.DOT,
89              TokenTypes.VARIABLE_DEF,
90              TokenTypes.IDENT,
91              TokenTypes.SLIST,
92              TokenTypes.LITERAL_FOR,
93              TokenTypes.OBJBLOCK,
94              TokenTypes.CLASS_DEF,
95              TokenTypes.INTERFACE_DEF,
96              TokenTypes.ANNOTATION_DEF,
97              TokenTypes.PACKAGE_DEF,
98              TokenTypes.LITERAL_NEW,
99              TokenTypes.METHOD_DEF,
100             TokenTypes.CTOR_DEF,
101             TokenTypes.STATIC_INIT,
102             TokenTypes.INSTANCE_INIT,
103             TokenTypes.COMPILATION_UNIT,
104             TokenTypes.LAMBDA,
105             TokenTypes.ENUM_DEF,
106             TokenTypes.RECORD_DEF,
107             TokenTypes.COMPACT_CTOR_DEF,
108             TokenTypes.PATTERN_VARIABLE_DEF,
109         };
110         assertWithMessage("Acceptable tokens are invalid")
111                 .that(actual)
112                 .isEqualTo(expected);
113     }
114 
115     @Test
116     public void testUnusedLocalVariable() throws Exception {
117         final String[] expected = {
118             "27:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "sameName"),
119             "28:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "b"),
120             "31:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "testInLambdas"),
121             "33:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "coding"),
122             "34:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE,
123                     "InputUnusedLocalVariable"),
124             "50:13: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "b"),
125             "54:13: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "c"),
126             "65:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "b"),
127             "67:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "c"),
128             "71:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "p"),
129             "81:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "f"),
130             "84:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "foo"),
131             "91:13: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "a"),
132         };
133         verifyWithInlineConfigParser(
134                 getPath("InputUnusedLocalVariable.java"), expected);
135     }
136 
137     @Test
138     public void testUnusedLocalVar2() throws Exception {
139         final String[] expected = {
140             "17:14: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "i"),
141             "19:14: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "j"),
142             "30:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "a"),
143             "31:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "b"),
144             "39:13: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "b"),
145             "40:13: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "Test"),
146             "41:13: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "obj"),
147             "61:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "a"),
148             "76:17: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "b"),
149         };
150         verifyWithInlineConfigParser(
151                 getPath("InputUnusedLocalVariable2.java"),
152                 expected);
153     }
154 
155     @Test
156     public void testUnusedLocalVar3() throws Exception {
157         final String[] expected = {
158             "21:13: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "a"),
159         };
160         verifyWithInlineConfigParser(
161                 getPath("InputUnusedLocalVariable3.java"),
162                 expected);
163     }
164 
165     @Test
166     public void testUnusedLocalVarInAnonInnerClasses() throws Exception {
167         final String[] expected = {
168             "14:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "a"),
169             "15:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "b"),
170             "17:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "obj"),
171             "22:17: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "obj"),
172             "32:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "obj2"),
173             "46:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "m"),
174             "47:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "l"),
175             "59:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "h"),
176             "62:17: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "v"),
177         };
178         verifyWithInlineConfigParser(
179                 getPath("InputUnusedLocalVariableAnonInnerClasses.java"),
180                 expected);
181     }
182 
183     @Test
184     public void testUnusedLocalVarGenericAnonInnerClasses() throws Exception {
185         final String[] expected = {
186             "13:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "l"),
187             "14:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "obj"),
188             "33:13: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "l"),
189             "34:13: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "obj2"),
190             "47:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "s"),
191             "67:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "variable"),
192             "69:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "anotherVar"),
193             "78:47: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "obj2"),
194         };
195         verifyWithInlineConfigParser(
196                 getPath("InputUnusedLocalVariableGenericAnonInnerClasses.java"),
197                 expected);
198     }
199 
200     @Test
201     public void testUnusedLocalVarDepthOfClasses() throws Exception {
202         final String[] expected = {
203             "28:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "r"),
204             "49:21: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "a"),
205             "64:13: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "a"),
206             "94:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "variable"),
207         };
208         verifyWithInlineConfigParser(
209                 getPath("InputUnusedLocalVariableDepthOfClasses.java"),
210                 expected);
211     }
212 
213     @Test
214     public void testUnusedLocalVarNestedClasses() throws Exception {
215         final String[] expected = {
216             "21:13: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "V"),
217             "23:13: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "S"),
218             "24:13: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "Q"),
219             "36:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "variable"),
220             "44:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "anotherVariable"),
221             "67:21: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "s"),
222             "68:21: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "n"),
223         };
224         verifyWithInlineConfigParser(
225                 getPath("InputUnusedLocalVariableNestedClasses.java"),
226                 expected);
227     }
228 
229     @Test
230     public void testUnusedLocalVarNestedClasses2() throws Exception {
231         final String[] expected = {
232             "29:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "q"),
233             "30:51: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "obj"),
234             "46:21: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "b"),
235             "57:21: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "b"),
236             "108:33: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "s"),
237         };
238         verifyWithInlineConfigParser(
239                 getPath("InputUnusedLocalVariableNestedClasses2.java"),
240                 expected);
241     }
242 
243     @Test
244     public void testUnusedLocalVarNestedClasses3() throws Exception {
245         final String[] expected = {
246             "36:17: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "p2"),
247             "54:13: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "o"),
248             "93:13: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "b"),
249             "95:13: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "a"),
250         };
251 
252         verifyWithInlineConfigParser(
253                 getPath("InputUnusedLocalVariableNestedClasses3.java"),
254                 expected);
255     }
256 
257     @Test
258     public void testUnusedLocalVarNestedClasses4() throws Exception {
259         final String[] expected = {
260             "12:5: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "a"),
261             "13:5: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "ab"),
262         };
263 
264         verifyWithInlineConfigParser(
265                 getPath("InputUnusedLocalVariableNestedClasses4.java"),
266                 expected);
267     }
268 
269     @Test
270     public void testUnusedLocalVarNestedClasses5() throws Exception {
271         final String[] expected = {
272             "12:5: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "a"),
273             "13:5: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "ab"),
274             "19:11: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "abc"),
275         };
276 
277         verifyWithInlineConfigParser(
278                 getPath("InputUnusedLocalVariableNestedClasses5.java"),
279                 expected);
280     }
281 
282     @Test
283     public void testUnusedLocalVarNestedClasses6() throws Exception {
284         final String[] expected = {
285             "12:5: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "a"),
286             "13:5: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "ab"),
287         };
288 
289         verifyWithInlineConfigParser(
290                 getPath("InputUnusedLocalVariableNestedClasses6.java"),
291                 expected);
292     }
293 
294     @Test
295     public void testUnusedLocalVarNestedClasses7() throws Exception {
296         final String[] expected = {
297             "10:5: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "a"),
298             "11:5: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "ab"),
299             "16:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "ab"),
300             "23:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "a"),
301             "24:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "ab"),
302             "28:13: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "a"),
303         };
304 
305         verifyWithInlineConfigParser(
306                 getPath("InputUnusedLocalVariableNestedClasses7.java"),
307                 expected);
308     }
309 
310     @Test
311     public void testUnusedLocalVarTestWarningSeverity() throws Exception {
312         final String[] expected = {
313             "14:19: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "p2"),
314         };
315 
316         verifyWithInlineConfigParser(
317                 getPath("InputUnusedLocalVariableTestWarningSeverity.java"),
318                 expected);
319     }
320 
321     @Test
322     public void testUnusedLocalVarEnum() throws Exception {
323         final String[] expected = {
324             "22:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "a"),
325             "50:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "a"),
326             "77:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "a"),
327             "80:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "j"),
328             "92:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "d"),
329         };
330         verifyWithInlineConfigParser(
331                 getPath("InputUnusedLocalVariableEnum.java"),
332                 expected);
333     }
334 
335     @Test
336     public void testUnusedLocalVarLambdas() throws Exception {
337         final String[] expected = {
338             "14:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "hoo"),
339             "19:17: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "j"),
340             "29:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "hoo2"),
341             "30:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "hoo3"),
342             "32:15: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "myComponent"),
343             "34:19: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "myComponent3"),
344             "40:25: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "j"),
345             "52:21: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "j"),
346             "65:17: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "ja"),
347             "73:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "k"),
348         };
349         verifyWithInlineConfigParser(
350                 getPath("InputUnusedLocalVariableLambdaExpression.java"),
351                 expected);
352     }
353 
354     @Test
355     public void testUnusedLocalVariableLocalClasses() throws Exception {
356         final String[] expected = {
357             "14:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "a"),
358             "15:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "ab"),
359         };
360         verifyWithInlineConfigParser(
361                 getPath("InputUnusedLocalVariableLocalClasses.java"),
362                 expected);
363     }
364 
365     @Test
366     public void testUnusedLocalVarRecords() throws Exception {
367         final String[] expected = {
368             "16:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "var1"),
369             "25:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "var1"),
370             "26:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "obj"),
371             "36:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "var2"),
372         };
373         verifyWithInlineConfigParser(
374                 getPath("InputUnusedLocalVariableRecords.java"),
375                 expected);
376     }
377 
378     @Test
379     public void testUnusedLocalVarWithoutPackageStatement() throws Exception {
380         final String[] expected = {
381             "12:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "a"),
382             "24:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "var2"),
383             "45:13: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "var3"),
384         };
385         verifyWithInlineConfigParser(
386                 getNonCompilablePath("InputUnusedLocalVariableWithoutPackageStatement.java"),
387                 expected);
388     }
389 
390     @Test
391     public void testCompactSourceFile() throws Exception {
392         final String[] expected = {
393             "12:5: " + getCheckMessage(MSG_UNUSED_NAMED_LOCAL_VARIABLE, "unused"),
394         };
395         verifyWithInlineConfigParser(
396                 getNonCompilablePath("InputUnusedLocalVariableCompactSourceFile.java"),
397                 expected);
398     }
399 
400     @Test
401     public void testUnusedLocalVariableTernaryAndExpressions() throws Exception {
402         final String[] expected = CommonUtil.EMPTY_STRING_ARRAY;
403         verifyWithInlineConfigParser(
404                 getPath("InputUnusedLocalVariableTernaryAndExpressions.java"),
405                 expected);
406     }
407 
408     @Test
409     public void testUnusedLocalVariableSwitchStatement() throws Exception {
410         final String[] expected = CommonUtil.EMPTY_STRING_ARRAY;
411         verifyWithInlineConfigParser(
412                 getPath("InputUnusedLocalVariableSwitchStatement.java"),
413                 expected);
414     }
415 
416     @Test
417     public void testUnusedLocalVariableSwitchStatement2() throws Exception {
418         final String[] expected = {
419             "59:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "j"),
420         };
421         verifyWithInlineConfigParser(
422                 getPath("InputUnusedLocalVariableSwitchStatement2.java"),
423                 expected);
424     }
425 
426     @Test
427     public void testUnusedLocalVariableSwitchExpression() throws Exception {
428         final String[] expected = {
429             "16:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "line2"),
430         };
431         verifyWithInlineConfigParser(
432                 getPath("InputUnusedLocalVariableSwitchExpression.java"),
433                 expected);
434     }
435 
436     @Test
437     public void testUnusedLocalVariableWithAllowUnnamed() throws Exception {
438         final String[] expected = {
439             "20:13: " + getCheckMessage(MSG_UNUSED_NAMED_LOCAL_VARIABLE, "_x"),
440             "21:13: " + getCheckMessage(MSG_UNUSED_NAMED_LOCAL_VARIABLE, "__"),
441             "35:14: " + getCheckMessage(MSG_UNUSED_NAMED_LOCAL_VARIABLE, "__"),
442             "45:14: " + getCheckMessage(MSG_UNUSED_NAMED_LOCAL_VARIABLE, "__"),
443         };
444         verifyWithInlineConfigParser(
445                 getNonCompilablePath("InputUnusedLocalVariableWithAllowUnnamed.java"),
446                 expected);
447     }
448 
449     @Test
450     public void testUnusedLocalVariableWithAllowUnnamedFalse() throws Exception {
451         final String[] expected = {
452             "20:13: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "_x"),
453             "21:13: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "__"),
454             "22:13: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "_"),
455             "32:14: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "_"),
456             "35:14: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "__"),
457             "43:14: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "_"),
458             "46:14: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "__"),
459         };
460         verifyWithInlineConfigParser(
461                 getNonCompilablePath("InputUnusedLocalVariableWithAllowUnnamedFalse.java"),
462                 expected);
463     }
464 
465     @Test
466     public void testUnusedLocalVariablePatternVariablesCondition() throws Exception {
467         final String[] expected = {
468             "19:37: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "redBall"),
469             "21:46: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "greenBall"),
470             "40:39: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "redBall"),
471             "54:42: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "redBall"),
472             "60:40: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "redBall"),
473             "62:49: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "greenBall"),
474             "80:30: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "lr"),
475         };
476         verifyWithInlineConfigParser(
477                 getPath("InputUnusedLocalVariablePatternVariablesCondition.java"),
478                 expected);
479     }
480 
481     @Test
482     public void testUnusedLocalVariablePatternVariablesCondition2() throws Exception {
483         final String[] expected = {
484             "23:68: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "lr"),
485             "43:33: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "s"),
486             "58:34: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "s1"),
487         };
488         verifyWithInlineConfigParser(
489                 getPath("InputUnusedLocalVariablePatternVariablesCondition2.java"),
490                 expected);
491     }
492 
493     @Test
494     public void testClearStateVariables() throws Exception {
495         final UnusedLocalVariableCheck check = new UnusedLocalVariableCheck();
496         final Optional<DetailAST> methodDef = TestUtil.findTokenInAstByPredicate(
497                 JavaParser.parseFile(
498                         new File(getPath("InputUnusedLocalVariable.java")),
499                         JavaParser.Options.WITHOUT_COMMENTS),
500                 ast -> ast.getType() == TokenTypes.METHOD_DEF);
501         assertWithMessage("Ast should contain METHOD_DEF")
502                 .that(methodDef.isPresent())
503                 .isTrue();
504         final DetailAST variableDef = methodDef.orElseThrow().getLastChild()
505                 .findFirstToken(TokenTypes.VARIABLE_DEF);
506         assertWithMessage("State is not cleared on beginTree")
507                 .that(TestUtil.isStatefulFieldClearedDuringBeginTree(check, variableDef,
508                         "variables",
509                         variables -> {
510                             return ((Collection<?>) variables).isEmpty();
511                         }))
512                 .isTrue();
513     }
514 
515     @Test
516     public void testClearStateClasses() throws Exception {
517         final UnusedLocalVariableCheck check = new UnusedLocalVariableCheck();
518         final Optional<DetailAST> classDef = TestUtil.findTokenInAstByPredicate(
519                 JavaParser.parseFile(
520                         new File(getPath("InputUnusedLocalVariable.java")),
521                         JavaParser.Options.WITHOUT_COMMENTS),
522                 ast -> ast.getType() == TokenTypes.CLASS_DEF);
523         assertWithMessage("Ast should contain CLASS_DEF")
524                 .that(classDef.isPresent())
525                 .isTrue();
526         final DetailAST classDefToken = classDef.orElseThrow();
527         assertWithMessage("State is not cleared on beginTree")
528                 .that(TestUtil.isStatefulFieldClearedDuringBeginTree(check, classDefToken,
529                         "typeDeclarations",
530                         typeDeclarations -> {
531                             return ((Collection<?>) typeDeclarations).isEmpty();
532                         }))
533                 .isTrue();
534         assertWithMessage("State is not cleared on beginTree")
535                 .that(TestUtil.isStatefulFieldClearedDuringBeginTree(check, classDefToken,
536                         "typeDeclAstToTypeDeclDesc",
537                         typeDeclAstToTypeDeclDesc -> {
538                             return ((Map<?, ?>) typeDeclAstToTypeDeclDesc).isEmpty();
539                         }))
540                 .isTrue();
541         assertWithMessage("State is not cleared on beginTree")
542                 .that(TestUtil.isStatefulFieldClearedDuringBeginTree(check, classDefToken,
543                         "depth",
544                         depth -> {
545                             return (int) depth == 0;
546                         }))
547                 .isTrue();
548     }
549 
550     @Test
551     public void testClearStateAnonInnerClass() throws Exception {
552         final UnusedLocalVariableCheck check = new UnusedLocalVariableCheck();
553         final DetailAST root = JavaParser.parseFile(
554                 new File(getPath("InputUnusedLocalVariableAnonInnerClasses.java")),
555                 JavaParser.Options.WITHOUT_COMMENTS);
556         // Not using TestUtil.isStatefulFieldClearedDuringBeginTree(..) because other
557         // nodes need to be processed first as those collections, maps are dependent on them.
558         final DetailAST classDefAst = root.findFirstToken(TokenTypes.CLASS_DEF);
559         final Optional<DetailAST> literalNew = TestUtil.findTokenInAstByPredicate(root,
560                 ast -> ast.getType() == TokenTypes.LITERAL_NEW);
561         assertWithMessage("Ast should contain LITERAL_NEW")
562                 .that(literalNew.isPresent())
563                 .isTrue();
564         check.beginTree(root);
565         check.visitToken(classDefAst);
566         check.visitToken(literalNew.orElseThrow());
567         check.beginTree(null);
568         final Predicate<Object> isClear = anonInnerAstToTypeDesc -> {
569             return ((Map<?, ?>) anonInnerAstToTypeDesc).isEmpty();
570         };
571         assertWithMessage("State is not cleared on beginTree")
572                 .that(isClear.test(TestUtil.getInternalState(
573                         check, "anonInnerAstToTypeDeclDesc", Object.class)))
574                 .isTrue();
575         final Predicate<Object> isQueueClear = anonInnerClassHolders -> {
576             return ((Collection<?>) anonInnerClassHolders).isEmpty();
577         };
578         assertWithMessage("State is not cleared on beginTree")
579                 .that(isQueueClear.test(TestUtil.getInternalState(
580                         check, "anonInnerClassHolders", Object.class)))
581                 .isTrue();
582     }
583 
584     @Test
585     public void testClearStatePackageDef() throws Exception {
586         final UnusedLocalVariableCheck check = new UnusedLocalVariableCheck();
587         final Optional<DetailAST> packageDef = TestUtil.findTokenInAstByPredicate(
588                 JavaParser.parseFile(
589                         new File(getPath("InputUnusedLocalVariable.java")),
590                         JavaParser.Options.WITHOUT_COMMENTS),
591                 ast -> ast.getType() == TokenTypes.PACKAGE_DEF);
592         assertWithMessage("Ast should contain PACKAGE_DEF")
593                 .that(packageDef.isPresent())
594                 .isTrue();
595         final DetailAST packageDefToken = packageDef.orElseThrow();
596         assertWithMessage("State is not cleared on beginTree")
597                 .that(TestUtil.isStatefulFieldClearedDuringBeginTree(check, packageDefToken,
598                         "packageName",
599                         Objects::isNull))
600                 .isTrue();
601     }
602 
603     @Test
604     public void testUnusedLocalVarInAnonInnerClasses2() throws Exception {
605         final String[] expected = {
606             "20:13: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "s"),
607         };
608         verifyWithInlineConfigParser(
609                 getPath("InputUnusedLocalVariableAnonInnerClasses2.java"),
610                 expected);
611     }
612 
613     @Test
614     public void testUnusedLocalVarInAnonInnerClasses3() throws Exception {
615         final String[] expected = {
616             "13:13: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "a"),
617             "20:13: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "obj"),
618             "32:13: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "s"),
619         };
620         verifyWithInlineConfigParser(
621                 getPath("InputUnusedLocalVariableAnonInnerClasses3.java"),
622                 expected);
623     }
624 
625     @Test
626     public void testUnusedLocalVariablePatternVariables() throws Exception {
627         final String[] expected = {
628             "18:25: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "c"),
629             "19:28: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "r"),
630             "42:29: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "c"),
631             "43:32: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "r"),
632         };
633         verifyWithInlineConfigParser(
634                 getPath(
635                     "InputUnusedLocalVariablePatternVariables.java"),
636                 expected);
637     }
638 
639     @Test
640     public void testUnusedLocalVariablePatternVariables2() throws Exception {
641         final String[] expected = {
642             "13:26: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "i"),
643             "27:25: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "s"),
644             "46:35: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "s"),
645         };
646         verifyWithInlineConfigParser(
647                 getPath(
648                     "InputUnusedLocalVariablePatternVariables2.java"),
649                 expected);
650     }
651 
652     @Test
653     public void testUnusedLocalVariablePatternVariablesAllowUnnamed() throws Exception {
654         final String[] expected = {
655             "18:28: " + getCheckMessage(MSG_UNUSED_NAMED_LOCAL_VARIABLE, "c"),
656             "19:28: " + getCheckMessage(MSG_UNUSED_NAMED_LOCAL_VARIABLE, "r"),
657             "29:32: " + getCheckMessage(MSG_UNUSED_NAMED_LOCAL_VARIABLE, "c"),
658             "30:32: " + getCheckMessage(MSG_UNUSED_NAMED_LOCAL_VARIABLE, "r"),
659             "45:35: " + getCheckMessage(MSG_UNUSED_NAMED_LOCAL_VARIABLE, "s"),
660         };
661         verifyWithInlineConfigParser(
662                 getPath(
663                     "InputUnusedLocalVariablePatternVariablesAllowUnnamed.java"),
664                 expected);
665     }
666 
667     /**
668      * Use TestUtil.invokeStaticMethod to access the private static method
669      * isInsideLocalAnonInnerClass to test this because it produces optimization
670      * mutations that we can not kill using verifyWithInlineConfigParser.
671      */
672     @Test
673     public void testIsInsideLocalAnonInnerClass() throws Exception {
674         final DetailAST root = JavaParser.parseFile(
675                 new File(getPath("InputUnusedLocalVariableLambdaAnonInner.java")),
676                 JavaParser.Options.WITHOUT_COMMENTS);
677 
678         final DetailAST fieldObj = TestUtil.findTokenInAstByPredicate(root,
679                         ast -> {
680                             return ast.getType() == TokenTypes.VARIABLE_DEF
681                                     && "fieldObj".equals(ast.findFirstToken(TokenTypes.IDENT)
682                                     .getText());
683                         })
684                 .orElseThrow();
685         final DetailAST literalNewField = fieldObj.findFirstToken(TokenTypes.ASSIGN)
686                 .findFirstToken(TokenTypes.EXPR)
687                 .findFirstToken(TokenTypes.LITERAL_NEW);
688 
689         final boolean resultFalse = TestUtil.invokeStaticMethod(
690                 UnusedLocalVariableCheck.class,
691                 "isInsideLocalAnonInnerClass",
692                 Boolean.class,
693                 literalNewField);
694 
695         assertWithMessage("Should be false for field initialization (no SLIST in ancestry)")
696                 .that(resultFalse)
697                 .isFalse();
698 
699         final DetailAST localObj = TestUtil.findTokenInAstByPredicate(root,
700                         ast -> {
701                             return ast.getType() == TokenTypes.VARIABLE_DEF
702                                     && "localObj".equals(ast.findFirstToken(TokenTypes.IDENT)
703                                     .getText());
704                         })
705                 .orElseThrow();
706         final DetailAST literalNewLocal = localObj.findFirstToken(TokenTypes.ASSIGN)
707                 .findFirstToken(TokenTypes.EXPR)
708                 .findFirstToken(TokenTypes.LITERAL_NEW);
709 
710         final boolean resultTrue = TestUtil.invokeStaticMethod(
711                 UnusedLocalVariableCheck.class,
712                 "isInsideLocalAnonInnerClass",
713                 Boolean.class,
714                 literalNewLocal);
715 
716         assertWithMessage("Should be true for local variable initialization")
717                 .that(resultTrue)
718                 .isTrue();
719     }
720 }