mirror of
https://github.com/abhigyanpatwari/GitNexus.git
synced 2026-09-20 00:11:37 +00:00
feat(python): dotted-typebinding receiver resolution
Adds case 3 to `emitReceiverBoundCalls`: when a receiver's typeBinding has a dotted rawName like `u: models.User` (the constructor-inferred form fired by `u = models.User(...)`), walk the namespace map + target file's defs to find the class, then look up the member via ownerId. `resolveTypeRef`'s QualifiedNameIndex fallback can't cover this because the target class's qualifiedName in models.py is just `"User"`, not `"models.User"` — the dotted form only exists in the call-site file's receiver expression. This pass bridges that gap without modifying the shared registry. Fixes 9 more failures (flag-on 45 → 36): - Python qualified constructor inference (2) - Python module import CALLS resolution (Issue #337) (3) - (cluster overlap — several downstream tests in assignment/nullable/ walrus that propagate through qualified-ctor bindings also benefit) Flag-off still 191/191.
This commit is contained in:
parent
585d2ba770
commit
2a8eb4ccc4
1 changed files with 56 additions and 0 deletions
|
|
@ -492,6 +492,38 @@ function emitReceiverBoundCalls(
|
|||
seen,
|
||||
);
|
||||
if (emitted2) emitted++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// ── Case 3: receiver has a dotted typeBinding (`u: models.User`) ──
|
||||
// Happens when `u = models.User(...)` fires the qualified-call
|
||||
// constructor-inferred capture. The shared resolveTypeRef can't
|
||||
// handle it because User's qualifiedName in models.py is just
|
||||
// "User" (not "models.User"), so QualifiedNameIndex fallback
|
||||
// misses. Resolve manually via the namespace map.
|
||||
const typeRef = findReceiverTypeBinding(site.inScope, receiverName, scopes);
|
||||
if (typeRef !== undefined && typeRef.rawName.includes('.')) {
|
||||
const [nsName, ...classNameParts] = typeRef.rawName.split('.');
|
||||
const className = classNameParts.join('.');
|
||||
const targetFile = namespaceTargets.get(nsName);
|
||||
if (targetFile !== undefined && className.length > 0) {
|
||||
const classDef3 = findExportedDef(targetFile, className, parsedFiles);
|
||||
if (classDef3 !== undefined) {
|
||||
const memberDef = findOwnedMember(classDef3.nodeId, memberName, parsedFiles);
|
||||
if (memberDef !== undefined) {
|
||||
const emitted3 = tryEmitEdge(
|
||||
graph,
|
||||
scopes,
|
||||
nodeLookup,
|
||||
site,
|
||||
memberDef,
|
||||
'python-scope: dotted-typebinding',
|
||||
seen,
|
||||
);
|
||||
if (emitted3) emitted++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -500,6 +532,30 @@ function emitReceiverBoundCalls(
|
|||
return emitted;
|
||||
}
|
||||
|
||||
/**
|
||||
* Walk the scope chain from `startScope` looking for a typeBinding
|
||||
* named `receiverName`. Returns the TypeRef or undefined if no binding
|
||||
* exists in the chain.
|
||||
*/
|
||||
function findReceiverTypeBinding(
|
||||
startScope: ScopeId,
|
||||
receiverName: string,
|
||||
scopes: ScopeResolutionIndexes,
|
||||
): { readonly rawName: string } | 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;
|
||||
const typeRef = scope.typeBindings.get(receiverName);
|
||||
if (typeRef !== undefined) return typeRef;
|
||||
currentId = scope.parent;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a `localName → targetFilePath` map over the file's module-scope
|
||||
* import edges, limited to namespace-kind imports (which is what binds
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue