Skip to content

Fix JavaSourceSet phantom diffs and use cached updates in all wrapping visitors#7376

Merged
steve-aom-elliott merged 4 commits intomainfrom
fix/changedependency-phantom-diffs
Apr 14, 2026
Merged

Fix JavaSourceSet phantom diffs and use cached updates in all wrapping visitors#7376
steve-aom-elliott merged 4 commits intomainfrom
fix/changedependency-phantom-diffs

Conversation

@steve-aom-elliott
Copy link
Copy Markdown
Contributor

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

Summary

  • Fix changeDependency() idempotency in JavaSourceSetUpdater — skip updates when gavToTypes is non-empty but doesn't contain the old dependency key (the dep was never part of the tracked classpath)
  • Use cached updateOnSourceFile overload in all six JavaSourceSet wrapping visitors for consistent cross-file deduplication within a source set
  • Defer JavaSourceSetUpdater construction and object allocation (newGav/newDep) into the cached lambda so they only execute on cache misses

Context

PR #7202 added JavaSourceSet marker updating to dependency-modifying recipes. Two issues emerged:

  1. Phantom diffs from non-idempotent changeDependency(): When multiple recipes in a composite like UpgradeSpringBoot_3_0 run, some JAR downloads succeed in cycle 1 while others fail. In cycle 2, retries succeed and add new types — creating marker-only diffs that trigger extra-cycle assertions.

  2. Inconsistent caching: ChangeDependencyGroupIdAndArtifactId (Maven) and ChangeDependency (Gradle) used the cached updateOnSourceFile(sf, cache, transform) overload, but UpgradeDependencyVersion (Maven/Gradle) and AddDependency (Maven/Gradle) did not — causing redundant JAR downloads per file and missing cross-file identity preservation.

Changes

  • JavaSourceSetUpdater.changeDependency(): Return early when gavToTypes is non-empty but doesn't contain the old GAV key. Preserve the empty-gavToTypes path for initial population.
  • UpgradeDependencyVersion (Maven): Replace manual marker extraction and identity check with JavaSourceSet.updateOnSourceFile(sf, cache, transform)
  • UpgradeDependencyVersion (Gradle): Same refactor, moving the multi-dep loop inside the transform lambda
  • AddDependency (Maven/Gradle): Switch from non-cached to cached updateOnSourceFile overload
  • All four recipes: Move JavaSourceSetUpdater initialization and object construction (newGav, newDep, remote repo lookup) inside the cached lambda to avoid unnecessary work on cache hits

Test plan

  • ChangeDependencyGroupIdAndArtifactIdTest.updatesJavaSourceSetMarkerOnJavaFiles passes
  • ChangeDependencyGroupIdAndArtifactIdTest.composedWithChangePackageUpdatesImports passes
  • Full rewrite-maven test suite passes (UpgradeDependencyVersionTest, AddDependencyTest, ChangeDependencyGroupIdAndArtifactIdTest)
  • Full rewrite-gradle test suite passes (UpgradeDependencyVersionTest, AddDependencyTest, ChangeDependencyTest)
  • rewrite-spring Boot3UpgradeTest.xmlBindMissing passes (was failing with "Expected recipe to complete in 1 cycle, but took 2 cycles")

When multiple dependency recipes update JavaSourceSet markers, JAR
downloads may succeed for some recipes in cycle 1 but fail for others.
In cycle 2, the previously-failed downloads retry and succeed, creating
marker-only diffs that trigger the 2-cycle assertion in downstream tests
like Boot3UpgradeTest.xmlBindMissing in rewrite-spring.

The fix: in changeDependency(), when gavToTypes is non-empty but doesn't
contain the old dependency key, skip the update. This dependency was never
part of the tracked classpath, so there's nothing to change. The existing
empty-gavToTypes path is preserved for initial population.
Four wrapping visitors (Maven/Gradle UpgradeDependencyVersion and
Maven/Gradle AddDependency) were not using the cached overload of
JavaSourceSet.updateOnSourceFile(), causing redundant JAR downloads
per file and missing cross-file identity preservation within a source
set. Align them with the pattern already used by
ChangeDependencyGroupIdAndArtifactId and Gradle ChangeDependency.
@steve-aom-elliott steve-aom-elliott changed the title Prevent JavaSourceSet phantom diffs across recipe cycles Fix JavaSourceSet phantom diffs and use cached updates in all wrapping visitors Apr 14, 2026
Avoid unnecessary object allocation and findRemoteRepository calls
for files that hit the updateOnSourceFile cache.
Defer construction of JavaSourceSetUpdater (which creates temp
directories and HTTP downloaders) until the cache misses, avoiding
unnecessary work when the cached JavaSourceSet is reused.
@steve-aom-elliott steve-aom-elliott merged commit e6daa51 into main Apr 14, 2026
1 check passed
@steve-aom-elliott steve-aom-elliott deleted the fix/changedependency-phantom-diffs branch April 14, 2026 21:57
@github-project-automation github-project-automation Bot moved this from In Progress to Done in OpenRewrite Apr 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Archived in project

Development

Successfully merging this pull request may close these issues.

1 participant