diff --git a/gitnexus/src/core/ingestion/languages/csharp.ts b/gitnexus/src/core/ingestion/languages/csharp.ts index 2e46a1944..837a7517b 100644 --- a/gitnexus/src/core/ingestion/languages/csharp.ts +++ b/gitnexus/src/core/ingestion/languages/csharp.ts @@ -159,7 +159,7 @@ export const csharpProvider = defineLanguage({ interpretTypeBinding: interpretCsharpTypeBinding, bindingScopeFor: csharpBindingScopeFor, importOwningScope: csharpImportOwningScope, - mergeBindings: csharpMergeBindings, + mergeBindings: (_scope, bindings) => csharpMergeBindings(bindings), receiverBinding: csharpReceiverBinding, arityCompatibility: csharpArityCompatibility, resolveImportTarget: resolveCsharpImportTarget, diff --git a/gitnexus/src/core/ingestion/languages/csharp/import-target.ts b/gitnexus/src/core/ingestion/languages/csharp/import-target.ts index 5079b23e2..8183bd2a4 100644 --- a/gitnexus/src/core/ingestion/languages/csharp/import-target.ts +++ b/gitnexus/src/core/ingestion/languages/csharp/import-target.ts @@ -22,13 +22,17 @@ import type { ParsedImport, WorkspaceIndex } from 'gitnexus-shared'; export interface CsharpResolveContext { readonly fromFile: string; - readonly allFilePaths: Set; + readonly allFilePaths: ReadonlySet; } export function resolveCsharpImportTarget( parsedImport: ParsedImport, workspaceIndex: WorkspaceIndex, ): string | null { + // WorkspaceIndex is `unknown` in the shared contract (Ring 1 + // placeholder). The scope-resolution orchestrator hands us a + // CsharpResolveContext-shaped object; narrow structurally rather + // than via a cast chain so unexpected shapes return null cleanly. const ctx = workspaceIndex as CsharpResolveContext | undefined; if ( ctx === undefined || diff --git a/gitnexus/src/core/ingestion/languages/csharp/merge-bindings.ts b/gitnexus/src/core/ingestion/languages/csharp/merge-bindings.ts index 671970424..dcd1a6f68 100644 --- a/gitnexus/src/core/ingestion/languages/csharp/merge-bindings.ts +++ b/gitnexus/src/core/ingestion/languages/csharp/merge-bindings.ts @@ -24,7 +24,7 @@ * earlier binding. */ -import type { BindingRef, Scope } from 'gitnexus-shared'; +import type { BindingRef } from 'gitnexus-shared'; const TIER_LOCAL = 0; const TIER_IMPORT = 1; @@ -46,10 +46,7 @@ function tierOf(b: BindingRef): number { } } -export function csharpMergeBindings( - _scope: Scope, - bindings: readonly BindingRef[], -): readonly BindingRef[] { +export function csharpMergeBindings(bindings: readonly BindingRef[]): readonly BindingRef[] { if (bindings.length === 0) return bindings; let bestTier = Number.POSITIVE_INFINITY; diff --git a/gitnexus/src/core/ingestion/languages/csharp/scope-resolver.ts b/gitnexus/src/core/ingestion/languages/csharp/scope-resolver.ts index af9553a4c..5fc4f9c63 100644 --- a/gitnexus/src/core/ingestion/languages/csharp/scope-resolver.ts +++ b/gitnexus/src/core/ingestion/languages/csharp/scope-resolver.ts @@ -6,7 +6,7 @@ * canonical shape. */ -import type { ParsedFile, Scope, WorkspaceIndex } from 'gitnexus-shared'; +import type { ParsedFile } from 'gitnexus-shared'; import { SupportedLanguages } from 'gitnexus-shared'; import { buildMro, defaultLinearize } from '../../scope-resolution/passes/mro.js'; import { populateClassOwnedMembers } from '../../scope-resolution/scope/walkers.js'; @@ -27,24 +27,19 @@ const csharpScopeResolver: ScopeResolver = { importEdgeReason: 'csharp-scope: using', resolveImportTarget: (targetRaw, fromFile, allFilePaths) => { - // CsharpResolveContext expects a mutable Set; the orchestrator - // hands us a ReadonlySet — safe to widen since the resolver only - // reads. - const ws: CsharpResolveContext = { - fromFile, - allFilePaths: allFilePaths as Set, - }; + const ws: CsharpResolveContext = { fromFile, allFilePaths }; + // `WorkspaceIndex` is an opaque `unknown` placeholder in the + // shared contract, so `ws` passes structurally without a cast. return resolveCsharpImportTarget( { kind: 'namespace', localName: '_', importedName: '_', targetRaw }, - ws as unknown as WorkspaceIndex, + ws, ); }, - // C# shadowing: local > using > using static. - mergeBindings: (existing, incoming, scopeId) => { - const fakeScope = { id: scopeId } as unknown as Scope; - return [...csharpMergeBindings(fakeScope, [...existing, ...incoming])]; - }, + // C# shadowing: local > using > using static. The per-scope id is + // unused by the C# implementation (shadowing is computed purely + // from the binding tier), so we don't need to synthesize a Scope. + mergeBindings: (existing, incoming) => [...csharpMergeBindings([...existing, ...incoming])], // Adapter: csharpArityCompatibility uses (def, callsite); the // contract is (callsite, def). diff --git a/gitnexus/test/unit/scope-resolution/csharp/csharp-hooks.test.ts b/gitnexus/test/unit/scope-resolution/csharp/csharp-hooks.test.ts index 92075895c..5e37e04d2 100644 --- a/gitnexus/test/unit/scope-resolution/csharp/csharp-hooks.test.ts +++ b/gitnexus/test/unit/scope-resolution/csharp/csharp-hooks.test.ts @@ -85,7 +85,6 @@ describe('csharpImportOwningScope', () => { }); describe('csharpMergeBindings — shadowing precedence', () => { - const scope = fakeScope('Function'); const def = (nodeId: string): SymbolDefinition => ({ nodeId, filePath: 't.cs', type: 'Function' }) as SymbolDefinition; const binding = (origin: BindingRef['origin'], nodeId: string): BindingRef => @@ -94,43 +93,43 @@ describe('csharpMergeBindings — shadowing precedence', () => { it('local declaration shadows `using` import', () => { const local = binding('local', 'L'); const imp = binding('import', 'I'); - expect(csharpMergeBindings(scope, [imp, local])).toEqual([local]); + expect(csharpMergeBindings([imp, local])).toEqual([local]); }); it('explicit `using` shadows `using static` (wildcard)', () => { const imp = binding('import', 'I'); const wc = binding('wildcard', 'W'); - expect(csharpMergeBindings(scope, [wc, imp])).toEqual([imp]); + expect(csharpMergeBindings([wc, imp])).toEqual([imp]); }); it('local shadows both `using` and `using static`', () => { const local = binding('local', 'L'); const imp = binding('import', 'I'); const wc = binding('wildcard', 'W'); - expect(csharpMergeBindings(scope, [wc, imp, local])).toEqual([local]); + expect(csharpMergeBindings([wc, imp, local])).toEqual([local]); }); it('keeps overload siblings at the same tier', () => { const a = binding('local', 'A'); const b = binding('local', 'B'); - expect(csharpMergeBindings(scope, [a, b])).toEqual([a, b]); + expect(csharpMergeBindings([a, b])).toEqual([a, b]); }); it('dedupes same-nodeId bindings', () => { const a = binding('local', 'A'); const a2 = binding('local', 'A'); - expect(csharpMergeBindings(scope, [a, a2])).toHaveLength(1); + expect(csharpMergeBindings([a, a2])).toHaveLength(1); }); it('namespace and reexport tie with explicit import (same tier)', () => { const ns = binding('namespace', 'N'); const re = binding('reexport', 'R'); const imp = binding('import', 'I'); - expect(csharpMergeBindings(scope, [ns, re, imp])).toHaveLength(3); + expect(csharpMergeBindings([ns, re, imp])).toHaveLength(3); }); it('empty in → empty out', () => { - expect(csharpMergeBindings(scope, [])).toEqual([]); + expect(csharpMergeBindings([])).toEqual([]); }); });