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             "28:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "sameName"),
119             "29:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "b"),
120             "32:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "testInLambdas"),
121             "34:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "coding"),
122             "35:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE,
123                     "InputUnusedLocalVariable"),
124             "51:13: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "b"),
125             "55:13: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "c"),
126             "66:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "b"),
127             "68:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "c"),
128             "72:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "p"),
129             "82:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "f"),
130             "85:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "foo"),
131             "92: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             "18:14: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "i"),
141             "20:14: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "j"),
142             "31:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "a"),
143             "32:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "b"),
144             "40:13: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "b"),
145             "41:13: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "Test"),
146             "42:13: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "obj"),
147             "62:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "a"),
148             "77: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             "22: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             "15:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "a"),
169             "16:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "b"),
170             "18:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "obj"),
171             "23:17: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "obj"),
172             "33:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "obj2"),
173             "47:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "m"),
174             "48:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "l"),
175             "60:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "h"),
176             "63:17: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "v"),
177             "88:10: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "o"),
178             "97:18: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "o"),
179             "102:18: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "o"),
180             "106:17: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "o"),
181         };
182         verifyWithInlineConfigParser(
183                 getPath("InputUnusedLocalVariableAnonInnerClasses.java"),
184                 expected);
185     }
186 
187     @Test
188     public void testUnusedLocalVarGenericAnonInnerClasses() throws Exception {
189         final String[] expected = {
190             "15:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "l"),
191             "16:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "obj"),
192             "35:13: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "l"),
193             "36:13: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "obj2"),
194             "49:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "s"),
195             "69:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "variable"),
196             "71:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "anotherVar"),
197             "80:47: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "obj2"),
198         };
199         verifyWithInlineConfigParser(
200                 getPath("InputUnusedLocalVariableGenericAnonInnerClasses.java"),
201                 expected);
202     }
203 
204     @Test
205     public void testUnusedLocalVarDepthOfClasses() throws Exception {
206         final String[] expected = {
207             "29:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "r"),
208             "50:21: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "a"),
209             "65:13: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "a"),
210             "95:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "variable"),
211         };
212         verifyWithInlineConfigParser(
213                 getPath("InputUnusedLocalVariableDepthOfClasses.java"),
214                 expected);
215     }
216 
217     @Test
218     public void testUnusedLocalVarNestedClasses() throws Exception {
219         final String[] expected = {
220             "22:13: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "V"),
221             "24:13: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "S"),
222             "25:13: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "Q"),
223             "37:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "variable"),
224             "45:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "anotherVariable"),
225             "68:21: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "s"),
226             "69:21: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "n"),
227         };
228         verifyWithInlineConfigParser(
229                 getPath("InputUnusedLocalVariableNestedClasses.java"),
230                 expected);
231     }
232 
233     @Test
234     public void testUnusedLocalVarNestedClasses2() throws Exception {
235         final String[] expected = {
236             "30:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "q"),
237             "31:51: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "obj"),
238             "47:21: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "b"),
239             "58:21: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "b"),
240             "109:33: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "s"),
241         };
242         verifyWithInlineConfigParser(
243                 getPath("InputUnusedLocalVariableNestedClasses2.java"),
244                 expected);
245     }
246 
247     @Test
248     public void testUnusedLocalVarNestedClasses3() throws Exception {
249         final String[] expected = {
250             "37:17: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "p2"),
251             "55:13: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "o"),
252             "94:13: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "b"),
253             "96:13: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "a"),
254         };
255 
256         verifyWithInlineConfigParser(
257                 getPath("InputUnusedLocalVariableNestedClasses3.java"),
258                 expected);
259     }
260 
261     @Test
262     public void testUnusedLocalVarNestedClasses4() throws Exception {
263         final String[] expected = {
264             "13:5: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "a"),
265             "14:5: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "ab"),
266         };
267 
268         verifyWithInlineConfigParser(
269                 getPath("InputUnusedLocalVariableNestedClasses4.java"),
270                 expected);
271     }
272 
273     @Test
274     public void testUnusedLocalVarNestedClasses5() throws Exception {
275         final String[] expected = {
276             "13:5: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "a"),
277             "14:5: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "ab"),
278             "20:11: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "abc"),
279         };
280 
281         verifyWithInlineConfigParser(
282                 getPath("InputUnusedLocalVariableNestedClasses5.java"),
283                 expected);
284     }
285 
286     @Test
287     public void testUnusedLocalVarNestedClasses6() throws Exception {
288         final String[] expected = {
289             "13:5: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "a"),
290             "14:5: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "ab"),
291         };
292 
293         verifyWithInlineConfigParser(
294                 getPath("InputUnusedLocalVariableNestedClasses6.java"),
295                 expected);
296     }
297 
298     @Test
299     public void testUnusedLocalVarNestedClasses7() throws Exception {
300         final String[] expected = {
301             "11:5: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "a"),
302             "12:5: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "ab"),
303             "17:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "ab"),
304             "24:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "a"),
305             "25:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "ab"),
306             "29:13: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "a"),
307         };
308 
309         verifyWithInlineConfigParser(
310                 getPath("InputUnusedLocalVariableNestedClasses7.java"),
311                 expected);
312     }
313 
314     @Test
315     public void testUnusedLocalVarTestWarningSeverity() throws Exception {
316         final String[] expected = {
317             "15:19: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "p2"),
318         };
319 
320         verifyWithInlineConfigParser(
321                 getPath("InputUnusedLocalVariableTestWarningSeverity.java"),
322                 expected);
323     }
324 
325     @Test
326     public void testUnusedLocalVarEnum() throws Exception {
327         final String[] expected = {
328             "23:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "a"),
329             "51:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "a"),
330             "78:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "a"),
331             "81:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "j"),
332             "93:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "d"),
333         };
334         verifyWithInlineConfigParser(
335                 getPath("InputUnusedLocalVariableEnum.java"),
336                 expected);
337     }
338 
339     @Test
340     public void testUnusedLocalVarLambdas() throws Exception {
341         final String[] expected = {
342             "15:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "hoo"),
343             "20:17: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "j"),
344             "30:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "hoo2"),
345             "31:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "hoo3"),
346             "33:15: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "myComponent"),
347             "35:19: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "myComponent3"),
348             "41:25: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "j"),
349             "53:21: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "j"),
350             "66:17: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "ja"),
351             "74:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "k"),
352         };
353         verifyWithInlineConfigParser(
354                 getPath("InputUnusedLocalVariableLambdaExpression.java"),
355                 expected);
356     }
357 
358     @Test
359     public void testUnusedLocalVariableLocalClasses() throws Exception {
360         final String[] expected = {
361             "15:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "a"),
362             "16:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "ab"),
363         };
364         verifyWithInlineConfigParser(
365                 getPath("InputUnusedLocalVariableLocalClasses.java"),
366                 expected);
367     }
368 
369     @Test
370     public void testUnusedLocalVarRecords() throws Exception {
371         final String[] expected = {
372             "17:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "var1"),
373             "26:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "var1"),
374             "27:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "obj"),
375             "37:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "var2"),
376         };
377         verifyWithInlineConfigParser(
378                 getPath("InputUnusedLocalVariableRecords.java"),
379                 expected);
380     }
381 
382     @Test
383     public void testUnusedLocalVarWithoutPackageStatement() throws Exception {
384         final String[] expected = {
385             "13:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "a"),
386             "25:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "var2"),
387             "46:13: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "var3"),
388         };
389         verifyWithInlineConfigParser(
390                 getNonCompilablePath("InputUnusedLocalVariableNoPackageStatement.java"),
391                 expected);
392     }
393 
394     @Test
395     public void testCompactSourceFile() throws Exception {
396         final String[] expected = {
397             "13:5: " + getCheckMessage(MSG_UNUSED_NAMED_LOCAL_VARIABLE, "unused"),
398         };
399         verifyWithInlineConfigParser(
400                 getNonCompilablePath("InputUnusedLocalVariableCompactSourceFile.java"),
401                 expected);
402     }
403 
404     @Test
405     public void testUnusedLocalVariableTernaryAndExpressions() throws Exception {
406         final String[] expected = CommonUtil.EMPTY_STRING_ARRAY;
407         verifyWithInlineConfigParser(
408                 getPath("InputUnusedLocalVariableTernaryAndExpressions.java"),
409                 expected);
410     }
411 
412     @Test
413     public void testUnusedLocalVariableSwitchStatement() throws Exception {
414         final String[] expected = CommonUtil.EMPTY_STRING_ARRAY;
415         verifyWithInlineConfigParser(
416                 getPath("InputUnusedLocalVariableSwitchStatement.java"),
417                 expected);
418     }
419 
420     @Test
421     public void testUnusedLocalVariableSwitchStatement2() throws Exception {
422         final String[] expected = {
423             "61:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "j"),
424         };
425         verifyWithInlineConfigParser(
426                 getPath("InputUnusedLocalVariableSwitchStatement2.java"),
427                 expected);
428     }
429 
430     @Test
431     public void testUnusedLocalVariableSwitchExpression() throws Exception {
432         final String[] expected = {
433             "17:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "line2"),
434         };
435         verifyWithInlineConfigParser(
436                 getPath("InputUnusedLocalVariableSwitchExpression.java"),
437                 expected);
438     }
439 
440     @Test
441     public void testUnusedLocalVariableWithAllowUnnamed() throws Exception {
442         final String[] expected = {
443             "22:13: " + getCheckMessage(MSG_UNUSED_NAMED_LOCAL_VARIABLE, "_x"),
444             "23:13: " + getCheckMessage(MSG_UNUSED_NAMED_LOCAL_VARIABLE, "__"),
445             "37:14: " + getCheckMessage(MSG_UNUSED_NAMED_LOCAL_VARIABLE, "__"),
446             "47:14: " + getCheckMessage(MSG_UNUSED_NAMED_LOCAL_VARIABLE, "__"),
447         };
448         verifyWithInlineConfigParser(
449                 getNonCompilablePath("InputUnusedLocalVariableWithAllowUnnamed.java"),
450                 expected);
451     }
452 
453     @Test
454     public void testUnusedLocalVariableWithAllowUnnamedFalse() throws Exception {
455         final String[] expected = {
456             "21:13: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "_x"),
457             "22:13: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "__"),
458             "23:13: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "_"),
459             "33:14: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "_"),
460             "36:14: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "__"),
461             "44:14: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "_"),
462             "47:14: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "__"),
463         };
464         verifyWithInlineConfigParser(
465                 getNonCompilablePath("InputUnusedLocalVariableWithAllowUnnamedFalse.java"),
466                 expected);
467     }
468 
469     @Test
470     public void testUnusedLocalVariablePatternVariablesCondition() throws Exception {
471         final String[] expected = {
472             "20:37: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "redBall"),
473             "22:46: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "greenBall"),
474             "41:39: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "redBall"),
475             "55:42: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "redBall"),
476             "61:40: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "redBall"),
477             "63:49: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "greenBall"),
478             "81:30: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "lr"),
479         };
480         verifyWithInlineConfigParser(
481                 getNonCompilablePath("InputUnusedLocalVariablePatternVariablesCondition.java"),
482                 expected);
483     }
484 
485     @Test
486     public void testUnusedLocalVariablePatternVariablesCondition2() throws Exception {
487         final String[] expected = {
488             "24:68: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "lr"),
489             "44:33: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "s"),
490             "59:34: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "s1"),
491             "80:14: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "id"),
492             "89:14: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "_"),
493         };
494         verifyWithInlineConfigParser(
495                 getNonCompilablePath("InputUnusedLocalVariablePatternVariablesCondition2.java"),
496                 expected);
497     }
498 
499     @Test
500     public void testUnusedLocalVariableNamedPatternVariable() throws Exception {
501         final String[] expected = {
502             "21:25: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "ignored"),
503             "22:26: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "ignored2"),
504             "28:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "x"),
505             "29:38: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "ignored"),
506             "36:30: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "y"),
507             "36:37: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "z"),
508             "42:57: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "ignoredAge"),
509             "58:43: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "inner"),
510         };
511         verifyWithInlineConfigParser(
512                 getPath("InputUnusedLocalVariableAllowNamedPatternVariables.java"),
513                 expected);
514     }
515 
516     @Test
517     public void testUnusedLocalVariableNamedPatternVariableTrue() throws Exception {
518         final String[] expected = {
519             "21:9: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "x"),
520             "22:38: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "ignored"),
521             "30:46: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "s"),
522         };
523         verifyWithInlineConfigParser(
524                 getPath("InputUnusedLocalVariableAllowNamedPatternVariablesTrue.java"),
525                 expected);
526     }
527 
528     @Test
529     public void testUnusedLocalVariableNamedPatternVariableInstanceof() throws Exception {
530         final String[] expected = {
531             "21:38: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "s"),
532         };
533         verifyWithInlineConfigParser(
534                 getPath("InputUnusedLocalVariableAllowNamedPatternVariablesInstanceOf.java"),
535                 expected);
536     }
537 
538     @Test
539     public void testUnusedLocalVariableNamedPatternVariableLegacyJdkFormat() throws Exception {
540         final String[] expected = CommonUtil.EMPTY_STRING_ARRAY;
541         verifyWithInlineConfigParser(
542                 getPath("InputUnusedLocalVariableJdkVersionLegacyFormat.java"),
543                 expected);
544     }
545 
546     @Test
547     public void testClearStateVariables() throws Exception {
548         final UnusedLocalVariableCheck check = new UnusedLocalVariableCheck();
549         final Optional<DetailAST> methodDef = TestUtil.findTokenInAstByPredicate(
550                 JavaParser.parseFile(
551                         new File(getPath("InputUnusedLocalVariable.java")),
552                         JavaParser.Options.WITHOUT_COMMENTS),
553                 ast -> ast.getType() == TokenTypes.METHOD_DEF);
554         assertWithMessage("Ast should contain METHOD_DEF")
555                 .that(methodDef.isPresent())
556                 .isTrue();
557         final DetailAST variableDef = methodDef.orElseThrow().getLastChild()
558                 .findFirstToken(TokenTypes.VARIABLE_DEF);
559         assertWithMessage("State is not cleared on beginTree")
560                 .that(TestUtil.isStatefulFieldClearedDuringBeginTree(check, variableDef,
561                         "variables",
562                         variables -> {
563                             return ((Collection<?>) variables).isEmpty();
564                         }))
565                 .isTrue();
566     }
567 
568     @Test
569     public void testClearStateClasses() throws Exception {
570         final UnusedLocalVariableCheck check = new UnusedLocalVariableCheck();
571         final Optional<DetailAST> classDef = TestUtil.findTokenInAstByPredicate(
572                 JavaParser.parseFile(
573                         new File(getPath("InputUnusedLocalVariable.java")),
574                         JavaParser.Options.WITHOUT_COMMENTS),
575                 ast -> ast.getType() == TokenTypes.CLASS_DEF);
576         assertWithMessage("Ast should contain CLASS_DEF")
577                 .that(classDef.isPresent())
578                 .isTrue();
579         final DetailAST classDefToken = classDef.orElseThrow();
580         assertWithMessage("State is not cleared on beginTree")
581                 .that(TestUtil.isStatefulFieldClearedDuringBeginTree(check, classDefToken,
582                         "typeDeclarations",
583                         typeDeclarations -> {
584                             return ((Collection<?>) typeDeclarations).isEmpty();
585                         }))
586                 .isTrue();
587         assertWithMessage("State is not cleared on beginTree")
588                 .that(TestUtil.isStatefulFieldClearedDuringBeginTree(check, classDefToken,
589                         "typeDeclAstToTypeDeclDesc",
590                         typeDeclAstToTypeDeclDesc -> {
591                             return ((Map<?, ?>) typeDeclAstToTypeDeclDesc).isEmpty();
592                         }))
593                 .isTrue();
594         assertWithMessage("State is not cleared on beginTree")
595                 .that(TestUtil.isStatefulFieldClearedDuringBeginTree(check, classDefToken,
596                         "depth",
597                         depth -> {
598                             return (int) depth == 0;
599                         }))
600                 .isTrue();
601     }
602 
603     @Test
604     public void testClearStateAnonInnerClass() throws Exception {
605         final UnusedLocalVariableCheck check = new UnusedLocalVariableCheck();
606         final DetailAST root = JavaParser.parseFile(
607                 new File(getPath("InputUnusedLocalVariableAnonInnerClasses.java")),
608                 JavaParser.Options.WITHOUT_COMMENTS);
609         // Not using TestUtil.isStatefulFieldClearedDuringBeginTree(..) because other
610         // nodes need to be processed first as those collections, maps are dependent on them.
611         final DetailAST classDefAst = root.findFirstToken(TokenTypes.CLASS_DEF);
612         final Optional<DetailAST> literalNew = TestUtil.findTokenInAstByPredicate(root,
613                 ast -> ast.getType() == TokenTypes.LITERAL_NEW);
614         assertWithMessage("Ast should contain LITERAL_NEW")
615                 .that(literalNew.isPresent())
616                 .isTrue();
617         check.beginTree(root);
618         check.visitToken(classDefAst);
619         check.visitToken(literalNew.orElseThrow());
620         check.beginTree(null);
621         final Predicate<Object> isClear = anonInnerAstToTypeDesc -> {
622             return ((Map<?, ?>) anonInnerAstToTypeDesc).isEmpty();
623         };
624         assertWithMessage("State is not cleared on beginTree")
625                 .that(isClear.test(TestUtil.getInternalState(
626                         check, "anonInnerAstToTypeDeclDesc", Object.class)))
627                 .isTrue();
628         final Predicate<Object> isQueueClear = anonInnerClassHolders -> {
629             return ((Collection<?>) anonInnerClassHolders).isEmpty();
630         };
631         assertWithMessage("State is not cleared on beginTree")
632                 .that(isQueueClear.test(TestUtil.getInternalState(
633                         check, "anonInnerClassHolders", Object.class)))
634                 .isTrue();
635     }
636 
637     @Test
638     public void testClearStatePackageDef() throws Exception {
639         final UnusedLocalVariableCheck check = new UnusedLocalVariableCheck();
640         final Optional<DetailAST> packageDef = TestUtil.findTokenInAstByPredicate(
641                 JavaParser.parseFile(
642                         new File(getPath("InputUnusedLocalVariable.java")),
643                         JavaParser.Options.WITHOUT_COMMENTS),
644                 ast -> ast.getType() == TokenTypes.PACKAGE_DEF);
645         assertWithMessage("Ast should contain PACKAGE_DEF")
646                 .that(packageDef.isPresent())
647                 .isTrue();
648         final DetailAST packageDefToken = packageDef.orElseThrow();
649         assertWithMessage("State is not cleared on beginTree")
650                 .that(TestUtil.isStatefulFieldClearedDuringBeginTree(check, packageDefToken,
651                         "packageName",
652                         Objects::isNull))
653                 .isTrue();
654     }
655 
656     @Test
657     public void testUnusedLocalVarInAnonInnerClasses2() throws Exception {
658         final String[] expected = {
659             "21:13: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "s"),
660         };
661         verifyWithInlineConfigParser(
662                 getPath("InputUnusedLocalVariableAnonInnerClasses2.java"),
663                 expected);
664     }
665 
666     @Test
667     public void testUnusedLocalVarInAnonInnerClasses3() throws Exception {
668         final String[] expected = {
669             "14:13: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "a"),
670             "21:13: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "obj"),
671             "33:13: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "s"),
672         };
673         verifyWithInlineConfigParser(
674                 getPath("InputUnusedLocalVariableAnonInnerClasses3.java"),
675                 expected);
676     }
677 
678     @Test
679     public void testUnusedLocalVariablePatternVariables() throws Exception {
680         final String[] expected = {
681             "19:25: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "c"),
682             "20:28: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "r"),
683             "43:29: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "c"),
684             "44:32: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "r"),
685         };
686         verifyWithInlineConfigParser(
687                 getNonCompilablePath(
688                     "InputUnusedLocalVariablePatternVariables.java"),
689                 expected);
690     }
691 
692     @Test
693     public void testUnusedLocalVariablePatternVariables2() throws Exception {
694         final String[] expected = {
695             "14:26: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "i"),
696             "28:25: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "s"),
697             "47:35: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "s"),
698         };
699         verifyWithInlineConfigParser(
700                 getPath(
701                     "InputUnusedLocalVariablePatternVariables2.java"),
702                 expected);
703     }
704 
705     @Test
706     public void testUnusedLocalVariablePatternVariablesAllowUnnamed() throws Exception {
707         final String[] expected = {
708             "19:28: " + getCheckMessage(MSG_UNUSED_NAMED_LOCAL_VARIABLE, "c"),
709             "20:28: " + getCheckMessage(MSG_UNUSED_NAMED_LOCAL_VARIABLE, "r"),
710             "30:32: " + getCheckMessage(MSG_UNUSED_NAMED_LOCAL_VARIABLE, "c"),
711             "31:32: " + getCheckMessage(MSG_UNUSED_NAMED_LOCAL_VARIABLE, "r"),
712             "46:35: " + getCheckMessage(MSG_UNUSED_NAMED_LOCAL_VARIABLE, "s"),
713             "57:14: " + getCheckMessage(MSG_UNUSED_NAMED_LOCAL_VARIABLE, "id"),
714         };
715         verifyWithInlineConfigParser(
716                 getNonCompilablePath(
717                     "InputUnusedLocalVariablePatternVariablesAllowUnnamed.java"),
718                 expected);
719     }
720 
721     @Test
722     public void testUnusedLocalVariablePatternVariablesUnnamedTry() throws Exception {
723         final String[] expected = {
724             "28:17: " + getCheckMessage(MSG_UNUSED_LOCAL_VARIABLE, "_"),
725         };
726         verifyWithInlineConfigParser(
727                 getNonCompilablePath(
728                     "InputUnusedLocalVariableUnnamedTryCatch.java"),
729                 expected);
730     }
731 
732     @Test
733     public void testUnusedLocalVariablePatternVariablesAllowUnnamedTry() throws Exception {
734         final String[] expected = CommonUtil.EMPTY_STRING_ARRAY;
735         verifyWithInlineConfigParser(
736                 getNonCompilablePath(
737                     "InputUnusedLocalVariableAllowUnnamedTryCatch.java"),
738                 expected);
739     }
740 
741     /**
742      * Use TestUtil.invokeStaticMethod to access the private static method
743      * isInsideLocalAnonInnerClass to test this because it produces optimization
744      * mutations that we can not kill using verifyWithInlineConfigParser.
745      */
746     @Test
747     public void testIsInsideLocalAnonInnerClass() throws Exception {
748         final DetailAST root = JavaParser.parseFile(
749                 new File(getPath("InputUnusedLocalVariableLambdaAnonInner.java")),
750                 JavaParser.Options.WITHOUT_COMMENTS);
751 
752         final DetailAST fieldObj = TestUtil.findTokenInAstByPredicate(root,
753                         ast -> {
754                             return ast.getType() == TokenTypes.VARIABLE_DEF
755                                     && "fieldObj".equals(ast.findFirstToken(TokenTypes.IDENT)
756                                     .getText());
757                         })
758                 .orElseThrow();
759         final DetailAST literalNewField = fieldObj.findFirstToken(TokenTypes.ASSIGN)
760                 .findFirstToken(TokenTypes.EXPR)
761                 .findFirstToken(TokenTypes.LITERAL_NEW);
762 
763         final boolean resultFalse = TestUtil.invokeStaticMethod(
764                 UnusedLocalVariableCheck.class,
765                 "isInsideLocalAnonInnerClass",
766                 Boolean.class,
767                 literalNewField);
768 
769         assertWithMessage("Should be false for field initialization (no SLIST in ancestry)")
770                 .that(resultFalse)
771                 .isFalse();
772 
773         final DetailAST localObj = TestUtil.findTokenInAstByPredicate(root,
774                         ast -> {
775                             return ast.getType() == TokenTypes.VARIABLE_DEF
776                                     && "localObj".equals(ast.findFirstToken(TokenTypes.IDENT)
777                                     .getText());
778                         })
779                 .orElseThrow();
780         final DetailAST literalNewLocal = localObj.findFirstToken(TokenTypes.ASSIGN)
781                 .findFirstToken(TokenTypes.EXPR)
782                 .findFirstToken(TokenTypes.LITERAL_NEW);
783 
784         final boolean resultTrue = TestUtil.invokeStaticMethod(
785                 UnusedLocalVariableCheck.class,
786                 "isInsideLocalAnonInnerClass",
787                 Boolean.class,
788                 literalNewLocal);
789 
790         assertWithMessage("Should be true for local variable initialization")
791                 .that(resultTrue)
792                 .isTrue();
793     }
794 
795 }