feat(resolution): inherited this.X, Java/Kotlin cross-file method refs, Swift type scoping (#810)
Three callback-registration shapes deferred from #756/#808, one arc: 1. INHERITED this.X (TS/JS + every this.-routed language): a `this.<member>` registration whose member isn't on the enclosing class defers to a second pass (resolveDeferredThisMemberRefs — in-memory like deferredChainRefs, runs after implements/extends edges persist, same lifecycle as the #750 conformance pass) and resolves up the supertype chain, depth-capped BFS, validated targets only. `bus.on("submit", this.handleSubmit)` in a subclass links to FormBase::handleSubmit; same-named methods on unrelated classes never match. this.-prefixed candidates skip the extraction name gate (an inherited member can't be in definedHere). 2. JAVA/KOTLIN qualified method refs: `Handlers::onMessage` / `OtherClass::handle` emit QUALIFIED names resolved by the scoped suffix-matcher — cross-file capable, gated on the scope name being a same-file type or an imported name (dotted JVM imports now contribute their last segment). `this::m` and `super::m` route through the class-scoped resolver (super rides the supertype pass). References through a VARIABLE (`subscriber::onNext`) deliberately produce nothing — receiver type is unknowable; RxJava's baseline bare capture was resolving these to same-named same-file methods (a test method "registering" an anonymous class's onNext) — the rework drops 18 such wrong edges and keeps the 7 genuine Type::method refs RxJava's main tree actually has. 3. SWIFT enclosing-type scoping (implicit self): bare callback names match methods only of the from-symbol's own type (extension/nested scopes reconciled by suffix), and top-level code never matches methods. Alamofire: −44 wrong edges (parameters like `request`/`data`/`retrier` resolving to same-named methods on unrelated protocols), all verified; the same-class param collision (`task`) remains and is documented. New ResolutionContext.getNodeById lets matchers derive the from-symbol's class scope. Controls: redis/fmt fnref edges byte-identical; excalidraw stable; typeorm +4 genuine inherited-getter dependencies; zero calls edges changed on any of 7 A/B repos; nodes identical everywhere. Kotlin companion-object members extract unqualified (pre-existing) so `Type::companionFn` stays silent rather than guessing — documented. Full suite 1389 passed. EXTRACTION_VERSION 20 → 21 (re-index to benefit). Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
38eb4e688c
commit
38095aa95b
@@ -227,7 +227,7 @@ export function matchFunctionRef(
|
||||
};
|
||||
}
|
||||
|
||||
const candidates = context
|
||||
let candidates = context
|
||||
.getNodesByName(ref.referenceName)
|
||||
.filter(
|
||||
(n) =>
|
||||
@@ -237,6 +237,35 @@ export function matchFunctionRef(
|
||||
);
|
||||
if (candidates.length === 0) return null;
|
||||
|
||||
// Swift implicit-self: a bare identifier can name a METHOD only of the
|
||||
// ENCLOSING type (`Button(action: handleTap)` written inside that type) —
|
||||
// a same-named method on any OTHER class is a parameter collision
|
||||
// (Alamofire: a `request` parameter resolving to EventMonitor::request).
|
||||
// Scope method candidates to the from-symbol's type; top-level code has no
|
||||
// implicit self, so method targets are excluded there entirely. Free
|
||||
// functions are unaffected.
|
||||
if (ref.language === 'swift' && candidates.some((n) => n.kind === 'method')) {
|
||||
const fromNode = context.getNodeById?.(ref.fromNodeId);
|
||||
const sep = fromNode ? fromNode.qualifiedName.lastIndexOf('::') : -1;
|
||||
const classPrefix = fromNode && sep > 0 ? fromNode.qualifiedName.slice(0, sep) : null;
|
||||
candidates = candidates.filter((n) => {
|
||||
if (n.kind !== 'method') return true;
|
||||
if (!classPrefix) return false;
|
||||
const mSep = n.qualifiedName.lastIndexOf('::');
|
||||
if (mSep <= 0) return false;
|
||||
const methodPrefix = n.qualifiedName.slice(0, mSep);
|
||||
// Accept exact-scope matches plus suffix relationships either way, so
|
||||
// extension-declared members (`Holder::m`) still match a nested
|
||||
// from-scope (`Module::Holder::wire`) and vice versa.
|
||||
return (
|
||||
methodPrefix === classPrefix ||
|
||||
methodPrefix.endsWith(`::${classPrefix}`) ||
|
||||
classPrefix.endsWith(`::${methodPrefix}`)
|
||||
);
|
||||
});
|
||||
if (candidates.length === 0) return null;
|
||||
}
|
||||
|
||||
// Same-file definition wins — the extraction gate guarantees most survivors
|
||||
// have one, and it's the dominant C pattern (static callback registered in
|
||||
// a same-file ops struct).
|
||||
|
||||
Reference in New Issue
Block a user