Repository navigation
fix(kotlin): a private property, and the members of a private class, stay in their file - #3010
Merged
Merged
Conversation
…stay in their file A Kotlin property was indexed without its visibility modifier, so the Kotlin file-local rule (#1745), which declines a private fun in another file, read every property as public: a call like token(...) could link to another file's @volatile private var token, and JavaScript, Go or Python callers to a Kotlin test fixture's private val. A public member of a private class or object was reachable from other files too, though nothing outside its file can name it. Both extractors (wasm and the native kernel) now read a property's modifiers the way they read a function's, and a candidate inside a private Kotlin class or object is declined from another file. That second rule is Kotlin's alone: a Scala private[pkg] class, or one with a private constructor, is its package's, and a C# private nested class serves every file of its partial class. Applied to every language where private is file-local, as the PR had it, it removed 742 edges on http4s (right ones among them, like stream.receiveHeaders() on a private[h2] class H2Stream), 91 on scalachess and 10 on serilog (9 of them right typed edges). Validation: - cross-file-visibility.test.ts: 2 cases, red on main, green here; the kernel parity fixture gains a private val and an internal var. - Kernel parity (kotlin), 0 diffs: okhttp 588/619 files, mihon 891/940, nowinandroid 340/350, SystemUI's Kotlin 188/198 byte-identical, the same files deferred to wasm as with main's kernel; torture 1/1. - A/B against main 01e0ac3 (with the Kotlin receiver typing of #1933): okhttp -274, mihon -177, SystemUI -94, every one a call from another file onto a private Kotlin property or a member of a private class or object (okhttp's private inner AndroidQuery.enqueue and RealWebSocketTest's private TestStreams, mihon's private IntentFilter filter that collections' filter { } reached, SystemUI's private MyExecutor.execute); nothing added. nowinandroid, jextract, click and gin identical; against main 4eb8c2a, http4s, scalachess and serilog were identical too. - Cost (mihon, median of 3 interleaved indexes, on main 4eb8c2a): CPU 25.7 s on main vs 25.5 s, wall 8.2 s on both. Original PR #1761 by @danusha2345. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Oct 29, 2026
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.
Summary
In Kotlin, a
private valorprivate varproperty, and the members of aprivateclass or object, no longer take calls from other files. Nothing outside their file can name them, but cross-file calls of the same name landed on them: collections'filter { … }on a class's privateIntentFilter filter, OkHttpenqueue(…)calls on a private inner class'senqueue, JavaScript, Go or Python callers on a Kotlin test fixture's privateval.Root cause
A Kotlin property was indexed without its visibility modifier, so the Kotlin file-local rule (#1745), which declines a
private funfrom another file, read every property as public. A member of a private class or object carries noprivateof its own, so the rule let it through as well.From the contributor, and what we changed
From @danusha2345 (#1761), unchanged: both extractors (the wasm path and the native kernel) read a property's
modifiersas they already did a function's; the parity fixture gains aprivate valand aninternal var;isVisibleAcrossFilesdeclines a candidate inside a private type in its file (by line range, memoised per node); the twocross-file-visibility.test.tscases.Our changes:
privateis file-local. A Scalaprivate[pkg] class, or one with a private constructor, belongs to its package, and a C# private nested class serves every file of its partial class: on main of that day, http4s lost 742 edges with the rule (right ones among them, likestream.receiveHeaders()on theprivate[h2] class H2Stream), scalachess 91, serilog 10 (9 of them right typed edges). A Kotlinprivate constructorleaves its class public, which the rule never looked at.01e0ac3a, which has fix(kotlin): type receivers from declarations; parse fun interface #1933's Kotlin receiver typing (fix(kotlin): a call through a property or a receiver chain resolves on its declared type #2972); the patch applies unchanged.Validation
Tests.
cross-file-visibility.test.ts: the 2 Kotlin cases are red on main and green here;kernel-kotlin-parity.test.tsgreen with the rebuilt kernel.Kernel parity (
scripts/kernel-parity.mjs --lang kotlin), 0 files with diffs: okhttp 588/619 files byte-identical, mihon 891/940, nowinandroid 340/350, SystemUI's Kotlin files 188/198, the torture fixture 1/1; the rest are deferred to wasm on parse errors, the same files as with main's kernel.A/B against main
01e0ac3a, fresh indexes with each arm's kernel, edges compared by site and target identity:Every removed edge, checked one by one against the new graph, is a call from another file onto a private Kotlin property (okhttp 54, mihon 154, SystemUI 5) or onto a member of a private Kotlin class or object (220, 23, 89). The largest groups: okhttp's private inner
AndroidDns.AndroidQuery.enqueue(71) andRealWebSocketTest's privateTestStreams.source(59); mihon's privateval filter = IntentFilter()that collections'filter { … }reached (65) andMangaRestorer's privatenow(47); SystemUI's privatePrivacyItemController.MyExecutor.execute(43) andControlsBindingControllerImpl.CallbackRunnable.run(13).Before #1933, against main
4eb8c2a5, the same change removed okhttp 307, mihon 237 and nowinandroid 7 (MutableStateFlow.update { … }reaching a privateScrollThumbElement.update). The difference is calls #1933 now handles itself: it leaves a call on a library type unlinked, or types it onto its declared class (mihon'sregistercalls reach thePreference.registerextension, okhttp'sconnectionCount()reachesConnectionPool), so they no longer land on a private declaration. http4s, scalachess (Scala) and serilog (C#) were identical in that run, as the private-owner rule is Kotlin's alone.Cost (mihon, median of 3 interleaved fresh indexes, on main
4eb8c2a5): process CPU 25.7 s on main vs 25.5 s, wall 8.2 s on both.Suite:
CODEGRAPH_KERNEL_EXPECT=1 npx vitest run --maxWorkers=4 --minWorkers=1on2aecd2ed(Windows 11, kernel built): 774 files passed, 6 skipped; 9,245 tests passed, 85 skipped, 0 failed.Lands #1761 by @danusha2345.
🤖 Generated with Claude Code