mirror of
https://github.com/colbymchenry/codegraph.git
synced 2026-11-14 16:52:09 +08:00
A supertype written through its enclosing type was lost in three ways.
Java and C# kept the dotted path (class Named extends Outer.Inner,
class N : Outer.IListener), but the qualified-name match compared it
verbatim with p::Outer::Inner, so it stayed unresolved; a Java anonymous
class (new Api.Listener() {...}) kept only the last segment, which no
nested type is found by; and Kotlin (both engines) took the FIRST
segment, so class K : Outer.Inner() extended Outer. A Kotlin object
expression, object : Listener {...}, made no class at all: its overrides
became local functions of the code around it, which a call through the
interface never reached, and bare calls elsewhere could be guessed onto
them.
A dotted Java, Kotlin or C# supertype now resolves to the type whose
qualified name ends with the path, when the file can name the path's
first type (its own file, package or namespace, an import; C#'s usings);
of several, the one closest in the tree, or none on a tie. The Java
anonymous class keeps the written path in its extends reference, and
Kotlin joins every segment of a supertype (type arguments left out), in
the wasm extractor and the kernel alike. A Kotlin object expression is
indexed as Java's anonymous class is: a class <Listener$anon@line> that
extends each supertype as written, which the code around it
instantiates (the class it builds, else its first supertype, also as
written and only ever a type that can be a supertype), with its body's
declarations as members; its header (constructor arguments, a
by-delegate) stays the enclosing code's. An anonymous class's members,
Java's and Kotlin's, are named only inside the declaration holding the
class: a bare block() elsewhere is never an object's override val block.
The walk up a Java or Kotlin class's supertypes for an inherited method
asks each supertype by the chain of types that names it
(AssistantMessage::Builder) before its simple name, so a builder's
build() is its own AbstractBuilder's and not an MCP annotation's;
the name still answers when an extends edge reached a namesake
(Minestom's Player extends LivingEntity).
The code in an object expression still sees the scope it is written in,
as when its overrides were local functions: a property of the class
around the object types a receiver there (okhttp's server.url() in an
object : Callback is MockWebServer's), an override is not what a call
of its own name in it means (original!!.contentLength(),
this@Outer.onScroll(), an extension receiver's onDataChanged()), and an
object's members out of reach leave a by-name guess as it was
(okhttp's it.isReady() is still the SerialTask interface's).
Validation:
- jvm-dotted-supertypes.test.ts: 13 tests, 11 fail on main 60b0d7a9
(the other two guard a dotted supertype the file cannot name and the
calls inside an anonymous class), all pass here.
- Kernel parity, 0 files with diffs: torture.kt 1/1, Torture.java 1/1,
okhttp 588/619 Kotlin and 71/71 Java, mihon 891/940, nowinandroid
340/350, SystemUI 188/198 Kotlin and 1141/1141 Java, jextract 218/218,
Minestom 1859/1894 (deferrals are main's).
- A/B against main 60b0d7a9, sites added/removed/retargeted: okhttp
+640/-53/149, SystemUI +435/-53/116, Minestom +165/-3/6, kestra
+108/-0/6, spring-ai +46/-0/38, playframework +27/-3/13, mihon
+22/-5/6, jextract +22/-0/1, spring-modulith +9/-0/10, nowinandroid
0/0/0; serilog, click, gin identical.
- Cost (median of 3 interleaved): okhttp CPU 27.8 -> 29.0 s, wall
8.6 -> 8.8 s (its 360 object classes and 1,491 more edges); mihon
23.6 -> 23.8 s, wall 7.8 -> 7.7 s.
Reimplements #1927 by @mixxer.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>