mirror of
https://github.com/abhigyanpatwari/GitNexus.git
synced 2026-09-20 00:11:37 +00:00
feat(python-scope): resolve dotted receivers via class-scope field types
Unit 4 partial — the dotted-receiver case (`user.address.save()`). Class-body annotations like `class User: address: Address` already land in the class scope's typeBindings via the existing `@type-binding.annotation` capture. This commit consumes that signal: - Build a `Map<classDefId, Scope>` from every parsed file's class scopes once per resolution pass. - New Case 0 in `emitReceiverBoundCalls`: when the receiver's name contains a dot, walk the chain — resolve the head's type, then for each remaining segment look up that field's type in the owner class's scope.typeBindings, then emit the call against the final class with MRO walk. - Cross-scope lookups use each TypeRef's `declaredAtScope` so an imported `Address` resolves in the file that owns the field declaration, not the file holding the call site. Verification: - Flag-off: 191/191 (identical baseline). - Flag-on: 29 fail / 162 pass (was 31/160; both `Field type resolution` fixtures now pass — same-file and cross-file disambig). - tsc --noEmit clean. Remaining Unit 4 work (write ACCESSES, `self.X` for-loop iteration) needs Unit 6's tuple/iterable destructuring before it can land — `for u in self.users` requires the iterable typing path.
This commit is contained in:
parent
f9ff390456
commit
c5221b98f5
1 changed files with 58 additions and 0 deletions
|
|
@ -338,6 +338,18 @@ function emitReceiverBoundCalls(
|
|||
}
|
||||
}
|
||||
|
||||
// Class def → Class scope map (for field-chain field-type lookup).
|
||||
// The class scope's `ownedDefs` contains the Class def per pass2's
|
||||
// structural-ownership rule.
|
||||
const classScopeByDefId = new Map<string, Scope>();
|
||||
for (const p of parsedFiles) {
|
||||
for (const scope of p.scopes) {
|
||||
if (scope.kind !== 'Class') continue;
|
||||
const cd = scope.ownedDefs.find((d) => d.type === 'Class');
|
||||
if (cd !== undefined) classScopeByDefId.set(cd.nodeId, scope);
|
||||
}
|
||||
}
|
||||
|
||||
for (const parsed of parsedFiles) {
|
||||
const namespaceTargets = collectNamespaceTargets(parsed, scopes);
|
||||
|
||||
|
|
@ -348,6 +360,52 @@ function emitReceiverBoundCalls(
|
|||
const receiverName = site.explicitReceiver.name;
|
||||
const memberName = site.name;
|
||||
|
||||
// ── Case 0: dotted receiver (`user.address.save()`) ──────────
|
||||
// Walk the dotted chain via class-scope typeBindings (fields).
|
||||
if (receiverName.includes('.')) {
|
||||
const parts = receiverName.split('.');
|
||||
const head = parts[0]!;
|
||||
const headType = findReceiverTypeBinding(site.inScope, head, scopes);
|
||||
let currentClass: SymbolDefinition | undefined = headType
|
||||
? findClassBindingInScope(headType.declaredAtScope, headType.rawName, scopes)
|
||||
: undefined;
|
||||
for (let i = 1; i < parts.length && currentClass !== undefined; i++) {
|
||||
const fieldName = parts[i]!;
|
||||
const cs = classScopeByDefId.get(currentClass.nodeId);
|
||||
const fieldType = cs?.typeBindings.get(fieldName);
|
||||
if (fieldType === undefined) {
|
||||
currentClass = undefined;
|
||||
break;
|
||||
}
|
||||
currentClass = findClassBindingInScope(
|
||||
fieldType.declaredAtScope,
|
||||
fieldType.rawName,
|
||||
scopes,
|
||||
);
|
||||
}
|
||||
if (currentClass !== undefined) {
|
||||
const chain = [currentClass.nodeId, ...scopes.methodDispatch.mroFor(currentClass.nodeId)];
|
||||
let memberDef: SymbolDefinition | undefined;
|
||||
for (const ownerId of chain) {
|
||||
memberDef = findOwnedMember(ownerId, memberName, parsedFiles);
|
||||
if (memberDef !== undefined) break;
|
||||
}
|
||||
if (memberDef !== undefined) {
|
||||
const ok = tryEmitEdge(
|
||||
graph,
|
||||
scopes,
|
||||
nodeLookup,
|
||||
site,
|
||||
memberDef,
|
||||
'python-scope: field-chain',
|
||||
seen,
|
||||
);
|
||||
if (ok) emitted++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ── Case 1: namespace receiver (`import models; models.X()`) ─
|
||||
const targetFile = namespaceTargets.get(receiverName);
|
||||
if (targetFile !== undefined) {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue