Skip to content

Commit 3305049

Browse files
authored
Fix RemoveUnusedImports keeping import only referenced in type attribution (#7336)
* Fix RemoveUnusedImports keeping import when type only appears in type attribution (#7333) When a type is only referenced through statically-imported methods/fields (e.g., `doSomething(DEFAULT_SOMETHING)` where `Something` is the parameter type), the non-static import `import org.example.a.Something` was incorrectly kept because `TypesInUse` includes types from AST type attribution. Add a check that verifies the imported type's simple name actually appears as an identifier in the source code before considering the import used. * Use reduce pattern for collectSourceIdentifierNames
1 parent 0972e56 commit 3305049

2 files changed

Lines changed: 76 additions & 0 deletions

File tree

rewrite-java-test/src/test/java/org/openrewrite/java/RemoveUnusedImportsTest.java

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1139,6 +1139,54 @@ public static void main(String[] args) {
11391139
);
11401140
}
11411141

1142+
@Issue("https://github.com/openrewrite/rewrite/issues/7333")
1143+
@Test
1144+
void removesImportUsedOnlyAsStaticImportParameterType() {
1145+
rewriteRun(
1146+
java(
1147+
"""
1148+
package org.example.a;
1149+
1150+
public class Something {
1151+
public static Something DEFAULT_SOMETHING = new Something();
1152+
1153+
public static void doSomething(Something something) {
1154+
System.out.println("Doing something... " + something);
1155+
}
1156+
}
1157+
"""
1158+
),
1159+
java(
1160+
"""
1161+
package org.example.b;
1162+
1163+
import org.example.a.Something;
1164+
1165+
import static org.example.a.Something.DEFAULT_SOMETHING;
1166+
import static org.example.a.Something.doSomething;
1167+
1168+
public class Test {
1169+
public static void main(String[] args) {
1170+
doSomething(DEFAULT_SOMETHING);
1171+
}
1172+
}
1173+
""",
1174+
"""
1175+
package org.example.b;
1176+
1177+
import static org.example.a.Something.DEFAULT_SOMETHING;
1178+
import static org.example.a.Something.doSomething;
1179+
1180+
public class Test {
1181+
public static void main(String[] args) {
1182+
doSomething(DEFAULT_SOMETHING);
1183+
}
1184+
}
1185+
"""
1186+
)
1187+
);
1188+
}
1189+
11421190
@Issue("https://github.com/openrewrite/rewrite/issues/3275")
11431191
@Test
11441192
void doesNotRemoveReferencedClassesBeingUsedAsParameters() {

rewrite-java/src/main/java/org/openrewrite/java/RemoveUnusedImports.java

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,10 @@ public J.CompilationUnit visitCompilationUnit(J.CompilationUnit cu, ExecutionCon
7373
// Collect all unqualified type references upfront for efficiency
7474
Set<String> unqualifiedTypeNames = collectUnqualifiedTypeNames(cu);
7575

76+
// Collect all identifier simple names from source (excluding imports) to detect
77+
// types that only appear in type attribution but not in actual source references
78+
Set<String> sourceIdentifierNames = collectSourceIdentifierNames(cu);
79+
7680
for (JavaType.Method method : cu.getTypesInUse().getUsedMethods()) {
7781
if (method.hasFlags(Flag.Static)) {
7882
methodsAndFieldsByTypeName.computeIfAbsent(method.getDeclaringType().getFullyQualifiedName(), t -> new TreeSet<>())
@@ -257,6 +261,11 @@ public J.CompilationUnit visitCompilationUnit(J.CompilationUnit cu, ExecutionCon
257261
} else {
258262
usedWildcardImports.add(target);
259263
}
264+
} else if (!sourceIdentifierNames.contains(qualid.getSimpleName())) {
265+
// The imported type's simple name doesn't appear anywhere in the source code;
266+
// it only appears in type attribution (e.g., as a parameter type of a statically imported method)
267+
anImport.used = false;
268+
changed = true;
260269
} else if (combinedTypes.stream().noneMatch(c -> {
261270
if ("*".equals(elem.getQualid().getSimpleName())) {
262271
return elem.getPackageName().equals(c.getPackageName());
@@ -509,6 +518,25 @@ private boolean isPartOfQualifiedReference(J.Identifier identifier) {
509518
}
510519
}.reduce(cu, new HashSet<>());
511520
}
521+
522+
/**
523+
* Collect all identifier simple names appearing in source code, excluding import statements.
524+
* Used to detect imports whose type name doesn't actually appear in the source.
525+
*/
526+
private Set<String> collectSourceIdentifierNames(J.CompilationUnit cu) {
527+
return new JavaIsoVisitor<Set<String>>() {
528+
@Override
529+
public J.Import visitImport(J.Import import_, Set<String> names) {
530+
return import_;
531+
}
532+
533+
@Override
534+
public J.Identifier visitIdentifier(J.Identifier identifier, Set<String> names) {
535+
names.add(identifier.getSimpleName());
536+
return super.visitIdentifier(identifier, names);
537+
}
538+
}.reduce(cu, new HashSet<>());
539+
}
512540
}
513541

514542
private static class ImportUsage {

0 commit comments

Comments
 (0)