mirror of
https://github.com/abhigyanpatwari/GitNexus.git
synced 2026-09-21 00:21:30 +00:00
feat(csharp-scope): Unit 6 — wire csharpScopeResolver + register
Creates the public barrel (index.ts) and ScopeResolver (scope-resolver.ts)
and plumbs them into the provider + registry:
- `languages/csharp/index.ts` — re-exports the hook entry points and
documents the 8 known limitations of the registry-primary path
(csproj-driven namespace resolution, multi-file namespace expansion,
type-based overload resolution, nested generics, dynamic, preprocessor
branches, cross-file global using, expression-bodied members).
- `languages/csharp/scope-resolver.ts` — ScopeResolver shape mirroring
Python's. `isSuperReceiver` matches the literal `base` keyword.
`fieldFallbackOnMethodLookup: false` since C# is statically typed
— the type-binding layer already produces precise owner types;
`propagatesReturnTypesAcrossImports: true` since signatures are
authoritative.
- `languages/csharp.ts` — adds the 9 hook entry points to the provider
(emitScopeCaptures, interpretImport, interpretTypeBinding, four
simple hooks, mergeBindings, arityCompatibility, resolveImportTarget).
- `scope-resolution/pipeline/registry.ts` — registers csharpScopeResolver
alongside the Python entry.
MIGRATED_LANGUAGES stays at {Python} — the resolver sits idle until
Unit 7's parity gate confirms ≥99% fixture parity. 368/368
scope-resolution unit tests pass; tsc clean.
This commit is contained in:
parent
ac8b55ba2c
commit
0e352ac669
4 changed files with 172 additions and 1 deletions
|
|
@ -25,6 +25,17 @@ import { csharpMethodConfig } from '../method-extractors/configs/csharp.js';
|
|||
import { createVariableExtractor } from '../variable-extractors/generic.js';
|
||||
import { csharpVariableConfig } from '../variable-extractors/configs/csharp.js';
|
||||
import { createHeritageExtractor } from '../heritage-extractors/generic.js';
|
||||
import {
|
||||
emitCsharpScopeCaptures,
|
||||
interpretCsharpImport,
|
||||
interpretCsharpTypeBinding,
|
||||
csharpBindingScopeFor,
|
||||
csharpImportOwningScope,
|
||||
csharpMergeBindings,
|
||||
csharpReceiverBinding,
|
||||
csharpArityCompatibility,
|
||||
resolveCsharpImportTarget,
|
||||
} from './csharp/index.js';
|
||||
|
||||
const BUILT_INS: ReadonlySet<string> = new Set([
|
||||
'Console',
|
||||
|
|
@ -138,4 +149,18 @@ export const csharpProvider = defineLanguage({
|
|||
classExtractor: createClassExtractor(csharpClassConfig),
|
||||
heritageExtractor: createHeritageExtractor(SupportedLanguages.CSharp),
|
||||
builtInNames: BUILT_INS,
|
||||
|
||||
// ── RFC #909 Ring 3: scope-based resolution hooks (RFC §5) ──────────
|
||||
// C# is the second migration after Python. See ./csharp/index.ts for
|
||||
// the full per-hook rationale and the canonical capture vocabulary
|
||||
// in ./csharp/query.ts (CSHARP_SCOPE_QUERY constant).
|
||||
emitScopeCaptures: emitCsharpScopeCaptures,
|
||||
interpretImport: interpretCsharpImport,
|
||||
interpretTypeBinding: interpretCsharpTypeBinding,
|
||||
bindingScopeFor: csharpBindingScopeFor,
|
||||
importOwningScope: csharpImportOwningScope,
|
||||
mergeBindings: csharpMergeBindings,
|
||||
receiverBinding: csharpReceiverBinding,
|
||||
arityCompatibility: csharpArityCompatibility,
|
||||
resolveImportTarget: resolveCsharpImportTarget,
|
||||
});
|
||||
|
|
|
|||
73
gitnexus/src/core/ingestion/languages/csharp/index.ts
Normal file
73
gitnexus/src/core/ingestion/languages/csharp/index.ts
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
/**
|
||||
* C# scope-resolution hooks (RFC #909 Ring 3, RFC §5).
|
||||
*
|
||||
* Public API barrel. Consumers should import from this file rather than
|
||||
* the individual modules.
|
||||
*
|
||||
* Module layout (each file is a single concern):
|
||||
*
|
||||
* - `query.ts` — tree-sitter query + lazy parser/query singletons
|
||||
* - `captures.ts` — `emitCsharpScopeCaptures` orchestrator
|
||||
* - `import-decomposer.ts` — each `using` → ParsedImport-shaped captures
|
||||
* - `interpret.ts` — capture-match → `ParsedImport` / `ParsedTypeBinding`
|
||||
* - `simple-hooks.ts` — small/no-op hooks made explicit
|
||||
* - `merge-bindings.ts` — C# `using` precedence
|
||||
* - `arity.ts` — C# arity compatibility (`params`, default values)
|
||||
* - `arity-metadata.ts` — synthesize arity metadata from declarations
|
||||
* - `import-target.ts` — `(ParsedImport, WorkspaceIndex) → file path` adapter
|
||||
* - `scope-resolver.ts` — `ScopeResolver` registered in `SCOPE_RESOLVERS`
|
||||
* - `cache-stats.ts` — PROF_SCOPE_RESOLUTION cache hit/miss counters
|
||||
*
|
||||
* ## Known limitations
|
||||
*
|
||||
* The C# registry-primary path intentionally does NOT resolve the
|
||||
* following. Each is a conscious trade-off at migration time.
|
||||
*
|
||||
* 1. **csproj-driven namespace resolution** — the legacy path
|
||||
* consults `csharpConfigs` (the parsed .csproj workspace) to map
|
||||
* `using X.Y;` back to the exact files declaring `namespace X.Y`.
|
||||
* The scope-resolver contract passes only `allFilePaths`, so we
|
||||
* fall back to suffix matching on `.cs` files. Unit 7's parity
|
||||
* gate flags any divergence.
|
||||
* 2. **Multi-file namespace expansion** — a single `using X.Y;` in
|
||||
* the legacy path can emit multiple IMPORTS edges (every file
|
||||
* declaring that namespace). The scope-resolver contract returns
|
||||
* a single target, so we pick the first match; partial-class
|
||||
* aggregation runs at graph-bridge time.
|
||||
* 3. **Overload resolution by parameter type** — arity narrowing is
|
||||
* wired (`arity.ts` + `arity-metadata.ts`), but type-based
|
||||
* disambiguation (`F(int)` vs `F(string)` at a call with a typed
|
||||
* argument) is left to the registry's type-binding layer.
|
||||
* 4. **Generic type parameter resolution** — `List<User>` binds the
|
||||
* bound name to `User` via the single-arg-generic stripper;
|
||||
* nested generics (`Dictionary<K, List<V>>`) fall through the
|
||||
* receiver-type heuristic.
|
||||
* 5. **`dynamic` typed expressions** — runtime dispatch through
|
||||
* `dynamic` is not followed.
|
||||
* 6. **Preprocessor-conditional code** — `#if DEBUG` blocks parse
|
||||
* as usual; branch selection is ignored, so both arms contribute
|
||||
* bindings.
|
||||
* 7. **Global using propagation across files** — treated as a
|
||||
* file-scoped using for the declaring file. Unit 7 parity gate
|
||||
* will flag cases where this matters.
|
||||
* 8. **Expression-bodied `=>` members** — handled by the method
|
||||
* extractor, but receiver synthesis for `=> this.Field` shortcuts
|
||||
* follows the same path as block-bodied methods.
|
||||
*
|
||||
* Shadow-harness corpus parity is the authoritative signal for which
|
||||
* of these matter in practice. The CI parity gate blocks any PR that
|
||||
* regresses either the legacy or registry-primary run of
|
||||
* `test/integration/resolvers/csharp.test.ts`.
|
||||
*/
|
||||
|
||||
export { emitCsharpScopeCaptures } from './captures.js';
|
||||
export { getCsharpCaptureCacheStats, resetCsharpCaptureCacheStats } from './cache-stats.js';
|
||||
export { interpretCsharpImport, interpretCsharpTypeBinding } from './interpret.js';
|
||||
export { csharpMergeBindings } from './merge-bindings.js';
|
||||
export { csharpArityCompatibility } from './arity.js';
|
||||
export { resolveCsharpImportTarget, type CsharpResolveContext } from './import-target.js';
|
||||
export {
|
||||
csharpBindingScopeFor,
|
||||
csharpImportOwningScope,
|
||||
csharpReceiverBinding,
|
||||
} from './simple-hooks.js';
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
/**
|
||||
* C# `ScopeResolver` registered in `SCOPE_RESOLVERS` and consumed by
|
||||
* the generic `runScopeResolution` orchestrator (RFC #909 Ring 3).
|
||||
*
|
||||
* Second migration after Python — see `pythonScopeResolver` for the
|
||||
* canonical shape.
|
||||
*/
|
||||
|
||||
import type { ParsedFile, Scope, WorkspaceIndex } 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';
|
||||
import type { ScopeResolver } from '../../scope-resolution/contract/scope-resolver.js';
|
||||
import { csharpProvider } from '../csharp.js';
|
||||
import {
|
||||
csharpArityCompatibility,
|
||||
csharpMergeBindings,
|
||||
resolveCsharpImportTarget,
|
||||
type CsharpResolveContext,
|
||||
} from './index.js';
|
||||
|
||||
const csharpScopeResolver: ScopeResolver = {
|
||||
language: SupportedLanguages.CSharp,
|
||||
languageProvider: csharpProvider,
|
||||
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<string>,
|
||||
};
|
||||
return resolveCsharpImportTarget(
|
||||
{ kind: 'namespace', localName: '_', importedName: '_', targetRaw },
|
||||
ws as unknown as WorkspaceIndex,
|
||||
);
|
||||
},
|
||||
|
||||
// C# shadowing: local > using > using static.
|
||||
mergeBindings: (existing, incoming, scopeId) => {
|
||||
const fakeScope = { id: scopeId } as unknown as Scope;
|
||||
return [...csharpMergeBindings(fakeScope, [...existing, ...incoming])];
|
||||
},
|
||||
|
||||
// Adapter: csharpArityCompatibility uses (def, callsite); the
|
||||
// contract is (callsite, def).
|
||||
arityCompatibility: (callsite, def) => csharpArityCompatibility(def, callsite),
|
||||
|
||||
buildMro: (graph, parsedFiles, nodeLookup) =>
|
||||
buildMro(graph, parsedFiles, nodeLookup, defaultLinearize),
|
||||
|
||||
populateOwners: (parsed: ParsedFile) => populateClassOwnedMembers(parsed),
|
||||
|
||||
// C# uses `base` for super-class dispatch, not `super`. Match as a
|
||||
// plain identifier (no `()` call like Python's `super(...)`) — `base`
|
||||
// is a keyword-like receiver, not a callable.
|
||||
isSuperReceiver: (text) => text.trim() === 'base',
|
||||
|
||||
// C# is statically typed — type information is reliable. Field-
|
||||
// fallback heuristic stays off (the type-binding layer already
|
||||
// produces precise owner types); return-type propagation on is fine
|
||||
// since signatures are authoritative.
|
||||
fieldFallbackOnMethodLookup: false,
|
||||
propagatesReturnTypesAcrossImports: true,
|
||||
};
|
||||
|
||||
export { csharpScopeResolver };
|
||||
|
|
@ -12,6 +12,7 @@
|
|||
import { SupportedLanguages } from 'gitnexus-shared';
|
||||
import type { ScopeResolver } from '../contract/scope-resolver.js';
|
||||
import { pythonScopeResolver } from '../../languages/python/scope-resolver.js';
|
||||
import { csharpScopeResolver } from '../../languages/csharp/scope-resolver.js';
|
||||
|
||||
/** Map of `SupportedLanguages` → `ScopeResolver`. The phase iterates
|
||||
* this map intersected with `MIGRATED_LANGUAGES` (the per-language
|
||||
|
|
@ -20,4 +21,7 @@ import { pythonScopeResolver } from '../../languages/python/scope-resolver.js';
|
|||
export const SCOPE_RESOLVERS: ReadonlyMap<SupportedLanguages, ScopeResolver> = new Map<
|
||||
SupportedLanguages,
|
||||
ScopeResolver
|
||||
>([[SupportedLanguages.Python, pythonScopeResolver]]);
|
||||
>([
|
||||
[SupportedLanguages.Python, pythonScopeResolver],
|
||||
[SupportedLanguages.CSharp, csharpScopeResolver],
|
||||
]);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue