Skip to content

fix(mapping): Synthesize filenames for Kotlin classes buried in synthetic wrappers#94

Merged
romtsn merged 3 commits intomasterfrom
rz/fix/proguard-synthesize-compose-singletons-filename
Apr 23, 2026
Merged

fix(mapping): Synthesize filenames for Kotlin classes buried in synthetic wrappers#94
romtsn merged 3 commits intomasterfrom
rz/fix/proguard-synthesize-compose-singletons-filename

Conversation

@romtsn
Copy link
Copy Markdown
Member

@romtsn romtsn commented Apr 22, 2026

Fix synthesized source filenames for Compose-compiler-generated ComposableSingletons$FileKt classes when they are inlined through R8 synthetic lambdas.

When a Compose compilation emits a ComposableSingletons$MainActivityKt class, the mapping often does not include a top-level declaration for it — only its $$ExternalSyntheticLambda* siblings appear, each carrying sourceFile: R8$$SyntheticClass. In that case the outer Kotlin class has no sourceFile metadata of its own, and frame resolution falls back to synthesize_source_file, which previously did split('$').next() and collapsed ComposableSingletons$MainActivityKt to ComposableSingletons. The resulting frame's filename became ComposableSingletons.java, dropping the real MainActivity.kt.

The fix scans every $-separated segment of the last dotted component for a Kotlin file-class marker (ends in Kt). The inner MainActivityKt segment now wins and produces MainActivity.kt. Bare top-level Kotlin classes (FooKt) and non-Kotlin inner classes (Foo$BarFoo.java) are unchanged — they still resolve on the first iteration exactly as before.

Includes a minimal reproducer in tests/r8-source-file-edge-cases.rs mirroring the scenario: a ComposableSingletons$MyScreenKt lambda inlined through an $$ExternalSyntheticLambda whose sourceFile is R8$$SyntheticClass.

When a Compose-compiler-generated class like `ComposableSingletons$MainActivityKt`
is inlined through an R8 synthetic lambda, the outer Kotlin class has no
top-level declaration in the mapping (only its `$$ExternalSyntheticLambda*`
children). The synthesizer previously took `split('$').next()`, collapsing
`ComposableSingletons$MainActivityKt` to `ComposableSingletons` and producing
`ComposableSingletons.java`.

Scan all `$`-separated segments for a Kotlin file-class marker (ends in `Kt`)
so the inner `MainActivityKt` segment wins and the file resolves to
`MainActivity.kt`. Bare top-level `FooKt` and non-Kotlin inner classes are
unaffected.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@romtsn romtsn requested a review from a team as a code owner April 22, 2026 19:07
…ader

Merge the restored "Kt suffix takes precedence" note back into the scan-loop
comment, and rename the test block header from `ComposableSingletonsInlinedLambdaStackTrace`
to a plain description — the sibling blocks in this file name upstream R8 retrace
fixtures, which this case is not.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@romtsn romtsn force-pushed the rz/fix/proguard-synthesize-compose-singletons-filename branch from c3d15a4 to 4dcda1d Compare April 22, 2026 19:15
@romtsn romtsn changed the title fix(mapping): Synthesize filenames for ComposableSingletons wrappers fix(mapping): Synthesize filenames for synthetic wrappers over Kotlin files Apr 22, 2026
@romtsn romtsn changed the title fix(mapping): Synthesize filenames for synthetic wrappers over Kotlin files fix(mapping): Synthesize filenames for Kotlin classes buried in synthetic wrappers Apr 22, 2026
Comment thread src/utils.rs Outdated
Comment thread tests/r8-source-file-edge-cases.rs Outdated
Replace the prose `For example:` list in the doc comment with actual unit
tests covering the full matrix — top-level Kt, Kt-over-reference-file,
reference-file extension, non-Kotlin inner class, ComposableSingletons
wrapper, degenerate bare `Kt` segment, default `.java` fallback.

Also convert the block `// ====` header on COMPOSABLE_SINGLETONS_INLINED_LAMBDA_MAPPING
into a `///` doc comment on the constant, per review feedback.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@romtsn romtsn requested a review from loewenheim April 23, 2026 09:33
@romtsn romtsn enabled auto-merge (squash) April 23, 2026 09:33
@romtsn romtsn merged commit f32d990 into master Apr 23, 2026
10 checks passed
@romtsn romtsn deleted the rz/fix/proguard-synthesize-compose-singletons-filename branch April 23, 2026 10:35
romtsn added a commit to getsentry/symbolicator that referenced this pull request Apr 23, 2026
Bumps `proguard` from 5.10.2 to 5.10.3 to pick up the
`ComposableSingletons$FileKt` source-file synthesis fix from
[getsentry/rust-proguard#94](getsentry/rust-proguard#94).

When a Compose-generated `ComposableSingletons$MainActivityKt` class is
inlined through an R8 synthetic lambda and no top-level class
declaration exists for it in the mapping (common in Android builds), the
previous `synthesize_source_file` truncated at the first `$` and
produced `ComposableSingletons.java`. 5.10.3 scans every `$`-segment for
a `Kt` marker and now resolves to `MainActivity.kt`.

No symbolicator-side changes are needed — the fix is entirely inside the
leaf utility. Upstream carries unit tests (7 cases in `src/utils.rs`)
and an integration test
(`tests/r8-source-file-edge-cases.rs::test_composable_singletons_inlined_lambda_stacktrace`).
Skipping an end-to-end integration test here since there's no new data
flow or wiring to exercise on this side; happy to add one if reviewers
prefer.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants