Repository navigation
fix(kotlin): resolve member calls through typed receivers and implicit this - #803
kunalgoesbyken wants to merge 1 commit into
Conversation
Kotlin calls were resolved by name against functions only, so a call on a constructor property, a parameter, a typed or constructed local, an object or companion, or an unqualified member of the same class produced no edge, while the Java twin of each resolved. `callers FooService.handle` reported nothing for a service wired by constructor injection (trailhq#431). Bind `val`/`var` constructor parameters, function parameters, and typed or constructor-initialised properties and locals, scoped the way the walk scopes them. Read `this.repo.call()` as a field access, treat an UpperCamelCase receiver as the type itself (objects, companions), and let a lowercase bare call inside a type resolve to the member first and the top-level function otherwise, as Swift does. A lambda parameter, loop variable, destructured name, untyped local or function-typed parameter hides a field of the same name, `this@Outer` means the outer class, and a bare call inside `apply`, `run`, `with` or a `buildX` lambda is not read as a member of the enclosing class.
🌱 graft blast radiusNothing outside this diff depends on it. 1 area changed; no indexed dependents at depth 2. Who knows this code — 2 people across 1 area
Ownership is git history over each area's own files, weighted towards recent work (120-day half-life). Merge commits and bots are dropped, and you are dropped from your own PR. A name with no Test signal per changed area — 1 ✗Reached = a node under a test path has a resolved edge into the changed symbol. It undercounts anything called indirectly — through a CLI, a spawned process or a dynamic import — so read a low ratio as “look here”, never as a coverage gate.
Open the interactive graph → — click an area to see the code that changed, and the line that reaches it. |
Summary
Kotlin calls were resolved by name against functions only, so a call on a constructor property, a parameter, a typed or constructed local, an object or companion, or an unqualified member of the same class produced no edge, while the Java twin of each resolved. #431 reports the constructor-property case; the same missing receiver typing breaks all of them.
Changes
bindings.ts: a Kotlin case indefName(so binding scopes line up withextract.ts) and ahandleKotlinthat bindsval/varconstructor parameters as fields, function parameters, and typed or constructor-initialised properties and locals. A plain constructor parameter is not visible to the methods and binds nothing. An UpperCamelCase receiver with no binding is the type (Utils.helper(),Registry.create()), as in Swift.forvariable, a destructured name, an untyped local or property, a function-typed parameter.extract.ts:this.repo.save()reads as a field access,this@Outer.save()as a call onOuter, and a lowercase bare call inside a type resolves to the member first and the top-level function otherwise, as Swift does. A bare call insideapply,run,withor abuildXlambda is not read as a member of the enclosing class, since its receiver is another object.An extension function, a call on a chained result (
items.first().load()) and an overload set that differs only by parameter types stay unresolved.Testing
On Windows 11 with Node 26:
test/graph-kotlin-members.test.ts(new, five tests): property,this.property, parameter, local, object, companion and implicit-this calls; an unknown member, an unbound receiver and a plain constructor parameter resolving to nothing; the shadowing cases; receiver lambdas; andthis@Outer. Four of the five fail on main; the second only asserts that nothing resolves.test/graph*,blast*,ask*,generic*,depth*,resolve*andcovers*suites: 677 tests, 1 fail. The one failure isdiscoverWorkspaceChildren follows a symlinked child repo, a symlink permission error on Windows that fails the same way on main.tsc --noEmitpasses.Closes #431