Skip to content

Replace JAR scanning on dependency change with a JavaSourceSet dirty flag#7460

Draft
steve-aom-elliott wants to merge 1 commit intomainfrom
fix/javasourceset-dirty-bit-for-ambiguity
Draft

Replace JAR scanning on dependency change with a JavaSourceSet dirty flag#7460
steve-aom-elliott wants to merge 1 commit intomainfrom
fix/javasourceset-dirty-bit-for-ambiguity

Conversation

@steve-aom-elliott
Copy link
Copy Markdown
Contributor

@steve-aom-elliott steve-aom-elliott commented Apr 22, 2026

Summary

This PR replaces the downloader with a dirty flag on the JavaSourceSet marker:

  • JavaSourceSetUpdater.{changeDependency,addDependency} now simply flip the flag - no downloads, no classpath reshaping.
  • Ambiguity-sensitive recipes read the flag and take the safe path when it is set:
    • ImportLayoutStyle.isPackageFoldable returns falseOrderImports never folds a group of imports into a star it cannot prove is unambiguous. AddImport passes the same flag through to the conflict detector.
    • ChangePackage.hasAmbiguity returns true → an existing star import is expanded before rewriting the package.
    • ChangeType.maybeAddExplicitImportForAmbiguity adds the explicit import unconditionally when dirty.

The flag is sticky within a recipe run and cleared only by re-parsing. Parser-built markers are never dirty. withDirty(boolean) is Lombok-generated.

Safety semantics

Two simple rules replace the old classpath inspection:

  1. Dirty → expand stars. If a classpath is stale, we cannot prove a star is unambiguous, so we assume it may be and expand it.
  2. Dirty → never fold into a star. Likewise, we cannot prove folding is safe, so we keep imports explicit.

Both decisions are strictly conservative with respect to the old behavior - a dirty marker never produces a result that the old classpath-based check would have forbidden.

Test plan

  • rewrite-java:test - passes
  • rewrite-java-test:test - passes (new coverage: JavaSourceSetTest.withDirtyTogglesFlagWithoutOtherChanges, JavaSourceSetTest.parserBuiltSourceSetIsNotDirty, OrderImportsTest.doNotFoldIntoStarWhenSourceSetIsDirty, ChangePackageTest.changePackageExpandsStarImportWhenSourceSetIsDirty, ChangeTypeTest.changeTypeAddsExplicitImportWhenSourceSetIsDirty)
  • rewrite-maven:test - passes (new coverage: JavaSourceSetUpdaterTest three dirty-flipping cases; ChangeDependencyGroupIdAndArtifactIdTest.updatesJavaSourceSetMarkerOnJavaFiles + composedWithChangePackageUpdatesImports updated to assert isDirty() == true)
  • rewrite-kotlin:test - passes
  • rewrite-gradle:compileTestJava - passes

@github-project-automation github-project-automation Bot moved this to In Progress in OpenRewrite Apr 22, 2026
@steve-aom-elliott steve-aom-elliott moved this from In Progress to Ready to Review in OpenRewrite Apr 22, 2026
…flag

When dependency-mutating recipes (ChangeDependency, AddDependency,
UpgradeDependencyVersion) run, they previously asked JavaSourceSetUpdater
to download the new dependency's JAR and scan it for types so that
downstream ambiguity checks (ChangePackage, ChangeType, OrderImports,
AddImport) could see the post-change classpath. On large multi-module
projects driven by recipes like UpgradeSpringBoot_4_0, that JAR retrieval
dominates recipe wall time with a large fraction of the run's wall time
spent there even after prior fetch optimizations.

Introduce a `dirty` flag on the JavaSourceSet marker instead.
JavaSourceSetUpdater now flips that flag on every change — no downloads,
no classpath reshaping. Ambiguity-sensitive recipes read the flag and
take the safe path when it is set:

- ImportLayoutStyle.isPackageFoldable returns false for a dirty source
  set, so OrderImports never folds imports into a star it cannot prove
  is unambiguous.
- AddImport passes the dirty flag through to the same conflict detector,
  so added imports are not folded either.
- ChangePackage.hasAmbiguity returns true for a dirty source set,
  expanding a star into explicit imports before rewriting the package.
- ChangeType.maybeAddExplicitImportForAmbiguity adds the explicit import
  unconditionally for a dirty source set.

Parser-built markers are never dirty; the flag is sticky within a recipe
run and cleared only by re-parsing. `withDirty(boolean)` is
Lombok-generated.

Tests cover the marker round-trip, the dirty-flipping in the updater,
and the four consumer recipes' safe-path behavior. A new
`markSourceSetDirty()` helper in rewrite-java Assertions simulates the
post-dependency-change state.
@steve-aom-elliott steve-aom-elliott force-pushed the fix/javasourceset-dirty-bit-for-ambiguity branch from a93cc9d to cd00f8c Compare April 27, 2026 13:28
@steve-aom-elliott
Copy link
Copy Markdown
Contributor Author

@sambsnyd Just wanted to check whether we still feel alright about this going in.

@steve-aom-elliott steve-aom-elliott marked this pull request as draft May 1, 2026 15:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Ready to Review

Development

Successfully merging this pull request may close these issues.

2 participants