Fix Scala parser losing type attribution when classpath lacks stdlib#7446
Merged
knutwannheden merged 2 commits intomainfrom Apr 22, 2026
Merged
Conversation
The `:latest.release` constraint on `junit-jupiter-api`/`params`/`engine` was resolving to `6.1.0-M1` (milestone) whose `junit-platform-commons` dropped `CollectionUtils.toUnmodifiableList()`, breaking the `junit-platform-launcher:1.14.0` that Gradle brings in for test discovery. The resulting `NoSuchMethodError` prevented any test from running in this module. Drop the explicit version so the dependencies align with the junit BOM used elsewhere in the build, matching how other modules declare these.
When a caller (e.g. `mod build`) passes a classpath that contains `scala3-compiler` but is missing one of the transitive stdlib jars dotty needs to operate (`scala-library`, `tasty-core`), dotty's `Definitions` cannot resolve foundational types like `scala.Unit`. The resulting corruption surfaces as either a `ClassCastException` (`NoSymbol cannot be cast to ClassSymbol` in `Definitions.UnitClass`) or the related `Bad symbolic reference` TASTy decoding error when the source imports dotty compiler APIs, causing affected files to lose type attribution entirely. `ScalaCompilerBridge` is itself loaded from those stdlib jars, so it can locate them at runtime via `ProtectionDomain.getCodeSource()` and append any that are missing. The self-heal only triggers when the caller's classpath already contains a Scala artifact, which preserves the narrow-classpath contract that `ScalaTemplate` relies on to keep type resolution out of template parsing.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Motivation
mod buildonopenrewrite/rewritefails to type-check 5 Scala files that import dotty compiler APIs, emitting errors such as:Root cause: when a caller's classpath includes
scala3-compilerbut is missing one of the stdlib jars dotty relies on (scala-library,tasty-core), dotty'sDefinitionscan't resolve foundational types likescala.Unit. Depending on where typechecking blows up, this surfaces as theBad symbolic referenceTASTy error above, or as aClassCastException: NoSymbol cannot be cast to ClassSymbolinDefinitions.UnitClass. Either way, the affected files lose type attribution entirely.Because
ScalaCompilerBridgeitself is loaded from those same jars, it can locate them at runtime and self-heal — analogous to howJavaParserdoesn't require callers to passrt.jar.Summary
ScalaCompilerBridgenow computes the dotty classpath through a newbuildClasspathhelper that appends any missing stdlib jars (scala-library,tasty-core,scala3-compiler), located viaProtectionDomain.getCodeSource()on probe classes (scala.Option,dotty.tools.tasty.TastyFormat,dotty.tools.dotc.core.Contexts).scala*/tasty*/dotty*artifact. This preserves the narrow-classpath contract thatScalaTemplatedepends on to keep type resolution out of template parsing.DottyImportTypeAttributionTestfeeds the bridge a classpath containing onlyscala3-compiler(mimicking themod buildscenario) and verifies that both an ordinary Scala file and one importingdotty.tools.dotc.core.Contexts.*come back with a populated typed tree.:latest.releasefrom threejunit-jupitertest dependencies inrewrite-scala/build.gradle.kts.:latest.releasewas resolving to6.1.0-M1(milestone), whosejunit-platform-commonsremovedCollectionUtils.toUnmodifiableList()— breaking thejunit-platform-launcher:1.14.0that Gradle's test runner uses. No tests in this module could run. Inheriting from the junit BOM (as the other modules do) restores consistency.Test plan
./gradlew :rewrite-scala:test— full module green, including the newDottyImportTypeAttributionTestand the pre-existing template/matching suites.mod buildon a freshopenrewrite/rewritecheckout with the CLI's sibling-Java fix (moderneinc/moderne-cli#3704) to confirm theBad symbolic referenceerrors no longer appear on the 5 files listed in the motivation.