View Javadoc
1   ///////////////////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code and other text files for adherence to a set of rules.
3   // Copyright (C) 2001-2025 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      protected 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          };
76          assertWithMessage("Required tokens are invalid")
77                  .that(actual)
78                  .isEqualTo(expected);
79      }
80  
81      @Test
82      public void testGetAcceptableTokens() {
83          final UnusedLocalVariableCheck typeParameterNameCheckObj =
84                  new UnusedLocalVariableCheck();
85          final int[] actual = typeParameterNameCheckObj.getAcceptableTokens();
86          final int[] expected = {
87              TokenTypes.DOT,
88              TokenTypes.VARIABLE_DEF,
89              TokenTypes.IDENT,
90              TokenTypes.SLIST,
91              TokenTypes.LITERAL_FOR,
92              TokenTypes.OBJBLOCK,
93              TokenTypes.CLASS_DEF,
94              TokenTypes.INTERFACE_DEF,
95              TokenTypes.ANNOTATION_DEF,
96              TokenTypes.PACKAGE_DEF,
97              TokenTypes.LITERAL_NEW,
98              TokenTypes.METHOD_DEF,
99              TokenTypes.CTOR_DEF,
100             TokenTypes.STATIC_INIT,
101             TokenTypes.INSTANCE_INIT,
102             TokenTypes.COMPILATION_UNIT,
103             TokenTypes.LAMBDA,
104             TokenTypes.ENUM_DEF,
105             TokenTypes.RECORD_DEF,
106             TokenTypes.COMPACT_CTOR_DEF,
107         };
108         assertWithMessage("Acceptable tokens are invalid")
109                 .that(actual)
110                 .isEqualTo(expected);
111     }
112 
113     @Test
114     public void testUnusedLocalVariable() throws Exception {
115         final String[] expected = {
116             "27:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "sameName"),
117             "28:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "b"),
118             "31:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "testInLambdas"),
119             "33:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "coding"),
120             "34:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE,
121                     "InputUnusedLocalVariable"),
122             "50:13: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "b"),
123             "54:13: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "c"),
124             "65:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "b"),
125             "67:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "c"),
126             "71:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "p"),
127             "81:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "f"),
128             "84:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "foo"),
129             "91:13: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "a"),
130         };
131         verifyWithInlineConfigParser(
132                 getPath("InputUnusedLocalVariable.java"), expected);
133     }
134 
135     @Test
136     public void testUnusedLocalVar2() throws Exception {
137         final String[] expected = {
138             "17:14: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "i"),
139             "19:14: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "j"),
140             "30:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "a"),
141             "31:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "b"),
142             "39:13: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "b"),
143             "40:13: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "Test"),
144             "41:13: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "obj"),
145             "61:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "a"),
146             "76:17: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "b"),
147         };
148         verifyWithInlineConfigParser(
149                 getPath("InputUnusedLocalVariable2.java"),
150                 expected);
151     }
152 
153     @Test
154     public void testUnusedLocalVar3() throws Exception {
155         final String[] expected = {
156             "21:13: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "a"),
157         };
158         verifyWithInlineConfigParser(
159                 getPath("InputUnusedLocalVariable3.java"),
160                 expected);
161     }
162 
163     @Test
164     public void testUnusedLocalVarInAnonInnerClasses() throws Exception {
165         final String[] expected = {
166             "14:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "a"),
167             "15:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "b"),
168             "17:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "obj"),
169             "22:17: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "obj"),
170             "32:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "obj2"),
171             "46:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "m"),
172             "47:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "l"),
173             "59:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "h"),
174             "62:17: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "v"),
175         };
176         verifyWithInlineConfigParser(
177                 getPath("InputUnusedLocalVariableAnonInnerClasses.java"),
178                 expected);
179     }
180 
181     @Test
182     public void testUnusedLocalVarGenericAnonInnerClasses() throws Exception {
183         final String[] expected = {
184             "13:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "l"),
185             "14:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "obj"),
186             "33:13: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "l"),
187             "34:13: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "obj2"),
188             "47:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "s"),
189             "67:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "variable"),
190             "69:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "anotherVar"),
191             "78:47: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "obj2"),
192         };
193         verifyWithInlineConfigParser(
194                 getPath("InputUnusedLocalVariableGenericAnonInnerClasses.java"),
195                 expected);
196     }
197 
198     @Test
199     public void testUnusedLocalVarDepthOfClasses() throws Exception {
200         final String[] expected = {
201             "28:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "r"),
202             "49:21: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "a"),
203             "64:13: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "a"),
204             "94:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "variable"),
205         };
206         verifyWithInlineConfigParser(
207                 getPath("InputUnusedLocalVariableDepthOfClasses.java"),
208                 expected);
209     }
210 
211     @Test
212     public void testUnusedLocalVarNestedClasses() throws Exception {
213         final String[] expected = {
214             "21:13: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "V"),
215             "23:13: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "S"),
216             "24:13: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "Q"),
217             "36:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "variable"),
218             "44:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "anotherVariable"),
219             "67:21: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "s"),
220             "68:21: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "n"),
221         };
222         verifyWithInlineConfigParser(
223                 getPath("InputUnusedLocalVariableNestedClasses.java"),
224                 expected);
225     }
226 
227     @Test
228     public void testUnusedLocalVarNestedClasses2() throws Exception {
229         final String[] expected = {
230             "29:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "q"),
231             "30:51: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "obj"),
232             "46:21: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "b"),
233             "57:21: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "b"),
234             "108:33: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "s"),
235         };
236         verifyWithInlineConfigParser(
237                 getPath("InputUnusedLocalVariableNestedClasses2.java"),
238                 expected);
239     }
240 
241     @Test
242     public void testUnusedLocalVarNestedClasses3() throws Exception {
243         final String[] expected = {
244             "36:17: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "p2"),
245             "54:13: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "o"),
246             "93:13: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "b"),
247             "95:13: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "a"),
248         };
249 
250         verifyWithInlineConfigParser(
251                 getPath("InputUnusedLocalVariableNestedClasses3.java"),
252                 expected);
253     }
254 
255     @Test
256     public void testUnusedLocalVarNestedClasses4() throws Exception {
257         final String[] expected = {
258             "12:5: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "a"),
259             "13:5: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "ab"),
260         };
261 
262         verifyWithInlineConfigParser(
263                 getPath("InputUnusedLocalVariableNestedClasses4.java"),
264                 expected);
265     }
266 
267     @Test
268     public void testUnusedLocalVarNestedClasses5() throws Exception {
269         final String[] expected = {
270             "12:5: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "a"),
271             "13:5: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "ab"),
272             "19:11: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "abc"),
273         };
274 
275         verifyWithInlineConfigParser(
276                 getPath("InputUnusedLocalVariableNestedClasses5.java"),
277                 expected);
278     }
279 
280     @Test
281     public void testUnusedLocalVarNestedClasses6() throws Exception {
282         final String[] expected = {
283             "12:5: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "a"),
284             "13:5: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "ab"),
285         };
286 
287         verifyWithInlineConfigParser(
288                 getPath("InputUnusedLocalVariableNestedClasses6.java"),
289                 expected);
290     }
291 
292     @Test
293     public void testUnusedLocalVarNestedClasses7() throws Exception {
294         final String[] expected = {
295             "10:5: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "a"),
296             "11:5: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "ab"),
297             "16:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "ab"),
298             "23:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "a"),
299             "24:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "ab"),
300             "28:13: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "a"),
301         };
302 
303         verifyWithInlineConfigParser(
304                 getPath("InputUnusedLocalVariableNestedClasses7.java"),
305                 expected);
306     }
307 
308     @Test
309     public void testUnusedLocalVarTestWarningSeverity() throws Exception {
310         final String[] expected = {
311             "14:19: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "p2"),
312         };
313 
314         verifyWithInlineConfigParser(
315                 getPath("InputUnusedLocalVariableTestWarningSeverity.java"),
316                 expected);
317     }
318 
319     @Test
320     public void testUnusedLocalVarEnum() throws Exception {
321         final String[] expected = {
322             "22:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "a"),
323             "50:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "a"),
324             "77:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "a"),
325             "80:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "j"),
326             "92:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "d"),
327         };
328         verifyWithInlineConfigParser(
329                 getPath("InputUnusedLocalVariableEnum.java"),
330                 expected);
331     }
332 
333     @Test
334     public void testUnusedLocalVarLambdas() throws Exception {
335         final String[] expected = {
336             "14:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "hoo"),
337             "19:17: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "j"),
338             "29:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "hoo2"),
339             "30:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "hoo3"),
340             "32:15: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "myComponent"),
341             "34:19: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "myComponent3"),
342             "40:25: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "j"),
343             "52:21: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "j"),
344             "65:17: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "ja"),
345             "73:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "k"),
346         };
347         verifyWithInlineConfigParser(
348                 getPath("InputUnusedLocalVariableLambdaExpression.java"),
349                 expected);
350     }
351 
352     @Test
353     public void testUnusedLocalVariableLocalClasses() throws Exception {
354         final String[] expected = {
355             "14:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "a"),
356             "15:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "ab"),
357         };
358         verifyWithInlineConfigParser(
359                 getPath("InputUnusedLocalVariableLocalClasses.java"),
360                 expected);
361     }
362 
363     @Test
364     public void testUnusedLocalVarRecords() throws Exception {
365         final String[] expected = {
366             "16:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "var1"),
367             "25:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "var1"),
368             "26:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "obj"),
369             "36:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "var2"),
370         };
371         verifyWithInlineConfigParser(
372                 getPath("InputUnusedLocalVariableRecords.java"),
373                 expected);
374     }
375 
376     @Test
377     public void testUnusedLocalVarWithoutPackageStatement() throws Exception {
378         final String[] expected = {
379             "12:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "a"),
380             "24:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "var2"),
381             "45:13: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "var3"),
382         };
383         verifyWithInlineConfigParser(
384                 getNonCompilablePath("InputUnusedLocalVariableWithoutPackageStatement.java"),
385                 expected);
386     }
387 
388     @Test
389     public void testUnusedLocalVariableTernaryAndExpressions() throws Exception {
390         final String[] expected = CommonUtil.EMPTY_STRING_ARRAY;
391         verifyWithInlineConfigParser(
392                 getPath("InputUnusedLocalVariableTernaryAndExpressions.java"),
393                 expected);
394     }
395 
396     @Test
397     public void testUnusedLocalVariableSwitchStatement() throws Exception {
398         final String[] expected = CommonUtil.EMPTY_STRING_ARRAY;
399         verifyWithInlineConfigParser(
400                 getPath("InputUnusedLocalVariableSwitchStatement.java"),
401                 expected);
402     }
403 
404     @Test
405     public void testUnusedLocalVariableSwitchStatement2() throws Exception {
406         final String[] expected = {
407             "59:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "j"),
408         };
409         verifyWithInlineConfigParser(
410                 getPath("InputUnusedLocalVariableSwitchStatement2.java"),
411                 expected);
412     }
413 
414     @Test
415     public void testUnusedLocalVariableSwitchExpression() throws Exception {
416         final String[] expected = {
417             "16:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "line2"),
418         };
419         verifyWithInlineConfigParser(
420                 getPath("InputUnusedLocalVariableSwitchExpression.java"),
421                 expected);
422     }
423 
424     @Test
425     public void testUnusedLocalVariableWithAllowUnnamed() throws Exception {
426         final String[] expected = {
427             "20:13: " + getCheckMessage(MSG_UNUSED_NAMED_LOCAL_VARIABLE, "_x"),
428             "21:13: " + getCheckMessage(MSG_UNUSED_NAMED_LOCAL_VARIABLE, "__"),
429             "35:14: " + getCheckMessage(MSG_UNUSED_NAMED_LOCAL_VARIABLE, "__"),
430             "45:14: " + getCheckMessage(MSG_UNUSED_NAMED_LOCAL_VARIABLE, "__"),
431         };
432         verifyWithInlineConfigParser(
433                 getPath("InputUnusedLocalVariableWithAllowUnnamed.java"),
434                 expected);
435     }
436 
437     @Test
438     public void testUnusedLocalVariableWithAllowUnnamedFalse() throws Exception {
439         final String[] expected = {
440             "20:13: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "_x"),
441             "21:13: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "__"),
442             "22:13: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "_"),
443             "32:14: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "_"),
444             "35:14: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "__"),
445             "43:14: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "_"),
446             "46:14: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "__"),
447         };
448         verifyWithInlineConfigParser(
449                 getPath("InputUnusedLocalVariableWithAllowUnnamedFalse.java"),
450                 expected);
451     }
452 
453     @Test
454     public void testClearStateVariables() throws Exception {
455         final UnusedLocalVariableCheck check = new UnusedLocalVariableCheck();
456         final Optional<DetailAST> methodDef = TestUtil.findTokenInAstByPredicate(
457                 JavaParser.parseFile(
458                         new File(getPath("InputUnusedLocalVariable.java")),
459                         JavaParser.Options.WITHOUT_COMMENTS),
460                 ast -> ast.getType() == TokenTypes.METHOD_DEF);
461         assertWithMessage("Ast should contain METHOD_DEF")
462                 .that(methodDef.isPresent())
463                 .isTrue();
464         final DetailAST variableDef = methodDef.orElseThrow().getLastChild()
465                 .findFirstToken(TokenTypes.VARIABLE_DEF);
466         assertWithMessage("State is not cleared on beginTree")
467                 .that(TestUtil.isStatefulFieldClearedDuringBeginTree(check, variableDef,
468                         "variables",
469                         variables -> {
470                             return ((Collection<?>) variables).isEmpty();
471                         }))
472                 .isTrue();
473     }
474 
475     @Test
476     public void testClearStateClasses() throws Exception {
477         final UnusedLocalVariableCheck check = new UnusedLocalVariableCheck();
478         final Optional<DetailAST> classDef = TestUtil.findTokenInAstByPredicate(
479                 JavaParser.parseFile(
480                         new File(getPath("InputUnusedLocalVariable.java")),
481                         JavaParser.Options.WITHOUT_COMMENTS),
482                 ast -> ast.getType() == TokenTypes.CLASS_DEF);
483         assertWithMessage("Ast should contain CLASS_DEF")
484                 .that(classDef.isPresent())
485                 .isTrue();
486         final DetailAST classDefToken = classDef.orElseThrow();
487         assertWithMessage("State is not cleared on beginTree")
488                 .that(TestUtil.isStatefulFieldClearedDuringBeginTree(check, classDefToken,
489                         "typeDeclarations",
490                         typeDeclarations -> {
491                             return ((Collection<?>) typeDeclarations).isEmpty();
492                         }))
493                 .isTrue();
494         assertWithMessage("State is not cleared on beginTree")
495                 .that(TestUtil.isStatefulFieldClearedDuringBeginTree(check, classDefToken,
496                         "typeDeclAstToTypeDeclDesc",
497                         typeDeclAstToTypeDeclDesc -> {
498                             return ((Map<?, ?>) typeDeclAstToTypeDeclDesc).isEmpty();
499                         }))
500                 .isTrue();
501         assertWithMessage("State is not cleared on beginTree")
502                 .that(TestUtil.isStatefulFieldClearedDuringBeginTree(check, classDefToken,
503                         "depth",
504                         depth -> {
505                             return (int) depth == 0;
506                         }))
507                 .isTrue();
508     }
509 
510     @Test
511     public void testClearStateAnonInnerClass() throws Exception {
512         final UnusedLocalVariableCheck check = new UnusedLocalVariableCheck();
513         final DetailAST root = JavaParser.parseFile(
514                 new File(getPath("InputUnusedLocalVariableAnonInnerClasses.java")),
515                 JavaParser.Options.WITHOUT_COMMENTS);
516         // Not using TestUtil.isStatefulFieldClearedDuringBeginTree(..) because other
517         // nodes need to be processed first as those collections, maps are dependent on them.
518         final DetailAST classDefAst = root.findFirstToken(TokenTypes.CLASS_DEF);
519         final Optional<DetailAST> literalNew = TestUtil.findTokenInAstByPredicate(root,
520                 ast -> ast.getType() == TokenTypes.LITERAL_NEW);
521         assertWithMessage("Ast should contain LITERAL_NEW")
522                 .that(literalNew.isPresent())
523                 .isTrue();
524         check.beginTree(root);
525         check.visitToken(classDefAst);
526         check.visitToken(literalNew.orElseThrow());
527         check.beginTree(null);
528         final Predicate<Object> isClear = anonInnerAstToTypeDesc -> {
529             return ((Map<?, ?>) anonInnerAstToTypeDesc).isEmpty();
530         };
531         assertWithMessage("State is not cleared on beginTree")
532                 .that(isClear.test(TestUtil.getInternalState(
533                         check, "anonInnerAstToTypeDeclDesc")))
534                 .isTrue();
535         final Predicate<Object> isQueueClear = anonInnerClassHolders -> {
536             return ((Collection<?>) anonInnerClassHolders).isEmpty();
537         };
538         assertWithMessage("State is not cleared on beginTree")
539                 .that(isQueueClear.test(TestUtil.getInternalState(
540                         check, "anonInnerClassHolders")))
541                 .isTrue();
542     }
543 
544     @Test
545     public void testClearStatePackageDef() throws Exception {
546         final UnusedLocalVariableCheck check = new UnusedLocalVariableCheck();
547         final Optional<DetailAST> packageDef = TestUtil.findTokenInAstByPredicate(
548                 JavaParser.parseFile(
549                         new File(getPath("InputUnusedLocalVariable.java")),
550                         JavaParser.Options.WITHOUT_COMMENTS),
551                 ast -> ast.getType() == TokenTypes.PACKAGE_DEF);
552         assertWithMessage("Ast should contain PACKAGE_DEF")
553                 .that(packageDef.isPresent())
554                 .isTrue();
555         final DetailAST packageDefToken = packageDef.orElseThrow();
556         assertWithMessage("State is not cleared on beginTree")
557                 .that(TestUtil.isStatefulFieldClearedDuringBeginTree(check, packageDefToken,
558                         "packageName",
559                         Objects::isNull))
560                 .isTrue();
561     }
562 
563     @Test
564     public void testUnusedLocalVarInAnonInnerClasses2() throws Exception {
565         final String[] expected = {
566             "20:13: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "s"),
567         };
568         verifyWithInlineConfigParser(
569                 getPath("InputUnusedLocalVariableAnonInnerClasses2.java"),
570                 expected);
571     }
572 
573     @Test
574     public void testUnusedLocalVarInAnonInnerClasses3() throws Exception {
575         final String[] expected = {
576             "13:13: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "a"),
577             "20:13: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "obj"),
578             "32:13: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "s"),
579         };
580         verifyWithInlineConfigParser(
581                 getPath("InputUnusedLocalVariableAnonInnerClasses3.java"),
582                 expected);
583     }
584 }