mirror of
https://github.com/abhigyanpatwari/GitNexus.git
synced 2026-10-04 02:31:36 +00:00
refactor(scope-resolution): gate module-scope typeBinding walk-up on hook
Add optional `hoistTypeBindingsToModule` to the ScopeResolver contract and gate the Module-scope walk-up in `resolveCompoundReceiverClass` on it. Only providers that hoist method return-type bindings to Module scope (C#) opt in; Python and other providers no longer traverse that fallback path. Closes the architectural leak flagged in the production-readiness review: the walk-up was unconditional and therefore widened Python's code path despite existing only for C#. No behavior change for C# (hook=true restores the prior lookup). No behavior change for Python (hook undefined = walk-up skipped, matching pre-PR behavior). Verified: - npx tsc --noEmit clean - C# unit suite 74/74 passing - C# + Python integration 388/388 passing
This commit is contained in:
parent
70691378ab
commit
0f18456539
4 changed files with 47 additions and 9 deletions
|
|
@ -82,6 +82,12 @@ const csharpScopeResolver: ScopeResolver = {
|
|||
// `(caller, target)` — multiple `g.Greet(...)` sites from Main
|
||||
// yield ONE edge, not one per site.
|
||||
collapseMemberCallsByCallerTarget: true,
|
||||
|
||||
// C# hoists method return-type bindings to the enclosing Module
|
||||
// scope so `propagateImportedReturnTypes` can mirror them across
|
||||
// files. The compound-receiver walker needs to walk up from the
|
||||
// class scope to find them; see the contract field for rationale.
|
||||
hoistTypeBindingsToModule: true,
|
||||
};
|
||||
|
||||
export { csharpScopeResolver };
|
||||
|
|
|
|||
|
|
@ -257,4 +257,22 @@ export interface ScopeResolver {
|
|||
indexes: ScopeResolutionIndexes,
|
||||
ctx: { readonly fileContents: ReadonlyMap<string, string> },
|
||||
) => void;
|
||||
|
||||
/**
|
||||
* Whether the compound-receiver resolver should walk up from a
|
||||
* class scope to ancestor (Module) scopes when looking up a
|
||||
* method's return-type typeBinding. Default `false`.
|
||||
*
|
||||
* Set `true` when a language stores method return-type bindings at
|
||||
* Module scope (rather than on each class) so cross-file return-
|
||||
* type propagation can mirror them. Without this walk-up, chain
|
||||
* resolution fails for methods whose return types were hoisted to
|
||||
* module scope.
|
||||
*
|
||||
* Languages that attach return-type bindings directly to the class
|
||||
* scope leave this undefined — enabling the walk-up for them would
|
||||
* add an unnecessary branch and risk picking up unrelated module-
|
||||
* level bindings.
|
||||
*/
|
||||
readonly hoistTypeBindingsToModule?: boolean;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -47,6 +47,11 @@ interface ResolveCompoundReceiverOptions {
|
|||
receiverType: string,
|
||||
accessor: string,
|
||||
) => string | undefined;
|
||||
/** Walk up from the class scope to ancestor (Module) scopes when
|
||||
* looking up a method's return-type typeBinding. Only enable for
|
||||
* languages that hoist return-type bindings to Module scope (C#);
|
||||
* otherwise we risk picking up unrelated module-level bindings. */
|
||||
readonly hoistTypeBindingsToModule?: boolean;
|
||||
}
|
||||
|
||||
export function resolveCompoundReceiverClass(
|
||||
|
|
@ -116,12 +121,14 @@ export function resolveCompoundReceiverClass(
|
|||
break;
|
||||
}
|
||||
// Fallback: walk up from the class scope looking for a return-
|
||||
// type binding on an ancestor (Module) scope. Some languages
|
||||
// (C#) hoist method return-type bindings to Module scope so
|
||||
// `propagateImportedReturnTypes` can mirror them cross-file;
|
||||
// this loop restores the owner-chain lookup path for those
|
||||
// languages without forcing a class-scope copy.
|
||||
if (cs !== undefined) {
|
||||
// type binding on an ancestor (Module) scope. Gated on
|
||||
// `hoistTypeBindingsToModule` because only languages that hoist
|
||||
// method return-type bindings to Module scope need this path;
|
||||
// enabling it unconditionally would let other languages pick up
|
||||
// unrelated module-level bindings. See contract doc for the
|
||||
// invariant and `propagateImportedReturnTypes` for how the
|
||||
// hoisted bindings originate.
|
||||
if (cs !== undefined && options.hoistTypeBindingsToModule === true) {
|
||||
let curId: ScopeId | null = cs.parent;
|
||||
while (curId !== null) {
|
||||
const curScope = scopes.scopeTree.getScope(curId);
|
||||
|
|
|
|||
|
|
@ -56,6 +56,7 @@ type ReceiverBoundProviderSubset = Pick<
|
|||
| 'fieldFallbackOnMethodLookup'
|
||||
| 'collapseMemberCallsByCallerTarget'
|
||||
| 'unwrapCollectionAccessor'
|
||||
| 'hoistTypeBindingsToModule'
|
||||
>;
|
||||
|
||||
export function emitReceiverBoundCalls(
|
||||
|
|
@ -74,6 +75,12 @@ export function emitReceiverBoundCalls(
|
|||
const seen = new Set<string>();
|
||||
const fieldFallback = provider.fieldFallbackOnMethodLookup ?? true;
|
||||
const collapse = provider.collapseMemberCallsByCallerTarget === true;
|
||||
const hoistTypeBindingsToModule = provider.hoistTypeBindingsToModule === true;
|
||||
const compoundOpts = {
|
||||
fieldFallback,
|
||||
unwrapCollectionAccessor: provider.unwrapCollectionAccessor,
|
||||
hoistTypeBindingsToModule,
|
||||
};
|
||||
|
||||
// Build an interface → implementors map from IMPLEMENTS edges.
|
||||
// Maps Interface graph-id → list of implementor class scope-def-ids.
|
||||
|
|
@ -187,7 +194,7 @@ export function emitReceiverBoundCalls(
|
|||
site.inScope,
|
||||
scopes,
|
||||
index,
|
||||
{ fieldFallback, unwrapCollectionAccessor: provider.unwrapCollectionAccessor },
|
||||
compoundOpts,
|
||||
);
|
||||
if (currentClass !== undefined) {
|
||||
const chain = [currentClass.nodeId, ...scopes.methodDispatch.mroFor(currentClass.nodeId)];
|
||||
|
|
@ -314,7 +321,7 @@ export function emitReceiverBoundCalls(
|
|||
typeRef.declaredAtScope,
|
||||
scopes,
|
||||
index,
|
||||
{ fieldFallback, unwrapCollectionAccessor: provider.unwrapCollectionAccessor },
|
||||
compoundOpts,
|
||||
);
|
||||
if (ownerDef === undefined) {
|
||||
ownerDef = resolveCompoundReceiverClass(
|
||||
|
|
@ -322,7 +329,7 @@ export function emitReceiverBoundCalls(
|
|||
typeRef.declaredAtScope,
|
||||
scopes,
|
||||
index,
|
||||
{ fieldFallback, unwrapCollectionAccessor: provider.unwrapCollectionAccessor },
|
||||
compoundOpts,
|
||||
);
|
||||
}
|
||||
if (ownerDef !== undefined) {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue