After upgrading kotlin 2.3.21 -> 2.4.10, compose branch coverage drop down
@Godin is already working on this.
Since Jul 20, 2026.
- Dominant language
- Java
- Stars
- 4.6k
- Forks
- 1.2k
- Avg merge
- 1d 9h
- Merged PRs (30d)
- 23
Description
Steps to reproduce
- JaCoCo version: 0.8.15 (also 0.8.14, older version has very different branch coverage result)
- Operating system: Linux
- Tool integration: Gradle + Kotlin + Kover
- Complete executable reproducer: https://github.com/jacoco/example-jacoco-kotlin-compose/pull/140
- Steps:
- Checkout repo
- Run
./gradlew test jacocoTestReport - Open
build/reports/jacoco/test/html/org.example/index.source.html
Expected behaviour
Give same amount of branch coverage
Actual behaviour
Increases number of branches, but coverage decreases
Extra info
Kotlin upgrade in our product cause significant coverage drop, which is enough to prevent upgrade to kotlin. Sometimes it was +120 branches per composable. I tried to make small as possible repro.
I wasn't sure at first whether this is the right place to report it, but similar problems were fixed here in #1616 and #1911. Let me know if it belongs elsewhere.
I have no idea if it even makes sense, but AI explained it like this:
AI Analysis
The difference is in the lambda memoization check inside the CC(remember) region (between sourceInformationMarkerStart/End). javap -c of ExampleKt.example, the check for the captured state parameter:
Kotlin 2.3.21 — an unconditional changedInstance() call, no extra branches, which roughly corresponds to:
val invalidated = $composer.changedInstance(state) or
($changed and 0b1110000 == 0b0100000) // onDone
Kotlin 2.4.10 — the same capture now consults the caller-provided $changed bits before falling back to changedInstance(), adding three conditionals, which roughly corresponds to:
val invalidated = ($changed and 0b0001110 == 0b0000100 ||
($changed and 0b0001000 != 0 && $composer.changedInstance(state))) or
($changed and 0b1110000 == 0b0100000) // onDone
The result is used as if (invalidated || rememberedValue === Empty) { create and cache the lambda } else { reuse cached }. Some of these branch outcomes depend on bits computed by the caller, so they cannot be reliably exercised by tests. It would be great if KotlinComposeFilter also filtered this pattern.
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.