Skip to content

Drop non-existent classpath entries before parsing - #468

Merged
timtebeek merged 1 commit into
mainfrom
tim/filter-nonexistent-classpath-entries
Aug 21, 2026
Merged

Drop non-existent classpath entries before parsing#468
timtebeek merged 1 commit into
mainfrom
tim/filter-nonexistent-classpath-entries

Conversation

@timtebeek

Copy link
Copy Markdown
Member

RewriteRunTest.kotlinSourceGradle9 has been failing on main since the 2026-08-20 nightly, with rewriteRun reporting:

There were problems parsing src/main/kotlin/com/foo/A.kt
java.lang.IllegalStateException: diagnostic collector is not initialized
  org.jetbrains.kotlin.cli.common.CLIConfigurationKeysKt._get_diagnosticsCollector_$lambda$0(CLIConfigurationKeys.kt:110)
  org.jetbrains.kotlin.cli.CliDiagnosticReportingKt.report(CliDiagnosticReporting.kt:36)
  org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment.findExistingRoot(KotlinCoreEnvironment.kt:395)
  org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment.contentRootToVirtualFile(KotlinCoreEnvironment.kt:379)
  ...

Nothing changed in this repo; rewrite-kotlin snapshots moved to kotlin-compiler-embeddable 2.4.10. When KotlinCoreEnvironment hits a classpath root that doesn't exist it tries to report a warning, and on 2.4.10 that report throws because no diagnostics collector is configured — taking the whole file's parse down with it.

A source set's runtimeClasspath includes its own output directories, and those don't exist when the corresponding compile task is NO-SOURCE (build/classes/java/main and build/resources/main in the test project). Filtering non-existent entries out of the classpath we hand to the parsers avoids the crash; they carried no type information anyway.

Applied at both sites that build a classpath for KotlinParser.

:plugin:test passes locally (58 tests, 0 failures).

The Kotlin 2.4 compiler embedded in rewrite-kotlin throws
"diagnostic collector is not initialized" when KotlinCoreEnvironment
encounters a classpath root that does not exist, so every Kotlin source
in a project with an empty source set output directory failed to parse.
@github-project-automation github-project-automation Bot moved this to In Progress in OpenRewrite Aug 21, 2026
@timtebeek
timtebeek marked this pull request as draft August 21, 2026 10:10
@timtebeek
timtebeek marked this pull request as ready for review August 21, 2026 10:17
@timtebeek

Copy link
Copy Markdown
Member Author

Raised in review: doesn't the test create the file that's now being filtered out? (RewriteRunTest.kt#L1027-L1084)

It doesn't. Two separate things are handed to KotlinParser, and the filter only touches one of them:

  • Source pathskotlinPaths, passed to kp.parse(kotlinPaths, baseDir, ctx). src/main/kotlin/com/foo/A.kt is here. The filter is not on this stream.
  • Classpath rootsdependencyPaths, built from sourceSet.getRuntimeClasspath() + getCompileClasspath(). The filter is only here.

To confirm rather than argue it, I instrumented the predicate to log every entry it keeps and drops, and ran kotlinSourceGradle9:

DROPPED  <PROJECT>/build/classes/java/main
DROPPED  <PROJECT>/build/classes/java/test
DROPPED  <PROJECT>/build/classes/kotlin/test
DROPPED  <PROJECT>/build/resources/main
DROPPED  <PROJECT>/build/resources/test
KEPT     <PROJECT>/build/classes/kotlin/main
KEPT     .../org.jetbrains.kotlin/kotlin-stdlib/2.2.0/kotlin-stdlib-2.2.0.jar
KEPT     .../org.jetbrains/annotations/13.0/annotations-13.0.jar

A.kt's compiled output, build/classes/kotlin/main, is on the classpath and is kept — it exists because compileKotlin ran. What gets dropped is the five output directories that were never created because their compile tasks were NO-SOURCE: the test project has no Java sources, no resources, and no test source set. Those are the roots KotlinCoreEnvironment.findExistingRoot chokes on.

Files::exists is evaluated when the stream is consumed inside rewriteRun, i.e. after the compile tasks have run, so anything that exists by then survives.

One thing worth calling out explicitly: the dropped test entries show this loop runs over every source set, so the filter applies more broadly than the single failing scenario. I think that's still sound — a path that doesn't exist on disk contributes no type information to any parser — but it is a wider blast radius than "fix the one broken test", so it's worth a second opinion.

@timtebeek
timtebeek merged commit d90321b into main Aug 21, 2026
2 checks passed
@timtebeek
timtebeek deleted the tim/filter-nonexistent-classpath-entries branch August 21, 2026 10:22
@github-project-automation github-project-automation Bot moved this from In Progress to Done in OpenRewrite Aug 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

1 participant