Skip to content

fix(scala): an Outer.Inner(...) call reaches the Scala case class or object it builds - #2924

Merged
colbymchenry merged 1 commit into
mainfrom
claude/pr-1951-scala-outer-inner
Oct 27, 2026
Merged

colbymchenry merged 1 commit into
mainfrom
claude/pr-1951-scala-outer-inner

Conversation

@colbymchenry

Copy link
Copy Markdown
Owner

Summary

A Scala call like RemoteNode.Obfuscated("x"), Uci.Move(from, to) or Header.Raw(name, value), a case-class or companion apply on a nested type, now links to the Scala case class or object it builds, and a Scala call no longer takes an unrelated Java constructor through a return-type annotation.

Root cause

Scala qualified names carry no package (Outer::Inner), so a call written Outer.Inner(...) found no type by its written name and fell through to the name strategies. Under def f(): WebSocket = WebSocket.Accepted(flow), the local-declaration reader took WebSocket for the receiver's type and handed over an unrelated Java constructor (Other::Accepted::Accepted).

From the contributor, and what we changed

From @danusha2345 (#1951), unchanged: the Scala Outer.Inner type lookup before the receiver-word strategy (one file, or the call's own file; a class or trait before an object), the guard that keeps the typed-receiver path from handing a Scala call another language's constructor, and the five resolution.test.ts cases.

Our changes: the CHANGELOG bullet rewritten in the house format (without the "never guessed" claim: only the typed path is guarded, though the receiver-word strategy never produced such an edge in our fixtures or repos) and moved next to the JVM overload bullet. Rebased onto main 81559dcc, which has #2875 (#1950), #2874 and #2923 (#1936).

Validation

Tests. 5 cases in resolution.test.ts: 4 red on main, all green here; the fifth, a Scala call to a real Java static method, passes on both and guards that link.

A/B against main 81559dcc (with #1950 and #1936), fresh indexes, edges compared by site (file, kind, position, ref name) and target identity (name, file, line):

repo files edge sites added removed retargeted
lichess-org/scalachess a498819 245 77 0 0
http4s/http4s 53b931f 765 371 1 0
playframework f3eb322 (Java + Scala) 2,727 291 1 0
playframework/play-samples 19eb65b 867 2 0 0
yahoo/CMAK 30abfde 190 identical
pallets/click, gin-gonic/gin (controls) identical

Triage:

  • Added: case-class and object applies, all sampled right: scalachess Uci.Move (17), Uci.Drop (13), Position.AndFullMoveNumber (11), Clock.Config (7), Eval.Mate (an object); http4s Header.Raw (66), H2Frame.Data (36), Uri.Ipv6Address (20), WebSocketFrame.Text (16), Uri.RegName (16), Request.Connection over the same-named headers.Connection. playframework: all 291 are Scala applies of nested case classes, recorded as instantiations at 0.85: RemoteNode.Ip (54), RemoteNode.Obfuscated (53), RemoteNode.Unknown (33), NodePort.Numeric (31), HttpEntity.Strict (20), HttpEntity.Streamed (19), WebSocket.Accepted (15), ApplicationLoader.Context under import play.api._ beside the renamed Java JApplicationLoader; these include the HttpEntity and WebSocket applies that fix(play): look up Class.method among the class's own members, not the whole file #1950 unlinked from Java constructors.
  • Removed: both are retargets (a call becomes an instantiation, so the site reads as removed and added). http4s: Origin.Host(...) moves from a 0.7 guess on defaults::Host to the Origin.Host case class. playframework: Writeable.scala's HttpEntity.Strict(...), in package play.api.http, moves from the Java play.http.HttpEntity.Strict constructor to the Scala case class.
  • With fix(play): look up Class.method among the class's own members, not the whole file #1950: on play-samples, fix(play): look up Class.method among the class's own members, not the whole file #1950 removed two wrong edges from UserActor.WatchStocks(...) and UserParentActor.Create(...) to Java constructors of another sample; this PR links both to their Scala case classes. On scalachess and http4s the two are independent: fix(play): look up Class.method among the class's own members, not the whole file #1950 gave +6 on scalachess and nothing on http4s, and this PR gives the same +77 and +371/-1 with or without it. The two merged cleanly in either order.

Cost (http4s, median of 3 interleaved fresh indexes, measured on main 69af19ac, where the A/B gave the same edges as on 81559dcc): wall 8.6 s on main vs 7.8 s with this PR, process CPU 27.5 s vs 26.2 s: within noise.

Suite: CODEGRAPH_KERNEL_EXPECT=1 npx vitest run --maxWorkers=4 --minWorkers=1 on c3937888 (Windows 11, kernel built): 760 files passed, 6 skipped; 9,037 tests passed, 83 skipped, 0 failed.

Lands #1951 by @danusha2345.

🤖 Generated with Claude Code

…object it builds

Scala qualified names carry no package (Outer::Inner), so a call written
Outer.Inner(args), a case-class or companion apply, found no type by its
written name. It fell through to the name strategies, and under a return
type annotation (def f(): WebSocket = WebSocket.Accepted(flow)) the
local-declaration reader took WebSocket for the receiver's type and handed
over an unrelated Java constructor, Other::Accepted::Accepted.

Before the receiver-word strategy, a Scala call whose member is
capitalized now resolves to the Scala type Outer::Inner when that is
unambiguous: every match in one file (a class and its companion), or one
in the call's own file; a class or trait wins over a same-named object.
It is recorded as instantiates for a class, as a call for an object. The
typed-receiver path no longer hands a Scala call another language's
constructor. Real Java static methods are still reached from Scala.

Validation:
- resolution.test.ts: 5 cases, 4 red on main; the fifth (a Scala call
  to a real Java static) passes on both and guards the Java side.
- A/B against main 81559dc (with #1950 and #1936): scalachess +77/-0,
  http4s +371/-1 (the one a retarget from a wrong defaults::Host to the
  Origin.Host case class), playframework +291/-1 (the one a retarget:
  Writeable's HttpEntity.Strict(...) from the Java play.http constructor
  to the Scala case class), play-samples +2 (two case-class applies #1950
  unlinked from Java constructors); CMAK, click and gin identical. Every
  addition is a Scala case-class or object apply; sampled all right
  (Uci.Move, Header.Raw, H2Frame.Data, RemoteNode.Ip,
  ApplicationLoader.Context, Request.Connection over headers.Connection).
- Cost (http4s, median of 3 interleaved fresh indexes, on main
  69af19a): wall 8.6 s on main vs 7.8 s, process CPU 27.5 s vs
  26.2 s, within noise. The A/B gave the same edges on both bases.

Original PR #1951 by @danusha2345.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.

1 participant