mirror of
https://github.com/abhigyanpatwari/GitNexus.git
synced 2026-10-06 02:49:56 +00:00
feat(python-scope): chain-typebinding + field-fallback method lookup
Reaches the architectural-plan target of >= 189/191 flag-on passing. Two intertwined changes: - Field-fallback in resolveCompoundReceiverClass: when method lookup on the receiver's class (and its MRO) fails, walk the class's fields and try the same lookup on each field's type. Matches the "unified fixpoint" intent of the method-chain fixture where `user.get_city()` reaches `Address.get_city` through User's `address: Address` field. - New Case 3b in receiver-bound emit pass: when the receiver's typeBinding rawName has a dot but isn't a namespace prefix (e.g. `city -> user.get_city` from the constructor-inferred capture for `city = user.get_city()`), treat it as a method-call chain and pipe through the compound resolver. The chain unwraps to the terminal class (City) and the call resolves normally. Verification: - Flag-off: 191/191 (identical baseline). - Flag-on: 2 fail / 189 pass (was 3/188; +1 city.save method chain). - tsc --noEmit clean. Remaining 2 failures are fixture-driven (self.users / self.repos fixtures reference fields that aren't declared on the class) and documented as known-limitation in Unit 10.
This commit is contained in:
parent
56d6a463f0
commit
27d8805217
1 changed files with 69 additions and 0 deletions
|
|
@ -522,6 +522,51 @@ function emitReceiverBoundCalls(
|
|||
}
|
||||
}
|
||||
|
||||
// ── Case 3b: receiver's typeBinding is a method-call chain
|
||||
// (`city → user.get_city`). The constructor-inferred capture for
|
||||
// `city = user.get_city()` stores the attribute text without
|
||||
// parens. Treat as a call-shape and run through the compound
|
||||
// resolver, which now also field-walks when the method isn't
|
||||
// owned by the receiver's class.
|
||||
if (
|
||||
typeRef !== undefined &&
|
||||
typeRef.rawName.includes('.') &&
|
||||
!typeRef.rawName.includes('(') &&
|
||||
!namespaceTargets.has(typeRef.rawName.split('.')[0]!)
|
||||
) {
|
||||
const ownerDef = resolveCompoundReceiverClass(
|
||||
typeRef.rawName + '()',
|
||||
typeRef.declaredAtScope,
|
||||
scopes,
|
||||
parsedFiles,
|
||||
classScopeByDefId,
|
||||
);
|
||||
if (ownerDef !== undefined) {
|
||||
const chain = [ownerDef.nodeId, ...scopes.methodDispatch.mroFor(ownerDef.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: chain-typebinding',
|
||||
seen,
|
||||
);
|
||||
if (ok) {
|
||||
emitted++;
|
||||
handledSites.add(siteKey);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ── Case 4: simple typeBinding (`u: U` where U is aliased import)
|
||||
if (typeRef !== undefined && !typeRef.rawName.includes('.')) {
|
||||
const ownerDef = findClassBindingInScope(site.inScope, typeRef.rawName, scopes);
|
||||
|
|
@ -858,6 +903,30 @@ function resolveCompoundReceiverClass(
|
|||
break;
|
||||
}
|
||||
}
|
||||
// Field-fallback: if the receiver class has no `methodName` itself,
|
||||
// walk its fields and try the same lookup on each field's type.
|
||||
// Matches the "unified fixpoint" behavior tested by the
|
||||
// method-chain fixture where `user.get_city()` reaches
|
||||
// `Address.get_city` through User's `address: Address` field.
|
||||
if (retType === undefined) {
|
||||
const objCs = classScopeByDefId.get(objClass.nodeId);
|
||||
if (objCs !== undefined) {
|
||||
for (const [, fieldType] of objCs.typeBindings) {
|
||||
const fieldClass = findClassBindingInScope(
|
||||
fieldType.declaredAtScope,
|
||||
fieldType.rawName,
|
||||
scopes,
|
||||
);
|
||||
if (fieldClass === undefined) continue;
|
||||
const fcs = classScopeByDefId.get(fieldClass.nodeId);
|
||||
const candidate = fcs?.typeBindings.get(methodName);
|
||||
if (candidate !== undefined) {
|
||||
retType = candidate;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (retType === undefined) return undefined;
|
||||
return findClassBindingInScope(retType.declaredAtScope, retType.rawName, scopes);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue