-
Notifications
You must be signed in to change notification settings - Fork 18
Issue #146: Add recipe for UnusedImports #151
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||
|---|---|---|---|---|
| @@ -0,0 +1,92 @@ | ||||
| /////////////////////////////////////////////////////////////////////////////////////////////// | ||||
| // checkstyle-openrewrite-recipes: Automatically fix Checkstyle violations with OpenRewrite. | ||||
| // Copyright (C) 2025 The Checkstyle OpenRewrite Recipes Authors | ||||
| // | ||||
| // Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| // you may not use this file except in compliance with the License. | ||||
| // You may obtain a copy of the License at | ||||
| // | ||||
| // http://www.apache.org/licenses/LICENSE-2.0 | ||||
| // | ||||
| // Unless required by applicable law or agreed to in writing, software | ||||
| // distributed under the License is distributed on an "AS IS" BASIS, | ||||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| // See the License for the specific language governing permissions and | ||||
| // limitations under the License. | ||||
| /////////////////////////////////////////////////////////////////////////////////////////////// | ||||
|
|
||||
| package org.checkstyle.autofix.recipe; | ||||
|
|
||||
| import java.nio.file.Path; | ||||
| import java.util.List; | ||||
|
|
||||
| import org.checkstyle.autofix.parser.CheckstyleViolation; | ||||
| import org.openrewrite.ExecutionContext; | ||||
| import org.openrewrite.Recipe; | ||||
| import org.openrewrite.TreeVisitor; | ||||
| import org.openrewrite.java.JavaIsoVisitor; | ||||
| import org.openrewrite.java.tree.J; | ||||
|
|
||||
| /** | ||||
| * Fixes Checkstyle UnusedImports violations by removing unused imports. | ||||
| */ | ||||
| public class UnusedImports extends Recipe { | ||||
|
|
||||
| private final List<CheckstyleViolation> violations; | ||||
|
|
||||
| public UnusedImports(List<CheckstyleViolation> violations) { | ||||
| this.violations = violations; | ||||
| } | ||||
|
|
||||
| @Override | ||||
| public String getDisplayName() { | ||||
| return "UnusedImports Recipe"; | ||||
| } | ||||
|
|
||||
| @Override | ||||
| public String getDescription() { | ||||
| return "Remove unused imports"; | ||||
| } | ||||
|
|
||||
| @Override | ||||
| public TreeVisitor<?, ExecutionContext> getVisitor() { | ||||
| return new UnusedImports.UnusedImportsVisitor(); | ||||
| } | ||||
|
|
||||
| private final class UnusedImportsVisitor extends JavaIsoVisitor<ExecutionContext> { | ||||
|
|
||||
| private static final String DOT_OPERATOR = "."; | ||||
|
|
||||
| private Path sourcePath; | ||||
|
|
||||
| @Override | ||||
| public J.CompilationUnit visitCompilationUnit(J.CompilationUnit cu, | ||||
| ExecutionContext executionContext) { | ||||
|
|
||||
| this.sourcePath = cu.getSourcePath(); | ||||
| return cu.withImports( | ||||
| cu.getImports().stream() | ||||
| .filter(importStmt -> !isAtViolationLocation(importStmt)) | ||||
| .toList() | ||||
| ); | ||||
| } | ||||
|
|
||||
| private String createMessage(J.Import literal) { | ||||
| String fullImport = literal.getTypeName(); | ||||
| if (literal.isStatic()) { | ||||
| fullImport += DOT_OPERATOR + literal.getQualid().getSimpleName(); | ||||
| } | ||||
| return "Unused import - " + fullImport + DOT_OPERATOR; | ||||
| } | ||||
|
|
||||
| private boolean isAtViolationLocation(J.Import literal) { | ||||
|
|
||||
| final String message = createMessage(literal); | ||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. strange that we rely on message .... why you isAtViolationLocation is so different from: checkstyle-openrewrite-recipes/src/main/java/org/checkstyle/autofix/recipe/UpperEll.java Line 90 in 14a2000
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i think to not match the violation on the basis of line and column basically what i did is extract each import from lst and match that import with violation message import but as suggested by you that the language is a problem.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's do as all other for now, ones we get better model, we will refactor all at ones
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. my exams are going on i will continue this after my exams |
||||
| return violations.stream().anyMatch(violation -> { | ||||
| final Path absolutePath = violation.getFilePath(); | ||||
| return violation.getMessage().equals(message) | ||||
| && absolutePath.endsWith(sourcePath); | ||||
| }); | ||||
| } | ||||
| } | ||||
| } | ||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,69 @@ | ||
| /////////////////////////////////////////////////////////////////////////////////////////////// | ||
| // checkstyle-openrewrite-recipes: Automatically fix Checkstyle violations with OpenRewrite. | ||
| // Copyright (C) 2025 The Checkstyle OpenRewrite Recipes Authors | ||
| // | ||
| // Licensed under the Apache License, Version 2.0 (the "License"); | ||
| // you may not use this file except in compliance with the License. | ||
| // You may obtain a copy of the License at | ||
| // | ||
| // http://www.apache.org/licenses/LICENSE-2.0 | ||
| // | ||
| // Unless required by applicable law or agreed to in writing, software | ||
| // distributed under the License is distributed on an "AS IS" BASIS, | ||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| // See the License for the specific language governing permissions and | ||
| // limitations under the License. | ||
| /////////////////////////////////////////////////////////////////////////////////////////////// | ||
|
|
||
| package org.checkstyle.autofix.recipe; | ||
|
|
||
| import static com.google.common.truth.Truth.assertWithMessage; | ||
|
|
||
| import org.checkstyle.autofix.parser.ReportParser; | ||
| import org.junit.jupiter.api.Test; | ||
|
|
||
| public class UnusedImportsTest extends AbstractRecipeTestSupport { | ||
|
|
||
| @Override | ||
| protected String getSubpackage() { | ||
| return "unusedimports"; | ||
| } | ||
|
|
||
| @Test | ||
| public void checkDescription() { | ||
| final UnusedImports recipe = new UnusedImports(null); | ||
| final String expectedDescription = "Remove unused imports"; | ||
| assertWithMessage("Invalid description") | ||
| .that(recipe.getDescription()) | ||
| .isEqualTo(expectedDescription); | ||
| } | ||
|
|
||
| @Test | ||
| public void checkDisplayName() { | ||
| final UnusedImports recipe = new UnusedImports(null); | ||
| final String expectedDisplayName = "UnusedImports Recipe"; | ||
| assertWithMessage("Invalid display name") | ||
| .that(recipe.getDisplayName()) | ||
| .isEqualTo(expectedDisplayName); | ||
| } | ||
|
|
||
| @RecipeTest | ||
| void unusedImportsCaseOne(ReportParser parser) throws Exception { | ||
| verify(parser, "UnusedCaseOne"); | ||
| } | ||
|
|
||
| @RecipeTest | ||
| void unusedImportsCaseTwo(ReportParser parser) throws Exception { | ||
| verify(parser, "UnusedCaseTwo"); | ||
| } | ||
|
|
||
| @RecipeTest | ||
| void unusedImportsCaseThree(ReportParser parser) throws Exception { | ||
| verify(parser, "UnusedCaseOne", "UnusedCaseTwo"); | ||
| } | ||
|
|
||
| @RecipeTest | ||
| void unusedImportsCaseFour(ReportParser parser) throws Exception { | ||
| verify(parser, "UnusedCaseThree"); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,40 @@ | ||
| --- src/test/resources/org/checkstyle/autofix/recipe/unusedimports/unusedcaseone/InputUnusedCaseOne.java | ||
| +++ src/test/resources/org/checkstyle/autofix/recipe/unusedimports/unusedcaseone/OutputUnusedCaseOne.java | ||
| @@ -11,28 +11,17 @@ | ||
|
|
||
| import java.io.*; | ||
| import java.lang.*; | ||
| -import java.lang.String; // violation 'Unused import - java.lang.String.' | ||
| - | ||
| -import java.util.List; // violation 'Unused import - java.util.List.' | ||
| -import java.util.List; // violation 'Unused import - java.util.List.' | ||
| import java.lang.*; | ||
| import java.util.Iterator; | ||
| -import java.util.Enumeration; // violation 'Unused import - java.util.Enumeration.' | ||
| import java.util.Arrays; | ||
| import javax.swing.JToolBar; | ||
| -import javax.swing.JToggleButton; // violation 'Unused import - javax.swing.JToggleButton.' | ||
| - | ||
| -import javax.swing.BorderFactory; // violation 'Unused import - javax.swing.BorderFactory.' | ||
|
|
||
| import static java.io.File.listRoots; | ||
|
|
||
| import static javax.swing.WindowConstants.*; | ||
| -import static java.io.File. // violation 'Unused import - java.io.File.createTempFile.' | ||
| - createTempFile; | ||
|
|
||
| import java.awt.Graphics2D; | ||
| import java.awt.HeadlessException; | ||
| -import java.awt.Label; // violation 'Unused import - java.awt.Label.' | ||
| import java.util.Date; | ||
| import java.util.Calendar; | ||
| import java.util.BitSet; | ||
| @@ -42,7 +31,7 @@ | ||
| * Here's an import used only by javadoc: {@link Date}. | ||
| * @see Calendar Should avoid unused import for Calendar | ||
| **/ | ||
| -public class InputUnusedCaseOne { | ||
| +public class OutputUnusedCaseOne { | ||
|
|
||
| private Class mUse1 = null; | ||
| private Class mUse2 = java.io.File.class; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,79 @@ | ||
| /*xml | ||
| <module name="Checker"> | ||
| <module name="TreeWalker"> | ||
| <module name="com.puppycrawl.tools.checkstyle.checks.imports.UnusedImportsCheck"/> | ||
| </module> | ||
| </module> | ||
|
|
||
| */ | ||
|
|
||
| package org.checkstyle.autofix.recipe.unusedimports.unusedcaseone; | ||
|
|
||
| import java.io.*; | ||
| import java.lang.*; | ||
| import java.lang.String; // violation 'Unused import - java.lang.String.' | ||
|
|
||
| import java.util.List; // violation 'Unused import - java.util.List.' | ||
| import java.util.List; // violation 'Unused import - java.util.List.' | ||
| import java.lang.*; | ||
| import java.util.Iterator; | ||
| import java.util.Enumeration; // violation 'Unused import - java.util.Enumeration.' | ||
| import java.util.Arrays; | ||
| import javax.swing.JToolBar; | ||
| import javax.swing.JToggleButton; // violation 'Unused import - javax.swing.JToggleButton.' | ||
|
|
||
| import javax.swing.BorderFactory; // violation 'Unused import - javax.swing.BorderFactory.' | ||
|
|
||
| import static java.io.File.listRoots; | ||
|
|
||
| import static javax.swing.WindowConstants.*; | ||
| import static java.io.File. // violation 'Unused import - java.io.File.createTempFile.' | ||
| createTempFile; | ||
|
|
||
| import java.awt.Graphics2D; | ||
| import java.awt.HeadlessException; | ||
| import java.awt.Label; // violation 'Unused import - java.awt.Label.' | ||
| import java.util.Date; | ||
| import java.util.Calendar; | ||
| import java.util.BitSet; | ||
|
|
||
| /** | ||
| * Test case for imports | ||
| * Here's an import used only by javadoc: {@link Date}. | ||
| * @see Calendar Should avoid unused import for Calendar | ||
| **/ | ||
| public class InputUnusedCaseOne { | ||
|
|
||
| private Class mUse1 = null; | ||
| private Class mUse2 = java.io.File.class; | ||
| private Class mUse3 = Iterator[].class; | ||
| private Class mUse4 = java.util.Enumeration[].class; | ||
|
|
||
| { | ||
| int[] x = {}; | ||
| Arrays.sort(x); | ||
| Object obj = javax.swing.BorderFactory.createEmptyBorder(); | ||
| File[] files = listRoots(); | ||
| } | ||
|
|
||
| private JToolBar.Separator mSep = null; | ||
|
|
||
| private Object mUse5 = new Object(); | ||
|
|
||
| private Object mUse6 = new javax.swing.JToggleButton.ToggleButtonModel(); | ||
|
|
||
| private int Component; | ||
|
|
||
| /** | ||
| * method comment with JavaDoc-only import {@link BitSet} | ||
| */ | ||
| public void Label() {} | ||
|
|
||
| /** | ||
| * Renders to a {@linkplain Graphics2D graphics context}. | ||
| * @throws HeadlessException if no graphis environment can be found. | ||
| */ | ||
| public void render() {} | ||
|
|
||
| public void aMethodWithManyLinks() {} | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,68 @@ | ||
| /*xml | ||
| <module name="Checker"> | ||
| <module name="TreeWalker"> | ||
| <module name="com.puppycrawl.tools.checkstyle.checks.imports.UnusedImportsCheck"/> | ||
| </module> | ||
| </module> | ||
|
|
||
| */ | ||
|
|
||
| package org.checkstyle.autofix.recipe.unusedimports.unusedcaseone; | ||
|
|
||
| import java.io.*; | ||
| import java.lang.*; | ||
| import java.lang.*; | ||
| import java.util.Iterator; | ||
| import java.util.Arrays; | ||
| import javax.swing.JToolBar; | ||
|
|
||
| import static java.io.File.listRoots; | ||
|
|
||
| import static javax.swing.WindowConstants.*; | ||
|
|
||
| import java.awt.Graphics2D; | ||
| import java.awt.HeadlessException; | ||
| import java.util.Date; | ||
| import java.util.Calendar; | ||
| import java.util.BitSet; | ||
|
|
||
| /** | ||
| * Test case for imports | ||
| * Here's an import used only by javadoc: {@link Date}. | ||
| * @see Calendar Should avoid unused import for Calendar | ||
| **/ | ||
| public class OutputUnusedCaseOne { | ||
|
|
||
| private Class mUse1 = null; | ||
| private Class mUse2 = java.io.File.class; | ||
| private Class mUse3 = Iterator[].class; | ||
| private Class mUse4 = java.util.Enumeration[].class; | ||
|
|
||
| { | ||
| int[] x = {}; | ||
| Arrays.sort(x); | ||
| Object obj = javax.swing.BorderFactory.createEmptyBorder(); | ||
| File[] files = listRoots(); | ||
| } | ||
|
|
||
| private JToolBar.Separator mSep = null; | ||
|
|
||
| private Object mUse5 = new Object(); | ||
|
|
||
| private Object mUse6 = new javax.swing.JToggleButton.ToggleButtonModel(); | ||
|
|
||
| private int Component; | ||
|
|
||
| /** | ||
| * method comment with JavaDoc-only import {@link BitSet} | ||
| */ | ||
| public void Label() {} | ||
|
|
||
| /** | ||
| * Renders to a {@linkplain Graphics2D graphics context}. | ||
| * @throws HeadlessException if no graphis environment can be found. | ||
| */ | ||
| public void render() {} | ||
|
|
||
| public void aMethodWithManyLinks() {} | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cu --> compileUnit, compilationUnit