Files
codegraph/src/resolution
Colby MchenryandClaude Opus 5.5 628f69e275 fix(kotlin): repository().pending() links the method of the type repository() declares (#2822)
Issue #2805: in a Kotlin test class with `private fun repository():
RealRepo`, `repository().pending(1)` went to a test double's
`FakeRepo.pending` in another package, and `val repo = repository();
repo.pending(1)` to the interface `Repo.pending`. The extractor records a
call on an unqualified call's result by the outer method's bare name (a
capitalized inner, `Foo().bar()`, is kept as a chain), so the name matcher
took whichever `pending` came first; and the local's type was read only
from a constructor or an annotation, so the receiver's name guessed it.

The name matcher now reads such a call where it is written
(`repository().pending(1)`, `this.repository()…`, `nullable()?.pending()`)
and finds the inner function the way Kotlin does: a method of the calling
class, of a class it inherits from or one around it, read from their
declarations, else a top-level function the file can name. The call is
resolved on the type that function's signature declares, including what
the type inherits and extensions on it, or links nothing when the type
lacks the method: the #750 rule, as for `Foo.factory().bar()`. A local set
from such a call alone (`val repo = repository()`) takes that type for
the existing typed-local path. Edge cases the A/B surfaced:
- a nested type is the one the function names, `Request.Builder` or a
  plain `Builder` written in `Request`, matched by whole names
  (`MockResponse.Builder` is no `Response.Builder`);
- an extension is the one the calling file can name (Exposed declares
  `Expression<T>.year()` in three modules);
- a type from outside the project, a call inside an extension on one,
  and an extension the index keeps unattached (on a nullable receiver, or
  `fun Dns.Call.x()` filed under `Dns`) keep the name match as before.

Validation:
- New kotlin-call-result-receiver test (9 cases): 7 fail on main, the two
  that guard unchanged behavior pass on both; all pass with the fix on the
  kernel and on the wasm path. Mutation checks confirm the owner-path,
  scoped-nested-type, whole-segment and extension-visibility rules are
  each exercised.
- A/B on main e32021f6, calls by call site, nodes identical:
  okhttp c317930: 82 retargeted, 176 gained, 3 dropped, 516 confidence
  only; JetBrains/Exposed 09f814c: 152 retargeted, 138 gained, 1 dropped,
  1353 confidence only; nowinandroid 2982e63: 1 confidence only. Controls
  gin (Go), nest (TypeScript) and mybatis-3 (Java): byte-identical.
- Triage: retargets go from name guesses to the declared type
  (`request().post()` → `Request.Builder.post`, `varchar(…).index()` →
  `Column.index`, `rowNumber().over()` → `RowNumber.over`,
  `newReader().readInt()` → `Hpack.Reader.readInt`); gains are locals now
  typed (`val connection = connect(peer)`); the 4 drops are Kotlin's
  `apply` scope function, which went to a test helper's or a Gradle
  plugin's `apply`. Every retarget group was checked against the source.
- Cost: whole index, median of 3 interleaved: okhttp 10.5 s -> 10.9 s,
  Exposed 17.7 s -> 16.8 s, within noise.

Fixes #2805

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 02:12:42 +00:00
..