Skip to content

Scala: receiver-type inference for member calls (recover edges suppressed by the weak-member guard) #2384

Description

@htarnacki

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:

  1. Extraction records, per call, the receiver's root identifier (values in values.get(k), underlying in underlying.close()); today only is_method is set.
  2. For the caller's file, build a name → declared type map 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).
  3. When the root identifier has an entry T and T resolves to a class in the graph (through the import map or the same file), resolve m as T.m / T$.m — a new strategy such as receiver_type sitting above suffix_match in confidence. When it does not, the call keeps the current suppressed outcome (no weak short-name fallback).
  4. 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

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    parsing/qualityGraph extraction bugs, false positives, missing edges

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions