Skip to content

Scala: companion object and class share one qualified name, so their members overwrite each other #2154

Open

Description

@htarnacki

Version

Built from source, main @ 9b24332 (v0.10.8-181)

Platform / Install channel / Binary variant

Linux (x64) / Built from source / standard

What happened, and what did you expect?

A Scala class Foo and its companion object Foo share the source name, so both containers and their members get the same qualified name and overwrite each other in the graph. Whichever is extracted last wins; methods of the other one disappear, and CALLS edges to them are lost or bound to the wrong owner. Class-body val/var definitions are not qualified by their declaring owner either, so members of the class and the companion collide as well.

Expected: the companion object gets a distinct qualified name (the conventional Foo$, as used by scalac and most Scala tooling), everywhere a class QN is computed, and body-level val/var are qualified by their owner.

Reproduction

  1. Dummy snippet (t/ValidateMandatoryInvoiceNumber.scala):
case class ValidateMandatoryInvoiceNumber private (invoice: Invoice) {
  override lazy val isValid: Boolean = invoice.number.isDefined
  def isMandatory: Boolean = true
}
case object ValidateMandatoryInvoiceNumber {
  def createComponentTasks(invoice: Invoice) =
    List(ValidateMandatoryInvoiceNumber(invoice))
}
  1. Command: codebase-memory-mcp cli index_repository '{"repo_path":"/tmp/repro"}' then
    codebase-memory-mcp cli search_graph '{"project":"repro","name_pattern":"ValidateMandatoryInvoiceNumber.*"}'

  2. Result: a single container t.ValidateMandatoryInvoiceNumber.ValidateMandatoryInvoiceNumber; only one of isValid/isMandatory vs createComponentTasks survives.
    Expected: …ValidateMandatoryInvoiceNumber (class, with isValid, isMandatory) and …ValidateMandatoryInvoiceNumber$ (object, with createComponentTasks).

Proposed fix

Use the Foo$ suffix for the companion in every QN computation and qualify class-body val/var by their declaring owner. One commit with extraction + pipeline tests. It is stacked on the Scala import/package work (#2153) because the tests share fixtures; branch:
https://github.com/htarnacki/codebase-memory-mcp/tree/feat/scala-receiver-resolution (second commit).

Confirmations

  • I searched existing issues and this is not a duplicate.
  • My reproduction uses shareable code (a dummy snippet), not proprietary code.

Activity

  1. added
    bugSomething isn't working
    priority/highNeeds near-term maintainer attention; high-impact bug, regression, safety issue, or release blocker.
    on Sep 19, 2026
  2. DeusData commented on Sep 25, 2026

    @DeusData
    Owner

    Hi @htarnacki, thank you so much for this report and for already writing the fix. The repro makes the collision obvious, and following scalac's Foo$ convention is a natural way to keep both the class and its companion findable.

    Since you've already done the work (commit 84c6ea7 on feat/scala-receiver-resolution), we'd much rather merge your change, with your authorship, than re-implement it. Could you open it as a PR stacked on the #2153 one (see my note there)? Both would need a rebase onto current main, which has moved a lot since 09-09. Please keep the receiver-resolution commits for separate PRs so each stays small and easy to review.

    One question we'd like your view on: should a standalone object Foo with no companion class also get the $ suffix, or only a companion? The first changes the qualified name of every Scala object in existing graphs. Thanks again, your Scala work is a real gift to graph quality here!

  3. htarnacki commented on Sep 26, 2026

    @htarnacki
    ContributorAuthor

    Opened #2361, stacked on #2360.

    On the standalone-object question: only companions get the $ suffix. A standalone object Foo has nothing to collide with, and suffixing it would rename every Scala object in existing graphs. With this rule the only QNs that change are the ones that were previously lost to a collision. Details and the finagle before/after are in the PR.

  4. added 6 commits that reference this issue on Sep 27, 2026
    0d720d5
    06ef2f6
    8116eaa
    4a0f966
    a5652ee
    bff50fe
  5. DeusData commented on Oct 19, 2026

    @DeusData
    Owner

    Thank you for opening #2361 and clarifying that only colliding companion objects get the suffix. That answers the question raised above; there is no new verified review or landing status to add in this triage.

  6. DeusData commented on Oct 25, 2026

    @DeusData
    Owner

    Thank you for #2361 and the companion-only suffix clarification. The next review is on our side; that question has already been answered. Sorry for the delay; I’ll follow up soon.

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

    bugSomething isn't workingparsing/qualityGraph extraction bugs, false positives, missing edgespriority/highNeeds near-term maintainer attention; high-impact bug, regression, safety issue, or release blocker.

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions