Repository navigation
fix(kotlin): a call through a property or a receiver chain resolves on its declared type - #2972
Merged
Merged
Conversation
…n its declared type Kotlin indexes a class property without a signature, and a primary-constructor property (class A(private val repo: Repo)) is no node at all, so a call through either (probe.close() on a lateinit var probe: HttpProbe) found no receiver type and fell to the name-only strategies: the interface's method, or any same-named method elsewhere. A call through a chain (engine.pump.drain()) was emitted as its bare method name, and a local bound to a qualified call (val lease = HardwareLock.tryBegin()) was untyped. tree-sitter-kotlin also predates fun interface: such a declaration parsed as a broken function and, with a doc comment after it, its error recovery swallowed the next declaration too. The declared type is now read from the declaration itself: a property's own lines, the class header for a constructor property, a callee's declared return type for a local bound to a call, an enum entry's enum, each segment of a receiver chain in turn (the chain is kept in the reference name, by the wasm extractor and the native kernel alike), a superclass's property, and an outer function's local inside an anonymous object. A receiver whose type the project does not declare (Regex, Properties, a JDK class) gets no edge instead of a name-only guess, unless the project extends that type. The extractor's preParse blanks the fun of a fun interface, offset-preserving, so the file parses cleanly in both paths. Validation: - kotlin-property-receiver.test.ts: 17 tests, 12 red on main, all green here; kernel-kotlin-parity.test.ts pins the fun interface fixture. - Kernel parity (kotlin), 0 diffs: okhttp 588/619 files byte-identical (main's kernel 578, the rest deferred to wasm on parse errors), mihon 891/940 (887), nowinandroid 340/350 (339): the fun interface files no longer defer. - A/B against main with #1942 (positions only: the extractor now keeps a chain in the reference name): okhttp +299/-257, 2,554 retargeted, 2,158 of them test calls moving from the deprecated okhttp3.mockwebserver.MockWebServer (a 0.65 name guess) to the mockwebserver3.MockWebServer each file imports; mihon +82/-118, 337 retargeted (Preference get/changes/set/register onto the declared Preference); nowinandroid -9, 23 retargeted (properties onto their concrete test doubles); SystemUI's Kotlin +1/-104, 7 retargeted; jextract, click and gin identical. The removed are name guesses on library types (okio sinks, collections' filter and clear, OkHttp bodies, MutableStateFlow.update, Kotlin's apply). 7 right edges are lost where a chain passes through a library wrapper (Lazy.value, StateFlow.value, Pair.first, a ViewBinding's view). - Cost (mihon, median of 3 interleaved indexes): CPU 25.8 s on main with #1942 vs 26.7 s, wall 8.3 vs 8.4 s. Original PR #1933 by @danusha2345. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
danusha2345
pushed a commit
to danusha2345/codegraph
that referenced
this pull request
Oct 28, 2026
…mchenry#2972; 10 landed PRs yield to main; EXTRACTION_VERSION 56) Main landed ten more of our PRs as our own commits with the maintainer's additions (colbymchenry#2923 ← colbymchenry#1936, colbymchenry#2924 ← colbymchenry#1951, colbymchenry#2925 ← colbymchenry#1939, colbymchenry#2930 ← colbymchenry#2921, colbymchenry#2940 ← colbymchenry#1736, colbymchenry#2942 ← colbymchenry#1947, colbymchenry#2950 ← colbymchenry#1952, colbymchenry#2961 ← colbymchenry#1954, colbymchenry#2965 ← colbymchenry#1942, colbymchenry#2972 ← colbymchenry#1933) and the module-path half of colbymchenry#2308 (colbymchenry#2955). Where the integration carried our version, main's wins: the enclosing-scope member pick and its tests, the Go and Java receiver typers, the delegating-call self-loop rule, the duplicated-module copy confidence, the C #if collapse, the module-path resolution of Rust paths, the trail/path hardening in utils.ts. Kept beside main: colbymchenry#1875's Verilog hooks and status fields, colbymchenry#2839's implementations command, colbymchenry#1923, colbymchenry#2922, colbymchenry#2258, colbymchenry#1761, colbymchenry#1583, the bare-call half of colbymchenry#2308 (rust-use-bindings.ts). CHANGELOG is main's plus the open PRs' bullets. EXTRACTION_VERSION 56: the C #if group handling (colbymchenry#2940) and the kernel change. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This was referenced Oct 29, 2026
Merged
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 call through a property now links to the method of the type the property is declared with: a
lateinit var server: MockWebServer, a constructor propertyclass A(private val repo: Repo), avalset from a constructor or from a call whose function declares its return type, a property a class inherits, a chain likeengine.pump.drain()orMode.ON.next(), and a local of the function around an anonymousobject : …. A call on a type from outside the project, likeRegexor a JDK class, no longer links to a same-named method of the project. Afun interfaceis now indexed as the interface it is, instead of a broken function that could hide the declaration after it.Root cause
Kotlin indexes a class property without a signature, and a primary-constructor property is no node at all, so main's field reader found no type for either and the call fell to the name-only strategies: an interface's method, or whichever method had the name. A call through a chain was emitted as its bare method name, so its receiver was never typed. tree-sitter-kotlin predates
fun interface: such a declaration parsed as a broken function, and with a doc comment after it, error recovery swallowed the next declaration too, so that interface and its methods went missing and its members surfaced as top-level functions.From the contributor, and what we changed
From @danusha2345 (#1933), unchanged: the Kotlin declared-type reader (a property's own lines, the class header for a constructor property, a callee's declared return type, an enum entry's enum, aliases,
requireNotNull-style pass-throughs, a superclass's property, an outer function's local inside an anonymous object), the receiver chain kept in the reference name by both extractors (tree-sitter.tsand the kernel'skotlin.rs) and typed segment by segment, the library-type rule (no edge unless the project declares an extension of that name on a library type), thefun interfacepreParse blanking, its kernel-parity fixture, andkotlin-property-receiver.test.ts.Our changes: the two CHANGELOG bullets rewritten in the house format and moved after the other Kotlin call bullets. The PR was rebased onto main by its author after our first review; the new head's diff equals the old one apart from the
resolution/index.tsimport line, and we re-read every changed line before running it. Rebased onto main77ee3484, which has #1942 (#2965): the two shareinferJavaFieldReceiverType's field block, where the Java branch is #1942's and this PR adds the Kotlin kinds and the Kotlin branch.Validation
Tests.
kotlin-property-receiver.test.ts: 17 tests, 12 red on main, all green here;kernel-kotlin-parity.test.ts(6) green with the rebuilt kernel.Kernel parity (
scripts/kernel-parity.mjs --lang kotlin), 0 files with diffs: okhttp 588/619 files byte-identical (578 with main's kernel), mihon 891/940 (887), nowinandroid 340/350 (339), SystemUI's Kotlin 188/198, the torture fixture 1/1. The files that no longer defer to wasm are thefun interfaceones, which now parse cleanly.A/B against main
7973df0d(with #1942;77ee3484after it changes only TypeScript path aliases), fresh indexes with each arm's kernel. Edges are compared by position and target, without the reference name, since this PR keeps a chain in it. The changed sites are the same, site for site, as in an earlier run against4eb8c2a5with #1942 stacked under this PR:Every changed edge has a Kotlin caller (one Java caller in okhttp reaches a
fun interfacemethod). Triage:server.enqueue(…),url,takeRequest,useHttps) moving from the deprecatedokhttp3.mockwebserver.MockWebServer, which main's 0.65 receiver-name guess picked, tomockwebserver3.MockWebServer, which all 47 calling files import. Others sampled right:finder.find()onprivate val finder = FastFallbackExchangeFinder(…),server.takeRequest()onprivate val server = FakeDns(),ANY_VALUE.fromDerontoBasicDerAdapter. mihon's arePreferencecalls (get99+,changes69,register46,set39) moving from a subclass main guessed to the declaredPreference; nowinandroid's move properties onto the concrete test doubles they are declared as (TestUserDataRepository).sink.writeUtf8→ a projectDerWriter), collections'filter,clear,remove, OkHttp response bodies, coroutines'MutableStateFlow.update(which reached nowinandroid'sScrollThumbElement.update), Kotlin'sapply, SQLDelight query objects, Android'sSnackbarData. Right edges lost, 7: a chain through a library wrapper reads as a library type, sodownloadManager.value.renameSource(…)andextensionManager.value.getExtensionPackage(…)(aLazy),result?.first?.toSuccessSearchResult()(aPair),activity.binding.navigationOverlay.setNavigation(…)×3 (a generated view binding) on mihon, andviewModel.uiState.value.shouldKeepSplashScreen()(aStateFlow) on nowinandroid.WebSocketRecorder.assertOpen(47) and other recorder andMockWebServercalls through chains, andHttpLoggingInterceptor.Logger.log, afun interfacemethod now indexed; mihon'sPreference.collectAsState(51).fun interfacedeclarations are interfaces with their methods, and the declarations after them come back.Cost (mihon, median of 3 interleaved fresh indexes): process CPU 25.8 s on main with #1942 vs 26.7 s with this PR, wall 8.3 s vs 8.4 s.
Suite:
CODEGRAPH_KERNEL_EXPECT=1 npx vitest run --maxWorkers=4 --minWorkers=1on7bcacd9b(Windows 11, kernel built): 772 files passed, 6 skipped; 9,241 tests passed, 85 skipped. Two tests hit the 5 s timeout while other jobs loaded the machine (explore-long-query.test.ts,mcp-nested-repo-projectpath.test.ts, neither Kotlin); both files pass re-run on the same commit, 26/26.Follow-ups (not in this PR)
Lazy.value,StateFlow.value,Pair.first, a view binding's views) reads as a library type: 7 right edges on mihon and nowinandroid.JAVA_EXTERNAL_TYPEandKOTLIN_EXTERNAL_TYPEare the same sentinel string and could share one constant.Lands #1933 by @danusha2345.
🤖 Generated with Claude Code