-
Notifications
You must be signed in to change notification settings - Fork 445
RemoveUnusedImports
creating ambiguous imports
#5714
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
Merged
Merged
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
## Problem The `RemoveUnusedImports` recipe was incorrectly creating ambiguous imports by unfolding wildcard imports for types that were only used in fully qualified form. For example, when `java.util.Date` was used as `new java.util.Date()`, the wildcard import `import java.util.*;` would be replaced with `import java.util.Date;` instead of being removed entirely. ## Root Cause The original implementation lacked proper detection of qualified vs unqualified type references. During wildcard import unfolding, the recipe would add imports for all types found in the `typesInUse` collection without checking whether those types were actually used in unqualified form or only in fully qualified form. ## Solution Enhanced the `RemoveUnusedImports` recipe in `/rewrite-java/src/main/java/org/openrewrite/java/RemoveUnusedImports.java` with: ### 1. Efficient Unqualified Reference Collection - **Added `collectUnqualifiedTypeNames()` method**: Performs a single AST traversal to collect all fully qualified names of types used in unqualified form - **Performance optimization**: Changed from O(n*m) complexity to O(m + n) by replacing multiple AST traversals with a single pass and fast set lookups - **Smart qualified reference detection**: Uses `isPartOfQualifiedReference()` helper to walk up the AST cursor and detect when an identifier is part of a `J.FieldAccess` chain ### 2. Improved Wildcard Unfolding Logic - **Line 246-247**: Modified wildcard unfolding to filter types using `unqualifiedTypeNames.contains(fqType.getFullyQualifiedName())` - **Lines 258-262**: Enhanced logic to completely remove wildcard imports when no types are used unqualified - **Lines 282-284, 317**: Added defensive checks to prevent IndexOutOfBoundsException ### 3. Edge Case Handling - **Added test case**: `keepImportWhenBothQualifiedAndUnqualifiedUsageExists()` verifies that imports are preserved when a type is used both qualified and unqualified in the same file - **Mixed usage support**: Correctly handles scenarios where `Date` and `java.util.Date` appear in the same file ## Key Changes **RemoveUnusedImports.java:** - Lines 87-88: Added upfront collection of unqualified type names - Lines 473-528: New `collectUnqualifiedTypeNames()` method with AST traversal logic - Lines 502-524: Helper method `isPartOfQualifiedReference()` for qualified reference detection - Line 246: Replaced inefficient per-type checking with fast set lookup **RemoveUnusedImportsTest.java:** - Lines 2215-2230: Added edge case test for mixed qualified/unqualified usage ## Verification - ✅ Original failing test `onlyFullyQualifiedUsageShouldRemoveWildcard()` now passes - ✅ New edge case test `keepImportWhenBothQualifiedAndUnqualifiedUsageExists()` passes - ✅ All 63 existing `RemoveUnusedImports` tests continue to pass with no regressions - ✅ Significant performance improvement for files with multiple wildcard imports ## Test Cases Covered 1. **Fully qualified only**: `java.util.Date` usage → wildcard import removed entirely 2. **Unqualified only**: `Date` usage → wildcard import unfolded to specific import 3. **Mixed usage**: Both `Date` and `java.util.Date` → import preserved due to unqualified reference 4. **Conflicting names**: Multiple packages with same class name → correct import precedence maintained Fixes: - #5703
rewrite-java-test/src/test/java/org/openrewrite/java/RemoveUnusedImportsTest.java
Outdated
Show resolved
Hide resolved
rewrite-java-test/src/test/java/org/openrewrite/java/RemoveUnusedImportsTest.java
Outdated
Show resolved
Hide resolved
rewrite-java-test/src/test/java/org/openrewrite/java/RemoveUnusedImportsTest.java
Outdated
Show resolved
Hide resolved
rewrite-java-test/src/test/java/org/openrewrite/java/RemoveUnusedImportsTest.java
Outdated
Show resolved
Hide resolved
timtebeek
approved these changes
Jul 26, 2025
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.
Nice to see some of these longer standing issues resolved! I've applied the recipe here as well, and seems like we can now safely enable it going forward. Great!
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
RemoveUnusedImports
creating ambiguous importsProblem
The
RemoveUnusedImports
recipe was incorrectly creating ambiguous imports by unfolding wildcard imports fortypes that were only used in fully qualified form. For example, when
java.util.Date
was used asnew java.util.Date()
, the wildcard importimport java.util.*;
would be replaced withimport java.util.Date;
instead of being removed entirely.
Root Cause
The original implementation lacked proper detection of qualified vs unqualified type references. During
wildcard import unfolding, the recipe would add imports for all types found in the
typesInUse
collectionwithout checking whether those types were actually used in unqualified form or only in fully qualified form.
Solution
Enhanced the
RemoveUnusedImports
recipe in/rewrite-java/src/main/java/org/openrewrite/java/RemoveUnusedImports.java
with:1. Efficient Unqualified Reference Collection
collectUnqualifiedTypeNames()
method: Performs a single AST traversal to collect all fullyqualified names of types used in unqualified form
with a single pass and fast set lookups
isPartOfQualifiedReference()
helper to walk up the AST cursorand detect when an identifier is part of a
J.FieldAccess
chain2. Improved Wildcard Unfolding Logic
unqualifiedTypeNames.contains(fqType.getFullyQualifiedName())
3. Edge Case Handling
keepImportWhenBothQualifiedAndUnqualifiedUsageExists()
verifies that imports arepreserved when a type is used both qualified and unqualified in the same file
Date
andjava.util.Date
appear in the samefile
Key Changes
RemoveUnusedImports.java:
collectUnqualifiedTypeNames()
method with AST traversal logicisPartOfQualifiedReference()
for qualified reference detectionRemoveUnusedImportsTest.java:
Verification
onlyFullyQualifiedUsageShouldRemoveWildcard()
now passeskeepImportWhenBothQualifiedAndUnqualifiedUsageExists()
passesRemoveUnusedImports
tests continue to pass with no regressionsTest Cases Covered
java.util.Date
usage → wildcard import removed entirelyDate
usage → wildcard import unfolded to specific importDate
andjava.util.Date
→ import preserved due to unqualified referenceRemoveUnusedImports
creating ambiguous imports when there's explicit imports accompanying wildcards #5703