Repository navigation
Description
Summary
Follow-up to #2155 / #2366. With the receiver-aware weak-member guard, a Scala call recv.m(...) whose receiver is a value (values.get(k), xs.foreach(_.register()), underlying.close()) no longer binds an arbitrary project-wide m by short name. That is the right default, but it also drops the edges where the receiver's type is knowable from the caller's own source — a field, parameter or local with an explicit type annotation, or a constructor call in scope.
Measured on twitter/finagle (ca472de) in #2366: of the 9,149 CALLS edges the guard removes, 2,549 (28%) target a class whose name appears in the caller's file (import, type annotation, new). A sample of those is a mix of correct edges (Memcached.serve → Server.serve, CachingPool.checkout → Service.close, TypeTest.testRow → Row.intOrZero) and coincidences (PushStackServerTest → Stack.exists, RefCountedFactoryTest → FailureFlags.isFlagged). The other 72% target a class the caller file never mentions and are almost certainly noise.
Proposal
Receiver-type inference for Scala, applied before the registry resolves a member call:
- Extraction records, per call, the receiver's root identifier (
valuesinvalues.get(k),underlyinginunderlying.close()); today onlyis_methodis set. - For the caller's file, build a
name → declared typemap from the shapes the grammar exposes directly:val x: T = …,var x: T, constructor/method parameters(x: T),val x = new T(...),val x = T(...)/T.apply(...)(companion factory). - When the root identifier has an entry
TandTresolves to a class in the graph (through the import map or the same file), resolvemasT.m/T$.m— a new strategy such asreceiver_typesitting abovesuffix_matchin confidence. When it does not, the call keeps the current suppressed outcome (no weak short-name fallback). - Only step 3's positive case adds edges; the guard from fix(scala): suppress weak short-name matches for receiver calls #2366 is unchanged for everything else.
Out of scope for a first cut: inherited members (T.m declared on a parent), generics (Seq[T]), inferred val x = someCall() types, implicit conversions.
Expected effect
Recovers a subset of the 2,549 "owner mentioned" edges — the ones where the type is written down — without reintroducing the 6,600 where it is not. A before/after on finagle with the same edge-identity comparison as #2366 would show recall regained per strategy and the residual.
Related
- Scala: receiver.method(...) calls are bound to an arbitrary same-named symbol by short name (weak-member suppression not applied) #2155 / fix(scala): suppress weak short-name matches for receiver calls #2366 — receiver-aware weak-member suppression for Scala (the guard this builds on)
- Scala: companion object and class share one qualified name, so their members overwrite each other #2154 / fix(scala): keep companion objects distinct from their classes (#2154) #2361 — companion object QNs (
T$.mfor object members) - Scala: parse import selectors/aliases/wildcards and resolve imports against the declared package instead of the directory layout #2153 / feat(scala): parse import selectors and resolve package-aware imports (#2153) #2360 — package-aware imports (used to resolve
Tfrom the caller's imports)
Activity
DeusData commentedon Sep 27, 2026
Thank you for the detailed follow-up and for separating the potentially useful edges from the coincidental matches in the measurements.
This proposal adds Scala receiver-type inference on top of the pending weak-member guard in #2366. We are keeping Issues focused on actionable defects, so please open a GitHub Discussion for this enhancement and link this issue and #2366 there: https://github.com/DeusData/codebase-memory-mcp/discussions
I am closing this as an enhancement request so the idea can be explored in Discussions. This does not decide whether it should be implemented. A small, separately reproducible incorrect or missing edge on a specified version is welcome as a linked bug report.
Thanks again for the concrete proposal and examples.