View Javadoc
1   ///////////////////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code and other text files for adherence to a set of rules.
3   // Copyright (C) 2001-2024 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.imports;
21  
22  import static com.google.common.truth.Truth.assertWithMessage;
23  import static com.puppycrawl.tools.checkstyle.checks.imports.UnusedImportsCheck.MSG_KEY;
24  
25  import java.io.File;
26  import java.util.Arrays;
27  import java.util.List;
28  
29  import org.junit.jupiter.api.Test;
30  
31  import com.google.common.collect.ImmutableMap;
32  import com.puppycrawl.tools.checkstyle.AbstractModuleTestSupport;
33  import com.puppycrawl.tools.checkstyle.DefaultConfiguration;
34  import com.puppycrawl.tools.checkstyle.api.TokenTypes;
35  import com.puppycrawl.tools.checkstyle.utils.CommonUtil;
36  
37  public class UnusedImportsCheckTest extends AbstractModuleTestSupport {
38  
39      @Override
40      protected String getPackageLocation() {
41          return "com/puppycrawl/tools/checkstyle/checks/imports/unusedimports";
42      }
43  
44      @Test
45      public void testReferencedStateIsCleared() throws Exception {
46          final DefaultConfiguration checkConfig = createModuleConfig(UnusedImportsCheck.class);
47          final String inputWithoutWarnings = getPath("InputUnusedImportsWithoutWarnings.java");
48          final String inputWithWarnings = getPath("InputUnusedImportsCheckClearState.java");
49          final List<String> expectedFirstInput = Arrays.asList(CommonUtil.EMPTY_STRING_ARRAY);
50          final List<String> expectedSecondInput = Arrays.asList(
51                  "10:8: " + getCheckMessage(MSG_KEY, "java.util.Arrays"),
52                  "11:8: " + getCheckMessage(MSG_KEY, "java.util.List"),
53                  "12:8: " + getCheckMessage(MSG_KEY, "java.util.Set")
54          );
55          final File[] inputsWithWarningsFirst =
56              {new File(inputWithWarnings), new File(inputWithoutWarnings)};
57          final File[] inputsWithoutWarningFirst =
58              {new File(inputWithoutWarnings), new File(inputWithWarnings)};
59  
60          verify(createChecker(checkConfig), inputsWithWarningsFirst, ImmutableMap.of(
61                  inputWithoutWarnings, expectedFirstInput,
62                  inputWithWarnings, expectedSecondInput));
63          verify(createChecker(checkConfig), inputsWithoutWarningFirst, ImmutableMap.of(
64                  inputWithoutWarnings, expectedFirstInput,
65                  inputWithWarnings, expectedSecondInput));
66      }
67  
68      @Test
69      public void testWithoutProcessJavadoc() throws Exception {
70          final String[] expected = {
71              "11:8: " + getCheckMessage(MSG_KEY,
72                  "com.google.errorprone.annotations."
73                  + "concurrent.GuardedBy"),
74              "15:8: " + getCheckMessage(MSG_KEY, "java.lang.String"),
75              "17:8: " + getCheckMessage(MSG_KEY, "java.util.List"),
76              "18:8: " + getCheckMessage(MSG_KEY, "java.util.List"),
77              "21:8: " + getCheckMessage(MSG_KEY, "java.util.Enumeration"),
78              "24:8: " + getCheckMessage(MSG_KEY, "javax.swing.JToggleButton"),
79              "26:8: " + getCheckMessage(MSG_KEY, "javax.swing.BorderFactory"),
80              "31:15: " + getCheckMessage(MSG_KEY, "java.io.File.createTempFile"),
81              // "33:8: Unused import - java.awt.Component.", // Should be detected
82              "34:8: " + getCheckMessage(MSG_KEY, "java.awt.Graphics2D"),
83              "35:8: " + getCheckMessage(MSG_KEY, "java.awt.HeadlessException"),
84              "36:8: " + getCheckMessage(MSG_KEY, "java.awt.Label"),
85              "37:8: " + getCheckMessage(MSG_KEY, "java.util.Date"),
86              "38:8: " + getCheckMessage(MSG_KEY, "java.util.Calendar"),
87              "39:8: " + getCheckMessage(MSG_KEY, "java.util.BitSet"),
88              "41:8: " + getCheckMessage(MSG_KEY, "com.google.errorprone."
89                      + "annotations.CheckReturnValue"),
90              "42:8: " + getCheckMessage(MSG_KEY, "com.google.errorprone."
91                      + "annotations.CanIgnoreReturnValue"),
92              "43:8: " + getCheckMessage(MSG_KEY, "com.google.errorprone."
93                      + "annotations.CompatibleWith"),
94              "44:8: " + getCheckMessage(MSG_KEY,
95                  "com.google.errorprone.annotations.concurrent."
96                          + "LazyInit"),
97              "45:8: " + getCheckMessage(MSG_KEY,
98                  "com.google.errorprone.annotations.DoNotCall"),
99              "46:8: " + getCheckMessage(MSG_KEY,
100                 "com.google.errorprone.annotations.CompileTimeConstant"),
101             "47:8: " + getCheckMessage(MSG_KEY,
102                 "com.google.errorprone.annotations.FormatMethod"),
103             "48:8: " + getCheckMessage(MSG_KEY, "com.google.errorprone.annotations.FormatString"),
104         };
105         verifyWithInlineConfigParser(
106                 getPath("InputUnusedImports2.java"), expected);
107     }
108 
109     @Test
110     public void testProcessJavadoc() throws Exception {
111         final String[] expected = {
112             "11:8: " + getCheckMessage(MSG_KEY,
113                     "com.google.errorprone.annotations."
114                     + "concurrent.GuardedBy"),
115             "15:8: " + getCheckMessage(MSG_KEY, "java.lang.String"),
116             "17:8: " + getCheckMessage(MSG_KEY, "java.util.List"),
117             "18:8: " + getCheckMessage(MSG_KEY, "java.util.List"),
118             "21:8: " + getCheckMessage(MSG_KEY, "java.util.Enumeration"),
119             "24:8: " + getCheckMessage(MSG_KEY, "javax.swing.JToggleButton"),
120             "26:8: " + getCheckMessage(MSG_KEY, "javax.swing.BorderFactory"),
121             "31:15: " + getCheckMessage(MSG_KEY, "java.io.File.createTempFile"),
122             // "30:8: Unused import - java.awt.Component.", // Should be detected
123             "36:8: " + getCheckMessage(MSG_KEY, "java.awt.Label"),
124             "48:8: " + getCheckMessage(MSG_KEY, "com.google.errorprone.annotations.ForOverride"),
125         };
126         verifyWithInlineConfigParser(
127                 getPath("InputUnusedImports.java"), expected);
128     }
129 
130     @Test
131     public void testProcessJavadocWithLinkTag() throws Exception {
132         final String[] expected = CommonUtil.EMPTY_STRING_ARRAY;
133         verifyWithInlineConfigParser(
134                 getPath("InputUnusedImportsWithValueTag.java"), expected);
135     }
136 
137     @Test
138     public void testProcessJavadocWithBlockTagContainingMethodParameters() throws Exception {
139         final String[] expected = CommonUtil.EMPTY_STRING_ARRAY;
140         verifyWithInlineConfigParser(
141                 getPath("InputUnusedImportsWithBlockMethodParameters.java"), expected);
142     }
143 
144     @Test
145     public void testAnnotations() throws Exception {
146         final String[] expected = CommonUtil.EMPTY_STRING_ARRAY;
147         verifyWithInlineConfigParser(
148                 getNonCompilablePath("InputUnusedImportsAnnotations.java"), expected);
149     }
150 
151     @Test
152     public void testArrayRef() throws Exception {
153         final String[] expected = {
154             "13:8: " + getCheckMessage(MSG_KEY, "java.util.ArrayList"),
155         };
156         verifyWithInlineConfigParser(
157                 getPath("InputUnusedImportsArrayRef.java"), expected);
158     }
159 
160     @Test
161     public void testBug() throws Exception {
162         final String[] expected = CommonUtil.EMPTY_STRING_ARRAY;
163         verifyWithInlineConfigParser(
164                 getPath("InputUnusedImportsBug.java"), expected);
165     }
166 
167     @Test
168     public void testNewlinesInsideTags() throws Exception {
169         final String[] expected = CommonUtil.EMPTY_STRING_ARRAY;
170         verifyWithInlineConfigParser(
171                 getPath("InputUnusedImportsWithNewlinesInsideTags.java"), expected);
172     }
173 
174     @Test
175     public void testGetRequiredTokens() {
176         final UnusedImportsCheck testCheckObject =
177                 new UnusedImportsCheck();
178         final int[] actual = testCheckObject.getRequiredTokens();
179         final int[] expected = {
180             TokenTypes.IDENT,
181             TokenTypes.IMPORT,
182             TokenTypes.STATIC_IMPORT,
183             // Definitions that may contain Javadoc...
184             TokenTypes.PACKAGE_DEF,
185             TokenTypes.ANNOTATION_DEF,
186             TokenTypes.ANNOTATION_FIELD_DEF,
187             TokenTypes.ENUM_DEF,
188             TokenTypes.ENUM_CONSTANT_DEF,
189             TokenTypes.CLASS_DEF,
190             TokenTypes.INTERFACE_DEF,
191             TokenTypes.METHOD_DEF,
192             TokenTypes.CTOR_DEF,
193             TokenTypes.VARIABLE_DEF,
194             TokenTypes.RECORD_DEF,
195             TokenTypes.COMPACT_CTOR_DEF,
196             TokenTypes.OBJBLOCK,
197             TokenTypes.SLIST,
198         };
199 
200         assertWithMessage("Default required tokens are invalid")
201             .that(actual)
202             .isEqualTo(expected);
203     }
204 
205     @Test
206     public void testGetAcceptableTokens() {
207         final UnusedImportsCheck testCheckObject =
208                 new UnusedImportsCheck();
209         final int[] actual = testCheckObject.getAcceptableTokens();
210         final int[] expected = {
211             TokenTypes.IDENT,
212             TokenTypes.IMPORT,
213             TokenTypes.STATIC_IMPORT,
214             // Definitions that may contain Javadoc...
215             TokenTypes.PACKAGE_DEF,
216             TokenTypes.ANNOTATION_DEF,
217             TokenTypes.ANNOTATION_FIELD_DEF,
218             TokenTypes.ENUM_DEF,
219             TokenTypes.ENUM_CONSTANT_DEF,
220             TokenTypes.CLASS_DEF,
221             TokenTypes.INTERFACE_DEF,
222             TokenTypes.METHOD_DEF,
223             TokenTypes.CTOR_DEF,
224             TokenTypes.VARIABLE_DEF,
225             TokenTypes.RECORD_DEF,
226             TokenTypes.COMPACT_CTOR_DEF,
227             TokenTypes.OBJBLOCK,
228             TokenTypes.SLIST,
229         };
230 
231         assertWithMessage("Default acceptable tokens are invalid")
232             .that(actual)
233             .isEqualTo(expected);
234     }
235 
236     @Test
237     public void testFileInUnnamedPackage() throws Exception {
238         final String[] expected = {
239             "12:8: " + getCheckMessage(MSG_KEY, "java.util.Arrays"),
240             "13:8: " + getCheckMessage(MSG_KEY, "java.lang.String"),
241         };
242         verifyWithInlineConfigParser(
243                 getNonCompilablePath("InputUnusedImportsFileInUnnamedPackage.java"),
244             expected);
245     }
246 
247     @Test
248     public void testImportsFromJavaLang() throws Exception {
249         final String[] expected = {
250             "10:8: " + getCheckMessage(MSG_KEY, "java.lang.String"),
251             "11:8: " + getCheckMessage(MSG_KEY, "java.lang.Math"),
252             "12:8: " + getCheckMessage(MSG_KEY, "java.lang.Class"),
253             "13:8: " + getCheckMessage(MSG_KEY, "java.lang.Exception"),
254             "14:8: " + getCheckMessage(MSG_KEY, "java.lang.Runnable"),
255             "15:8: " + getCheckMessage(MSG_KEY, "java.lang.RuntimeException"),
256             "16:8: " + getCheckMessage(MSG_KEY, "java.lang.ProcessBuilder"),
257             "17:8: " + getCheckMessage(MSG_KEY, "java.lang.Double"),
258             "18:8: " + getCheckMessage(MSG_KEY, "java.lang.Integer"),
259             "19:8: " + getCheckMessage(MSG_KEY, "java.lang.Float"),
260             "20:8: " + getCheckMessage(MSG_KEY, "java.lang.Short"),
261         };
262         verifyWithInlineConfigParser(
263                 getPath("InputUnusedImportsFromJavaLang.java"), expected);
264     }
265 
266     @Test
267     public void testImportsJavadocQualifiedName() throws Exception {
268         final String[] expected = {
269             "11:8: " + getCheckMessage(MSG_KEY, "java.util.List"),
270         };
271         verifyWithInlineConfigParser(
272                 getPath("InputUnusedImportsJavadocQualifiedName.java"), expected);
273     }
274 
275     @Test
276     public void testSingleWordPackage() throws Exception {
277         final String[] expected = {
278             "10:8: " + getCheckMessage(MSG_KEY, "module"),
279         };
280         verifyWithInlineConfigParser(
281                 getNonCompilablePath("InputUnusedImportsSingleWordPackage.java"),
282                 expected);
283     }
284 
285     @Test
286     public void testRecordsAndCompactCtors() throws Exception {
287         final String[] expected = {
288             "19:8: " + getCheckMessage(MSG_KEY, "javax.swing.JToolBar"),
289             "20:8: " + getCheckMessage(MSG_KEY, "javax.swing.JToggleButton"),
290         };
291         verifyWithInlineConfigParser(
292                 getNonCompilablePath("InputUnusedImportsRecordsAndCompactCtors.java"),
293                 expected);
294     }
295 
296     @Test
297     public void testShadowedImports() throws Exception {
298         final String[] expected = {
299             "12:8: " + getCheckMessage(MSG_KEY, "java.util.Map"),
300             "13:8: " + getCheckMessage(MSG_KEY, "java.util.Set"),
301             "16:8: " + getCheckMessage(MSG_KEY, "com.puppycrawl.tools.checkstyle.checks.imports."
302                     + "unusedimports.InputUnusedImportsShadowed"),
303         };
304         verifyWithInlineConfigParser(
305                 getPath("InputUnusedImportsShadowed.java"), expected);
306     }
307 
308     @Test
309     public void testUnusedImports3() throws Exception {
310         final String[] expected = {
311             "11:8: " + getCheckMessage(MSG_KEY, "java.awt.Rectangle"),
312             "13:8: " + getCheckMessage(MSG_KEY, "java.awt.event.KeyEvent"),
313         };
314         verifyWithInlineConfigParser(
315                 getPath("InputUnusedImports3.java"), expected);
316     }
317 
318     @Test
319     public void testStateIsClearedOnBeginTreeCollect() throws Exception {
320         final String file1 = getNonCompilablePath(
321                 "InputUnusedImportsRecordsAndCompactCtors.java");
322         final String file2 = getNonCompilablePath(
323                 "InputUnusedImportsSingleWordPackage.java");
324         final List<String> expectedFirstInput = List.of(
325             "19:8: " + getCheckMessage(MSG_KEY, "javax.swing.JToolBar"),
326             "20:8: " + getCheckMessage(MSG_KEY, "javax.swing.JToggleButton")
327         );
328         final List<String> expectedSecondInput = List.of(
329             "10:8: " + getCheckMessage(MSG_KEY, "module")
330         );
331         verifyWithInlineConfigParser(file1, file2, expectedFirstInput, expectedSecondInput);
332     }
333 
334     @Test
335     public void testStaticMethodRefImports() throws Exception {
336         final String[] expected = {
337             "26:15: " + getCheckMessage(MSG_KEY, "java.lang.String.format"),
338             "27:15: " + getCheckMessage(MSG_KEY, "java.util.Arrays.sort"),
339             "28:15: " + getCheckMessage(MSG_KEY, "java.util.List.of"),
340             "29:15: " + getCheckMessage(MSG_KEY, "java.util.Collections.emptyMap"),
341         };
342         verifyWithInlineConfigParser(
343                 getPath("InputUnusedImportsFromStaticMethodRef.java"), expected);
344     }
345 
346     @Test
347     public void testStaticMethodRefImportsExtended() throws Exception {
348         final String[] expected = {
349             "17:8: " + getCheckMessage(MSG_KEY, "java.util.Objects"),
350             "18:15: " + getCheckMessage(MSG_KEY, "java.util.Arrays.toString"),
351             "19:15: " + getCheckMessage(MSG_KEY, "java.util.Arrays.asList"),
352             "20:15: " + getCheckMessage(MSG_KEY, "java.lang.Integer.parseInt"),
353             "21:15: " + getCheckMessage(MSG_KEY, "java.util.Collections.emptyList"),
354         };
355         verifyWithInlineConfigParser(
356                 getPath("InputUnusedImportsFromStaticMethodRefExtended.java"), expected);
357     }
358 
359     @Test
360     public void testStaticMethodRefImportsWithJavadocDisabled() throws Exception {
361         final String[] expected = {
362             "24:8: " + getCheckMessage(MSG_KEY, "java.util.Arrays"),
363             "25:15: " + getCheckMessage(MSG_KEY, "java.lang.Integer.parseInt"),
364             "26:15: " + getCheckMessage(MSG_KEY, "java.lang.String.format"),
365             "27:15: " + getCheckMessage(MSG_KEY, "java.util.List.of"),
366             "28:15: " + getCheckMessage(MSG_KEY, "java.util.Collections.emptyMap"),
367         };
368         verifyWithInlineConfigParser(
369                 getPath("InputUnusedImportsFromStaticMethodRefJavadocDisabled.java"), expected);
370     }
371 
372     @Test
373     public void testStaticMethodRefImportsInDocsOnly() throws Exception {
374         final String[] expected = {
375             "11:8: " + getCheckMessage(MSG_KEY, "java.lang.Integer"),
376             "12:15: " + getCheckMessage(MSG_KEY, "java.util.Collections.emptyEnumeration"),
377             "13:15: " + getCheckMessage(MSG_KEY, "java.util.Arrays.sort"),
378             "14:15: " + getCheckMessage(MSG_KEY, "java.util.Collections.shuffle"),
379         };
380         verifyWithInlineConfigParser(
381                 getPath("InputUnusedImportsFromStaticMethodRefInDocsOnly.java"), expected);
382     }
383 
384 }