Fixes #149.
What was wrong
Removing unused imports dropped every import from java.lang, used or not. A single-type import shadows a type with the same simple name declared in the file's own package, and one brought in by an import on demand (JLS 6.4.1). So a used import java.lang.Byte; can be the only thing that binds Byte to the JDK class, and removing it makes the name ambiguous or binds it to the other class:
package com.company.project.bar;
import com.company.project.foo.*; // also declares a Byte
import java.lang.Byte;
public class Test {
Byte foo() {
return null;
}
}
Until now the java.lang.Byte import was removed. I checked this with a com.company.project.foo.Byte class next to the file: the input compiles, but the output of 21.X.X fails with reference to Byte is ambiguous. Now the import stays, because Byte is used. Unused java.lang imports and import java.lang.*; are still removed, and so are imports from the file's own package, which can never disambiguate anything.
The change
The commit is palantir#1778 by Zayan Khan, cherry-picked with -x (c9d7b75), with the author and the message kept. Upstream closed the PR unmerged; it fixes palantir#1334. Its CLAUDE.md and changelog commits are left out. The only conflict was in RemoveUnusedImportsTest, whose table has more rows here; upstream's rows come after ours.
Tests
The first commit adds upstream's five rows to RemoveUnusedImportsTest and changes the existing row whose output now keeps import java.lang.Foo;.
The second commit is ours. It moves the tests of unused import removal into case files under test-cases/imports/, one file per test, named after what it checks:
- 19 of the table's 23 rows and the one test of
RemoveUnusedImportsCaseLabelsTest became cases. A case runs the whole formatter, so its after block is the row's expected output, formatted.
- Four rows stay in
RemoveUnusedImportsTest as plain tests with text blocks:
With the RemoveUnusedImports of 21.X.X, four cases fail: each loses a java.lang import it uses. With the fix all of them pass.
FormatterCasesTest: 887 → 907
RemoveUnusedImportsTest: 18 → 4 (the module import is skipped on JDK 21; on JDK 25 all four run)
RemoveUnusedImportsCaseLabelsTest: removed, its test is now a case
open-java-format: 1408 → 1413 tests
./gradlew test --rerun passes for all modules (Gradle plugin 51, IntelliJ plugin 10), and the 907 cases also pass on JDK 25
Effect on output
Of the 15,747 files of the JDK 21 sources, 13 format differently. Each of them keeps a java.lang import that it uses (String, Integer, Character, RuntimePermission and others), and a second run changes none of them. In two of these files a comment that belongs to such an import no longer ends up somewhere else:
// java import
//
+import java.lang.Comparable; // to be substituted for jdk1.1.x
import java.util.Set;
-// to be substituted for jdk1.1.x
That is javax/management/openmbean/OpenMBeanParameterInfo.java. In java/util/prefs/Preferences.java, the comment // These imports needed only as a workaround for a JavaDoc bug stays with its import, where before it was left alone after the import block.
Because this changes output, it counts toward the second number of the version.