diff --git a/gitnexus/src/core/ingestion/languages/csharp/index.ts b/gitnexus/src/core/ingestion/languages/csharp/index.ts index 5c396002d..c7f7d3207 100644 --- a/gitnexus/src/core/ingestion/languages/csharp/index.ts +++ b/gitnexus/src/core/ingestion/languages/csharp/index.ts @@ -53,6 +53,19 @@ * 8. **Expression-bodied `=>` members** — handled by the method * extractor, but receiver synthesis for `=> this.Field` shortcuts * follows the same path as block-bodied methods. + * 9. **Regex-based namespace-sibling detection** — + * `namespace-siblings.ts` scans raw file content for `namespace X` + * and `using static X.Y` to compute same-namespace cross-file + * visibility. Known misses (each acknowledged in-file): + * a. `global using static X.Y;` is not detected. + * b. Aliased `using static X = Y.Z;` is not detected. + * c. Multi-namespace files: classes are attributed to the first + * declared namespace only. + * d. Preprocessor-gated `namespace` declarations are seen as + * whichever branch is textually present. + * See `namespace-siblings.ts`'s file-head comment for the full + * rationale; refactor to AST-driven detection is deferred to a + * separate PR. * * Shadow-harness corpus parity is the authoritative signal for which * of these matter in practice. The CI parity gate blocks any PR that diff --git a/gitnexus/src/core/ingestion/languages/csharp/namespace-siblings.ts b/gitnexus/src/core/ingestion/languages/csharp/namespace-siblings.ts index 4a15eecfa..e2217ac1c 100644 --- a/gitnexus/src/core/ingestion/languages/csharp/namespace-siblings.ts +++ b/gitnexus/src/core/ingestion/languages/csharp/namespace-siblings.ts @@ -19,6 +19,37 @@ * sibling classes into each Namespace scope's finalized bindings * with `origin: 'namespace'` — a tier below `local` so a local * declaration still shadows a cross-file sibling with the same name. + * + * ## Why regex and not the AST + * + * The pass is file-path-driven and only needs two pieces of info per + * file: which `namespace X` it declares, and which `using static X.Y` + * it pulls in. The tree-sitter tree isn't available at the pass's + * call site (the scope-resolution orchestrator feeds raw + * `fileContents` — re-parsing just to count namespaces would cost + * more than the regex walk). We accept the regex's known misses (see + * below) in exchange for a cheap, allocation-free pass. + * + * ## Known misses + * + * The regex-based detection silently skips: + * - `global using static X.Y;` (no plain `using static` token). + * - Aliased `using static X = Y.Z;` (the `=` breaks the pattern). + * - Attributed namespace declarations like `[assembly: X] + * namespace Y` — the regex still matches `namespace Y` but any + * trailing `[attr]` before `{` on the same line would fail. + * - Multi-namespace files: the `first-wins` attribution below + * groups all top-level classes under the first declared + * namespace; truly interleaved namespaces are rare but lose + * fidelity here. + * - `namespace` identifiers split by preprocessor `#if` / + * conditional compilation — the regex sees whichever branch is + * textually present. + * + * The limitations are mirrored in `csharp/index.ts`'s ledger so the + * operator-visible surface and the in-code justification stay in sync. + * If additional precision is required, refactor to consume the + * `@namespace.name` capture via the extractor (deferred — separate PR). */ import type { BindingRef, ParsedFile, Scope, ScopeId, SymbolDefinition } from 'gitnexus-shared';