diff --git a/CHANGES.md b/CHANGES.md index eb93716703..6bff21096a 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -12,6 +12,7 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format ( ## [Unreleased] ### Fixed - GrEclipse no longer leaves a `file%3A...` directory in the working directory. ([#2019](https://github.com/diffplug/spotless/issues/2019)) +- `RemoveUnusedImportsStep` and `GoogleJavaFormatStep` no longer remove imports that are only used by links in Markdown doc comments (`///`, JDK 23+). ([#3161](https://github.com/diffplug/spotless/issues/3161)) ## [4.11.0] - 2026-11-02 ### Added diff --git a/lib/src/googleJavaFormat/java/com/diffplug/spotless/glue/java/GoogleJavaFormatFormatterFunc.java b/lib/src/googleJavaFormat/java/com/diffplug/spotless/glue/java/GoogleJavaFormatFormatterFunc.java index 859d728151..ab0e1d6e4f 100644 --- a/lib/src/googleJavaFormat/java/com/diffplug/spotless/glue/java/GoogleJavaFormatFormatterFunc.java +++ b/lib/src/googleJavaFormat/java/com/diffplug/spotless/glue/java/GoogleJavaFormatFormatterFunc.java @@ -26,7 +26,6 @@ import com.google.googlejavaformat.java.ImportOrderer; import com.google.googlejavaformat.java.JavaFormatterOptions; import com.google.googlejavaformat.java.JavaFormatterOptions.Style; -import com.google.googlejavaformat.java.RemoveUnusedImports; import com.google.googlejavaformat.java.StringWrapper; import com.diffplug.spotless.FormatterFunc; @@ -116,7 +115,7 @@ private static JavaFormatterOptions.Builder disableCleanup(JavaFormatterOptions. @Nonnull public String apply(@Nonnull String input) throws Exception { String formatted = formatter.formatSource(input); - String removedUnused = RemoveUnusedImports.removeUnusedImports(formatted); + String removedUnused = GoogleJavaFormatUnusedImports.removeUnusedImports(formatted); String sortedImports = ImportOrderer.reorderImports(removedUnused, reorderImports ? formatterStyle : Style.GOOGLE); return reflowLongStrings(sortedImports); } diff --git a/lib/src/googleJavaFormat/java/com/diffplug/spotless/glue/java/GoogleJavaFormatRemoveUnusedImporterFormatterFunc.java b/lib/src/googleJavaFormat/java/com/diffplug/spotless/glue/java/GoogleJavaFormatRemoveUnusedImporterFormatterFunc.java index de21a18795..852765c1cd 100644 --- a/lib/src/googleJavaFormat/java/com/diffplug/spotless/glue/java/GoogleJavaFormatRemoveUnusedImporterFormatterFunc.java +++ b/lib/src/googleJavaFormat/java/com/diffplug/spotless/glue/java/GoogleJavaFormatRemoveUnusedImporterFormatterFunc.java @@ -1,5 +1,5 @@ /* - * Copyright 2023 DiffPlug + * Copyright 2023-2026 DiffPlug * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,8 +19,6 @@ import javax.annotation.Nonnull; -import com.google.googlejavaformat.java.RemoveUnusedImports; - import com.diffplug.spotless.FormatterFunc; public class GoogleJavaFormatRemoveUnusedImporterFormatterFunc implements FormatterFunc { @@ -35,6 +33,6 @@ public GoogleJavaFormatRemoveUnusedImporterFormatterFunc(@Nonnull String version @Override @Nonnull public String apply(@Nonnull String input) throws Exception { - return RemoveUnusedImports.removeUnusedImports(input); + return GoogleJavaFormatUnusedImports.removeUnusedImports(input); } } diff --git a/lib/src/googleJavaFormat/java/com/diffplug/spotless/glue/java/GoogleJavaFormatUnusedImports.java b/lib/src/googleJavaFormat/java/com/diffplug/spotless/glue/java/GoogleJavaFormatUnusedImports.java new file mode 100644 index 0000000000..d2bc23bc42 --- /dev/null +++ b/lib/src/googleJavaFormat/java/com/diffplug/spotless/glue/java/GoogleJavaFormatUnusedImports.java @@ -0,0 +1,49 @@ +/* + * Copyright 2026 DiffPlug + * + * 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 com.diffplug.spotless.glue.java; + +import javax.annotation.Nonnull; + +import com.google.googlejavaformat.java.FormatterException; +import com.google.googlejavaformat.java.RemoveUnusedImports; +import com.sun.source.util.DocTrees; + +final class GoogleJavaFormatUnusedImports { + private GoogleJavaFormatUnusedImports() {} + + /** + * Calls {@link RemoveUnusedImports#removeUnusedImports(String)} with javac's class loader as + * the context class loader. + *
+ * On JDK 23+, javac parses Markdown doc comments ({@code ///}) with a transformer from the
+ * {@code jdk.internal.md} module, which it looks up with {@link java.util.ServiceLoader} on the
+ * context class loader. Gradle and Maven run Spotless with a context class loader that cannot
+ * see that module, so javac silently fell back to treating Markdown doc comments as plain text,
+ * and imports used only by their links were removed
+ * (https://github.com/diffplug/spotless/issues/3161).
+ */
+ @Nonnull
+ static String removeUnusedImports(@Nonnull String input) throws FormatterException {
+ Thread thread = Thread.currentThread();
+ ClassLoader originalClassLoader = thread.getContextClassLoader();
+ thread.setContextClassLoader(DocTrees.class.getClassLoader());
+ try {
+ return RemoveUnusedImports.removeUnusedImports(input);
+ } finally {
+ thread.setContextClassLoader(originalClassLoader);
+ }
+ }
+}
diff --git a/plugin-gradle/CHANGES.md b/plugin-gradle/CHANGES.md
index 5aaef36403..29742423f6 100644
--- a/plugin-gradle/CHANGES.md
+++ b/plugin-gradle/CHANGES.md
@@ -5,6 +5,7 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format (
## [Unreleased]
### Fixed
- GrEclipse no longer leaves a `file%3A...` directory in the working directory. ([#2019](https://github.com/diffplug/spotless/issues/2019))
+- `removeUnusedImports()` and `googleJavaFormat()` no longer remove imports that are only used by links in Markdown doc comments (`///`, JDK 23+). ([#3161](https://github.com/diffplug/spotless/issues/3161))
## [8.11.0] - 2026-11-02
### Added
diff --git a/plugin-maven/CHANGES.md b/plugin-maven/CHANGES.md
index 4081c6671e..19003c5ec5 100644
--- a/plugin-maven/CHANGES.md
+++ b/plugin-maven/CHANGES.md
@@ -5,6 +5,7 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format (
## [Unreleased]
### Fixed
- GrEclipse no longer leaves a `file%3A...` directory in the working directory. ([#2019](https://github.com/diffplug/spotless/issues/2019))
+- `