Skip to content

fix(scala): classify companion objects as module; prefer type defs for extends/implements resolution - #1826

Open
htarnacki wants to merge 1 commit into
colbymchenry:mainfrom
htarnacki:fix/scala-companion-object
Open

fix(scala): classify companion objects as module; prefer type defs for extends/implements resolution#1826
htarnacki wants to merge 1 commit into
colbymchenry:mainfrom
htarnacki:fix/scala-companion-object

Conversation

@htarnacki

@htarnacki htarnacki commented Sep 9, 2026

Copy link
Copy Markdown

Fixes #1824

Problem

Scala object definitions were indexed as kind class. In the trait + companion object idiom the trait and its companion share both name and file, so extends X references resolved by name matching tied between the two candidates. When the companion won, the extends edge landed on the singleton and every subtype was detached from the trait's inheritance chain — codegraph impact on the trait stopped at depth 1.

Changes

  • Extraction (scala.ts, tree-sitter-types.ts, tree-sitter.ts): classify Scala object_definition as kind module instead of class. A Scala object is a singleton value, not a type, so it can never be a valid extends target.
  • Resolution (name-matcher.ts): for inheritance references (isInheritanceRef), penalize module candidates in findBestMatch, so the trait wins the tie deterministically. module stays in SUPERTYPE_TARGET_KINDS (fix(resolution): constrain inheritance/import reference target kinds (#1536, #1537) #1796) — it is still eligible (Ruby include, TS namespaces), just ranked below a same-named type.
  • Test (resolution.test.ts): trait + companion in one file, subclass in another — asserts the extends edge targets the trait (none on the module) and that getImpactRadius from the trait reaches the subtype.
  • CHANGELOG: entry under [Unreleased].

Verification

On a production Scala codebase (~900k nodes), codegraph impact -d 10 <widely-mixed-in trait> went from 28 to 186 affected symbols after this change (combined with #1825).

Rebased on current main (post #1796). extraction, resolution, graph and frameworks suites green on Node 24 (FTS5); the remaining engine-suite failures in my environment are identical on a clean origin/main checkout (no dist/viewer, daemon/MCP process tests).

The Scala trait + companion object idiom puts two same-named symbols in
one file. Both were indexed as kind 'class', so an 'extends X' reference
tied between the trait and its companion and the winner was arbitrary.
When the companion won, every subtype was detached from the trait's
inheritance chain, and impact analysis on a widely-used trait stopped at
depth 1.

- classify Scala object_definition as 'module' (a singleton value, not a
  type - 'extends' can never target it)
- add 'module' to the classifyClassNode contract and handle it in the
  extractor dispatch
- in name matching, bias extends/implements references toward actual
  type definitions and penalize 'module' candidates, so the trait wins
  the tie deterministically

Regression test: trait + companion in one file, subclass in another -
the extends edge must land on the trait and impact must reach the
subtype through it.
@htarnacki
htarnacki force-pushed the fix/scala-companion-object branch from 9a2f10f to 1498217 Compare September 10, 2026 07:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Scala companion object hijacks extends resolution — impact analysis stops at depth 1

1 participant