Repository navigation
Conversation
|
Checked 9a41129 against Build and tests: Edge diff,
One regression. In a private Kotlin project, five test methods call a field typed as the concrete class: private lateinit var probe: HttpProbeImpl // implements the Probe interface
fun testA() { probe.probe("relay", "drone", "secret") }
fun testB() { … probe.probe(…) … }
fun tearDown() { probe.close() }
fun otherTest() {
val fake = object : Probe { override fun probe(…) = …; override fun close() = Unit }
…
}
Suggested guard: treat members of an anonymous class that sits inside a function the way Everything else matches the numbers in the description. Nice split of #1872. |
|
Thanks for the detailed edge comparison and the concrete Kotlin example. I reproduced the name-only fallback in a focused regression test: before the fix, I pushed
I cannot access the private Kotlin corpus. Could you recheck the five |
|
Verified 4fedc76 on the same private Kotlin app, plus two others.
LGTM from my side. |
|
I repeated the post- The private Kotlin repro is fixed per the recheck above. I found one separate, source-confirmed false edge in this public corpus: Focused |
|
Reproduced with the three real files at
My guess is that your corpus doesn't include #1933 already covers this: once the receiver's declared type is known and isn't a project type, it emits no edge. With both PRs applied, the result is the same as A narrower guard could also go in #1927, independent of #1933: don't let an |
|
Follow-up to the
These are independent of #1927 and #1933, so this PR's existing commits remain unchanged. Each new PR has a focused regression test, a passing build, and a passing standalone full suite. |
|
The three-file AOSP reproduction still shows a false edge on this PR alone when |
4fedc76 to
58c04ea
Compare
|
Rebased this PR onto #1933 ( |
Integrate Java/Kotlin anonymous inheritance follow-up from upstream colbymchenry#1927
12aec01 to
fb7ff1d
Compare
fb7ff1d to
1ea3c1c
Compare
1ea3c1c to
ca3bb2e
Compare
inferJavaFieldReceiverType reads a field's type from its signature, in the Java shape `Type name`. Kotlin properties are indexed without a signature, and primary-constructor properties (`class A(private val repo: Repo)`) are not indexed at all, so every Kotlin `prop.method()` got no type and fell through to name-only guessing: the interface method, or any same-named method elsewhere (`probe.close()` on a `lateinit var probe: HttpProbe` resolved to an unrelated class's `close`). For Kotlin the type is now read from the declaration: the property's own lines (`name: Type`, or `name = Type(…)`), or the class header before its first member for a constructor property. A nested type keeps its outer type (`HardwareLock.Lease` → `HardwareLock::Lease`), so it matches that `Lease` and not another class's. resolveMethodOnType still validates the method. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…tion tree-sitter-kotlin has no `fun interface` (Kotlin 1.4 functional interfaces). The declaration parsed as a broken function, and when a doc comment followed it, error recovery swallowed the NEXT declaration too: an interface and its methods went missing, surfacing as top-level functions, or a class vanished with its members. The Kotlin extractor's preParse now blanks the `fun` of a `fun interface` declaration (three spaces for three letters, so offsets hold); the kernel receives the same bytes through the preParse hoist and no longer defers these files. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nguessed Two more receivers the Kotlin resolver could not type: - A local or property bound to a call (`val lease = HardwareLock.tryBegin()`, `private val schema = Checker.load()`) is typed by the callee's declared return type, from its signature. A return type nested in the callee's owner keeps it (`Lease` in `HardwareLock` → `HardwareLock::Lease`). - A receiver whose type the project does not declare (`Regex`, `Properties`, a call on `Executors`) now gets no edge. The name-only fallback used to bind it to any project method of the same name: a `regex.find()` to an unrelated `find`, `socket.connect()` to a `connect` elsewhere, a `close()` to itself. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…railing-lambda constructors
More Kotlin receivers the resolver could not type, found on real apps:
- A `val` property is indexed as a `constant` and a companion-object
property as a `variable`; the property lookup only took `field`.
- `var mode = Mode.ON` is typed by its enum (project enums only; `Limits.MAX`
is an Int).
- `val t = Thread { … }` is a constructor too (trailing lambda, no parens).
- `val old = current ?: return` / `current!!` / `val x = current` take the
aliased value's type; `requireNotNull(x)` / `checkNotNull(x)` take x's.
- A typed parameter of the enclosing function or overridden callback
(`onOpen(webSocket: WebSocket, …)`) is used when nothing else names one.
- Known stdlib factories (`listOf`, `mutableMapOf`, `lazy`, `thread`, …)
return a library type; any other library function leaves the type unknown.
A library type still gets no edge: `thread.join()`, `process.destroy()`,
`webSocket.send()` no longer bind to project methods (or a test fake) of the
same name.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A Kotlin call through a receiver chain (`engine.pump.drain()`, `this.engine.drain()`, `a?.b?.c()`) was extracted as the bare method name, so it could only be name-guessed: a same-named method in the caller's file, or any project method when the chain ended in a library type (`runtime.reconnectTask?.cancel()` on a `ScheduledFuture`). The extractor (wasm path and kernel, in parity) now keeps a chain of up to four identifier segments. The resolver types the first segment as a single receiver (`this` is the enclosing class, a type name its object or companion), then each next segment as a property or enum entry declared in the class of the type before it, read from that class's own file. A typed chain resolves on its type; a chain through a library type gets no edge unless the project declares an extension of that name on a library type; an untyped chain resolves as the bare method name, exactly as before. A constructor call with type arguments (`Crate<Int>()`, `mutableListOf<T>()`) now types its value too. The alias test now picks the anonymous object's `onOpen` and ignores synthesized override edges, so it holds once object-literal members are extracted. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nonymous objects A receiver (or chain segment) that the enclosing class does not declare is looked up in its project supertypes, read from each class header (breadth- first, at most four levels); a library or ambiguous supertype is not walked, so the receiver stays unknown and keeps the name-only resolution. The local declaration scan continues from a function nested in another (an anonymous object's member, a local function) into the outer function above it, skipping sibling functions whose locals are not visible. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ca3bb2e to
3bfb25c
Compare
Summary
Preserve Java anonymous classes and Kotlin object-literal members, resolve nested supertypes in source context, and resolve Kotlin property and chained receivers by declared type. Missing external types and ambiguous owners remain unresolved. The original Kotlin receiver commits and authorship from #1933 are retained.
Validation
8a5ba3d02c833f205b5453305b47fffee0008a6b; validated head3bfb25c07ef5d41df097e861786042b920c71927.eda54a121cbc06b13a5f97d215f239e639a22b87: its receiver helpers match the implementation retained here; original authorship and receiver regressions remain preserved.