diff --git a/CHANGES.md b/CHANGES.md
index 6ccbfade93..4f2300afa5 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -10,6 +10,10 @@ This document is intended for Spotless developers.
We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format (starting after version `1.27.0`).
## [Unreleased]
+### Added
+- Add `javaparserVersion` option to the Cleanthat step, allowing callers to override the JavaParser version pulled in transitively by Cleanthat. ([#2903](https://github.com/diffplug/spotless/pull/2903))
+### Changes
+- Bump default `cleanthat` version `2.24` -> `2.25`. ([#2903](https://github.com/diffplug/spotless/pull/2903))
## [4.5.0] - 2026-03-18
### Added
diff --git a/lib/src/cleanthat/java/com/diffplug/spotless/glue/java/JavaCleanthatRefactorerFunc.java b/lib/src/cleanthat/java/com/diffplug/spotless/glue/java/JavaCleanthatRefactorerFunc.java
index b75c93f067..ef2ed1bbcf 100644
--- a/lib/src/cleanthat/java/com/diffplug/spotless/glue/java/JavaCleanthatRefactorerFunc.java
+++ b/lib/src/cleanthat/java/com/diffplug/spotless/glue/java/JavaCleanthatRefactorerFunc.java
@@ -87,7 +87,7 @@ private String doApply(String input, File file) throws IOException {
JavaRefactorer refactorer = new JavaRefactorer(engineProperties, refactorerProperties);
- LOGGER.debug("Processing sourceJdk={} included={} excluded={}", jdkVersion, included, excluded, includeDraft);
+ LOGGER.debug("Processing sourceJdk={} included={} excluded={} includeDraft={}", jdkVersion, included, excluded, includeDraft);
LOGGER.debug("Available mutators: {}", JavaRefactorer.getAllIncluded());
PathAndContent pathAndContent = new PathAndContent(file.toPath(), input);
diff --git a/lib/src/main/java/com/diffplug/spotless/java/CleanthatJavaStep.java b/lib/src/main/java/com/diffplug/spotless/java/CleanthatJavaStep.java
index 439d10a0af..5f181d029c 100644
--- a/lib/src/main/java/com/diffplug/spotless/java/CleanthatJavaStep.java
+++ b/lib/src/main/java/com/diffplug/spotless/java/CleanthatJavaStep.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2023-2025 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.
@@ -21,6 +21,7 @@
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.nio.file.Path;
+import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
@@ -31,6 +32,8 @@
import com.diffplug.spotless.Jvm;
import com.diffplug.spotless.Provisioner;
+import edu.umd.cs.findbugs.annotations.Nullable;
+
/**
* Enables CleanThat as a SpotLess step.
*
@@ -42,13 +45,15 @@ public final class CleanthatJavaStep implements Serializable {
private static final long serialVersionUID = 1L;
private static final String NAME = "cleanthat";
private static final String MAVEN_COORDINATE = "io.github.solven-eu.cleanthat:java";
+ private static final String JAVAPARSER_MAVEN_COORDINATE = "com.github.javaparser:javaparser-symbol-solver-core";
/**
* CleanThat changelog is available at here.
*/
- private static final Jvm.Support JVM_SUPPORT = Jvm. support(NAME).add(17, "2.24");
+ private static final Jvm.Support JVM_SUPPORT = Jvm. support(NAME).add(17, "2.25");
private final JarState.Promised jarState;
private final String version;
+ private final String javaparserVersion;
private final String sourceJdkVersion;
private final List included;
private final List excluded;
@@ -56,12 +61,14 @@ public final class CleanthatJavaStep implements Serializable {
private CleanthatJavaStep(JarState.Promised jarState,
String version,
+ String javaparserVersion,
String sourceJdkVersion,
List included,
List excluded,
boolean includeDraft) {
this.jarState = jarState;
this.version = version;
+ this.javaparserVersion = javaparserVersion;
this.sourceJdkVersion = sourceJdkVersion;
this.included = included;
@@ -76,7 +83,14 @@ public static FormatterStep create(Provisioner provisioner) {
/** Creates a step that applies default CleanThat mutators. */
public static FormatterStep create(String version, Provisioner provisioner) {
- return createWithStepName(NAME, MAVEN_COORDINATE, version, defaultSourceJdk(), defaultMutators(), defaultExcludedMutators(), defaultIncludeDraft(), provisioner);
+ return createWithStepName(NAME, MAVEN_COORDINATE, version, defaultJavaparserVersion(), defaultSourceJdk(), defaultMutators(), defaultExcludedMutators(), defaultIncludeDraft(), provisioner);
+ }
+
+ /**
+ * Default JavaParser version: {@code null}, meaning whichever transitive version is brought in by Cleanthat.
+ */
+ @Nullable public static String defaultJavaparserVersion() {
+ return null;
}
public static String defaultSourceJdk() {
@@ -106,6 +120,7 @@ public static boolean defaultIncludeDraft() {
static FormatterStep createWithStepName(String stepName,
String groupArtifact,
String version,
+ String javaparserVersion,
String sourceJdkVersion,
List included,
List excluded,
@@ -117,8 +132,14 @@ static FormatterStep createWithStepName(String stepName,
}
Objects.requireNonNull(version, "version");
Objects.requireNonNull(provisioner, "provisioner");
+ List coordinates = new ArrayList<>();
+ coordinates.add(groupArtifact + ":" + version);
+ if (javaparserVersion != null) {
+ // Added alongside Cleanthat so dependency resolution can upgrade the transitive JavaParser.
+ coordinates.add(JAVAPARSER_MAVEN_COORDINATE + ":" + javaparserVersion);
+ }
return FormatterStep.create(stepName,
- new CleanthatJavaStep(JarState.promise(() -> JarState.from(groupArtifact + ":" + version, provisioner)), version, sourceJdkVersion, included, excluded, includeDraft),
+ new CleanthatJavaStep(JarState.promise(() -> JarState.from(coordinates, provisioner)), version, javaparserVersion, sourceJdkVersion, included, excluded, includeDraft),
CleanthatJavaStep::equalityState,
State::createFormat);
}
@@ -131,7 +152,19 @@ public static FormatterStep create(String groupArtifact,
List excluded,
boolean includeDraft,
Provisioner provisioner) {
- return createWithStepName(NAME, groupArtifact, version, sourceJdkVersion, included, excluded, includeDraft, provisioner);
+ return createWithStepName(NAME, groupArtifact, version, defaultJavaparserVersion(), sourceJdkVersion, included, excluded, includeDraft, provisioner);
+ }
+
+ /** Creates a step that applies selected CleanThat mutators, with a custom JavaParser version. */
+ public static FormatterStep create(String groupArtifact,
+ String version,
+ String javaparserVersion,
+ String sourceJdkVersion,
+ List included,
+ List excluded,
+ boolean includeDraft,
+ Provisioner provisioner) {
+ return createWithStepName(NAME, groupArtifact, version, javaparserVersion, sourceJdkVersion, included, excluded, includeDraft, provisioner);
}
/** Get default formatter version */
@@ -144,7 +177,7 @@ public static String defaultGroupArtifact() {
}
private State equalityState() {
- return new State(jarState.get(), version, sourceJdkVersion, included, excluded, includeDraft);
+ return new State(jarState.get(), version, javaparserVersion, sourceJdkVersion, included, excluded, includeDraft);
}
private static final class State implements Serializable {
@@ -153,6 +186,7 @@ private static final class State implements Serializable {
private final JarState jarState;
private final String version;
+ private final String javaparserVersion;
private final String sourceJdkVersion;
private final List included;
private final List excluded;
@@ -160,6 +194,7 @@ private static final class State implements Serializable {
State(JarState jarState,
String version,
+ String javaparserVersion,
String sourceJdkVersion,
List included,
List excluded,
@@ -168,6 +203,7 @@ private static final class State implements Serializable {
ModuleHelper.doOpenInternalPackagesIfRequired();
this.jarState = jarState;
this.version = version;
+ this.javaparserVersion = javaparserVersion;
this.sourceJdkVersion = sourceJdkVersion;
this.included = included;
this.excluded = excluded;
diff --git a/lib/src/main/java/com/diffplug/spotless/java/RemoveUnusedImportsStep.java b/lib/src/main/java/com/diffplug/spotless/java/RemoveUnusedImportsStep.java
index 590432aedc..37f1c99c07 100644
--- a/lib/src/main/java/com/diffplug/spotless/java/RemoveUnusedImportsStep.java
+++ b/lib/src/main/java/com/diffplug/spotless/java/RemoveUnusedImportsStep.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016-2025 DiffPlug
+ * Copyright 2016-2026 DiffPlug
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -53,7 +53,7 @@ public static FormatterStep create(String unusedImportRemover, Provisioner provi
case GJF:
return GoogleJavaFormatStep.createRemoveUnusedImportsOnly(provisioner);
case CLEANTHAT:
- return CleanthatJavaStep.createWithStepName(NAME, CleanthatJavaStep.defaultGroupArtifact(), CleanthatJavaStep.defaultVersion(), "99.9", List.of(CLEANTHAT_MUTATOR), List.of(), false, provisioner);
+ return CleanthatJavaStep.createWithStepName(NAME, CleanthatJavaStep.defaultGroupArtifact(), CleanthatJavaStep.defaultVersion(), CleanthatJavaStep.defaultJavaparserVersion(), "99.9", List.of(CLEANTHAT_MUTATOR), List.of(), false, provisioner);
default:
throw new IllegalArgumentException("Invalid unusedImportRemover: " + unusedImportRemover);
}
diff --git a/plugin-gradle/CHANGES.md b/plugin-gradle/CHANGES.md
index 692ed74491..25215dae53 100644
--- a/plugin-gradle/CHANGES.md
+++ b/plugin-gradle/CHANGES.md
@@ -5,8 +5,11 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format (
## [Unreleased]
### Added
- Add `withIndentStyle` and `withIndentSize` configuration to `tableTestFormatter` for setting the fallback indent when no `.editorconfig` is found. ([#2893](https://github.com/diffplug/spotless/pull/2893))
+- Add `javaparserVersion(...)` to `cleanthat`, allowing users to override the JavaParser version pulled in transitively by Cleanthat. ([#2903](https://github.com/diffplug/spotless/pull/2903))
### Fixed
- Fix `tableTestFormatter` editorconfig cache not honoring `.editorconfig` changes across Gradle daemon runs due to a shared static `EditorConfigProvider`. ([#2893](https://github.com/diffplug/spotless/pull/2893))
+### Changes
+- Bump default `cleanthat` version `2.24` -> `2.25`. ([#2903](https://github.com/diffplug/spotless/pull/2903))
## [8.4.0] - 2026-03-18
### Added
diff --git a/plugin-gradle/README.md b/plugin-gradle/README.md
index db4162fb89..3efa2f2438 100644
--- a/plugin-gradle/README.md
+++ b/plugin-gradle/README.md
@@ -422,7 +422,8 @@ spotless {
// optional: you can specify a specific version and/or config file
cleanthat()
.groupArtifact('io.github.solven-eu.cleanthat:java') // Optional. Default is 'io.github.solven-eu.cleanthat:java'
- .version('2.8') // You may force a custom version of Cleanthat
+ .version('2.25') // You may force a custom version of Cleanthat
+ .javaparserVersion('3.26.4') // Advanced: override the JavaParser version transitively pulled by Cleanthat
.sourceCompatibility('1.7') // default is '1.7'
.addMutator('SafeAndConsensual') // Default includes the SafeAndConsensual composite mutator
.addMutator('your.custom.MagicMutator') // List of mutators: https://github.com/solven-eu/cleanthat/blob/master/MUTATORS.generated.MD
diff --git a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/JavaExtension.java b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/JavaExtension.java
index 90684b323d..a6a6ce1e5d 100644
--- a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/JavaExtension.java
+++ b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/JavaExtension.java
@@ -433,6 +433,8 @@ public class CleanthatJavaConfig {
private String version = CleanthatJavaStep.defaultVersion();
+ private String javaparserVersion = CleanthatJavaStep.defaultJavaparserVersion();
+
private String sourceJdk = CleanthatJavaStep.defaultSourceJdk();
private List mutators = new ArrayList<>(CleanthatJavaStep.defaultMutators());
@@ -459,6 +461,18 @@ public CleanthatJavaConfig version(String version) {
return this;
}
+ /**
+ * Advanced: override the version of JavaParser pulled in transitively by Cleanthat. The coordinate
+ * {@code com.github.javaparser:javaparser-symbol-solver-core:} is appended to the resolved classpath;
+ * standard dependency resolution rules then apply (newest wins).
+ */
+ public CleanthatJavaConfig javaparserVersion(String javaparserVersion) {
+ Objects.requireNonNull(javaparserVersion);
+ this.javaparserVersion = javaparserVersion;
+ replaceStep(createStep());
+ return this;
+ }
+
public CleanthatJavaConfig sourceCompatibility(String jdkVersion) {
Objects.requireNonNull(jdkVersion);
this.sourceJdk = jdkVersion;
@@ -504,6 +518,7 @@ private FormatterStep createStep() {
return CleanthatJavaStep.create(
groupArtifact,
version,
+ javaparserVersion,
sourceJdk, mutators, excludedMutators, includeDraft, provisioner());
}
}
diff --git a/plugin-maven/CHANGES.md b/plugin-maven/CHANGES.md
index f2af1a3388..8844f77e63 100644
--- a/plugin-maven/CHANGES.md
+++ b/plugin-maven/CHANGES.md
@@ -3,6 +3,10 @@
We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format (starting after version `1.27.0`).
## [Unreleased]
+### Added
+- Add `` option to ``, allowing users to override the JavaParser version pulled in transitively by Cleanthat. ([#2903](https://github.com/diffplug/spotless/pull/2903))
+### Changes
+- Bump default `cleanthat` version `2.24` -> `2.25`. ([#2903](https://github.com/diffplug/spotless/pull/2903))
## [3.4.0] - 2026-03-18
### Added
diff --git a/plugin-maven/README.md b/plugin-maven/README.md
index 4a000e9cab..d47e571fcc 100644
--- a/plugin-maven/README.md
+++ b/plugin-maven/README.md
@@ -386,7 +386,8 @@ These mechanisms already exist for the Gradle plugin.
```xml
- 2.8
+ 2.25
+ 3.26.4
${maven.compiler.source}
SafeAndConsensual
diff --git a/plugin-maven/src/main/java/com/diffplug/spotless/maven/java/CleanthatJava.java b/plugin-maven/src/main/java/com/diffplug/spotless/maven/java/CleanthatJava.java
index 4133409919..9e5661fe65 100644
--- a/plugin-maven/src/main/java/com/diffplug/spotless/maven/java/CleanthatJava.java
+++ b/plugin-maven/src/main/java/com/diffplug/spotless/maven/java/CleanthatJava.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.
@@ -31,6 +31,14 @@ public class CleanthatJava implements FormatterStepFactory {
@Parameter
private String version;
+ /**
+ * Optional: override the version of JavaParser pulled in as a transitive dependency of Cleanthat. Advanced use-case:
+ * declaring a version here appends {@code com.github.javaparser:javaparser-symbol-solver-core:}
+ * to the resolved classpath, so standard Maven resolution rules (newest wins) apply. At your own risk.
+ */
+ @Parameter
+ private String javaparserVersion;
+
// https://maven.apache.org/plugins/maven-compiler-plugin/compile-mojo.html#source
@Parameter(property = "maven.compiler.source")
private String sourceJdk = CleanthatJavaStep.defaultSourceJdk();
@@ -49,6 +57,6 @@ public FormatterStep newFormatterStep(FormatterStepConfig config) {
String groupArtifact = this.groupArtifact != null ? this.groupArtifact : CleanthatJavaStep.defaultGroupArtifact();
String version = this.version != null ? this.version : CleanthatJavaStep.defaultVersion();
- return CleanthatJavaStep.create(groupArtifact, version, sourceJdk, mutators, excludedMutators, includeDraft, config.getProvisioner());
+ return CleanthatJavaStep.create(groupArtifact, version, javaparserVersion, sourceJdk, mutators, excludedMutators, includeDraft, config.getProvisioner());
}
}