mirror of
https://github.com/abhigyanpatwari/GitNexus.git
synced 2026-09-20 00:11:37 +00:00
feat(python-scope): super() receiver dispatches up the MRO
Unit 8 — `super().method()` inside a class method walks the enclosing class's MRO chain (skipping self) and resolves to the first ancestor that owns the method. New receiver branch in `emitReceiverBoundCalls` recognizes `super(...)` syntactically (regex-cheap), finds the enclosing class via a new `findEnclosingClassDef` scope-walk helper, then re-uses `scopes.methodDispatch.mroFor` + `findOwnedMember` from the existing class-receiver path. Handled before the compound-receiver case so `super()` doesn't fall into the bare-identifier branch where `super` isn't a binding. Verification: - Flag-off: 191/191 (identical baseline). - Flag-on: 21 fail / 170 pass (was 22/169; `super().save() inside User to BaseModel.save` now passes). - tsc --noEmit clean.
This commit is contained in:
parent
eb897540f3
commit
a1382d3cf7
1 changed files with 50 additions and 0 deletions
|
|
@ -375,6 +375,33 @@ function emitReceiverBoundCalls(
|
|||
const receiverName = site.explicitReceiver.name;
|
||||
const memberName = site.name;
|
||||
|
||||
// ── super() — resolve to the enclosing class's PARENT (first MRO entry).
|
||||
// Python's `super()` inside a method dispatches up the MRO chain.
|
||||
if (/^super\s*\(/.test(receiverName)) {
|
||||
const enclosingClass = findEnclosingClassDef(site.inScope, scopes);
|
||||
if (enclosingClass !== undefined) {
|
||||
const ancestors = scopes.methodDispatch.mroFor(enclosingClass.nodeId);
|
||||
let memberDef: SymbolDefinition | undefined;
|
||||
for (const ownerId of ancestors) {
|
||||
memberDef = findOwnedMember(ownerId, memberName, parsedFiles);
|
||||
if (memberDef !== undefined) break;
|
||||
}
|
||||
if (memberDef !== undefined) {
|
||||
const ok = tryEmitEdge(
|
||||
graph,
|
||||
scopes,
|
||||
nodeLookup,
|
||||
site,
|
||||
memberDef,
|
||||
'python-scope: super-receiver',
|
||||
seen,
|
||||
);
|
||||
if (ok) emitted++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ── Case 0: compound receiver (`user.address.save()` or
|
||||
// `svc.get_user().save()`) — walk the dotted/call chain,
|
||||
// resolving each segment to a class via field types or
|
||||
|
|
@ -575,6 +602,29 @@ function emitFreeCallFallback(
|
|||
return emitted;
|
||||
}
|
||||
|
||||
/** Walk a scope chain upward looking for the innermost enclosing
|
||||
* Class scope and return that class's def. Used by the `super()`
|
||||
* receiver case to discover the dispatch base. */
|
||||
function findEnclosingClassDef(
|
||||
startScope: ScopeId,
|
||||
scopes: ScopeResolutionIndexes,
|
||||
): SymbolDefinition | undefined {
|
||||
let currentId: ScopeId | null = startScope;
|
||||
const visited = new Set<ScopeId>();
|
||||
while (currentId !== null) {
|
||||
if (visited.has(currentId)) return undefined;
|
||||
visited.add(currentId);
|
||||
const scope = scopes.scopeTree.getScope(currentId);
|
||||
if (scope === undefined) return undefined;
|
||||
if (scope.kind === 'Class') {
|
||||
const cd = scope.ownedDefs.find((d) => d.type === 'Class');
|
||||
if (cd !== undefined) return cd;
|
||||
}
|
||||
currentId = scope.parent;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/** Max depth for compound-receiver chain resolution (`a().b().c().d()`).
|
||||
* Practical Python rarely exceeds 3-4 hops; the cap just prevents
|
||||
* pathological recursion if the receiver text turns out to be malformed. */
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue