Closes 3 parity failures (51 → 48). Target bucket: Category C from the
parity plan.
Changes:
- `languages/csharp/receiver-binding.ts` (new): walks up from a
function node to the enclosing class/struct/record/interface,
synthesizes `@type-binding.self` captures with boundName `'this'`
(and `'base'` when the enclosing type is a class/record with an
explicit base_list entry). Skips static methods and interface /
struct `base` cases. Anchors to the method's `body` block so the
scope-extractor's positionIndex places the binding inside the
function scope (not the enclosing class scope).
- `languages/csharp/captures.ts`: route `@scope.function` matches
through the synth, emitting the receiver captures as separate
matches.
- `languages/csharp/interpret.ts`: map `@type-binding.self` to
`source: 'self'` (parity with Python).
- `languages/csharp/query.ts`: explicit patterns for `this.X()`,
`base.X()`, and `this.X = ...` / `base.X = ...` assignment writes.
`this` and `base` are anonymous tokens in tree-sitter-c-sharp so
the existing `expression: (_)` pattern (named-only) didn't match.
Tests:
- 8 new unit tests for receiver-binding synthesis edge cases
(class/struct/record/interface, static, nested, constructor,
local function inside method).
- Parity: 48 failed | 127 passed (175) under REGISTRY_PRIMARY_CSHARP=1;
legacy path 175/175 green.
Adversarial review flagged overload narrowing as a blocker for the Unit
7 flip. This lands the declaration-side metadata; callsite-side arity
synthesis is a separate gap we'll address if the parity gate surfaces
overload misresolution.
- `arity-metadata.ts` — reads `csharpMethodConfig.extractParameters`
and produces `{ parameterCount, requiredParameterCount,
parameterTypes }`. `params` variadic collapses parameterCount to
undefined (matches Python's `*args` treatment) and appends a literal
`'params'` marker to parameterTypes so the compatibility hook can
detect it without re-reading the AST. Default-valued parameters
contribute to optionalCount → requiredParameterCount = total − optional.
- `arity.ts` — `csharpArityCompatibility(def, callsite)` returns
compatible / incompatible / unknown. Mirrors Python's three-verdict
shape so the central registry's arity filter works without adapter
logic per-verdict.
- `captures.ts` — on every @declaration.method / @declaration.constructor
/ @declaration.function match, synthesize
@declaration.parameter-count, @declaration.required-parameter-count,
and @declaration.parameter-types captures. Covers method_declaration,
constructor_declaration, destructor_declaration, operator_declaration,
conversion_operator_declaration, and local_function_statement.
12 new tests: 5 on captures-side synthesis (method + params + types +
variadic + constructor + local function), 7 on the compatibility hook.
66/66 C# scope-resolution unit tests pass; tsc clean.
Three-tier shadowing, same shape as Python's LEGB merge:
0: local — class members, locals, parameters
1: using — namespace / named / reexport (equal tier; compiler
requires explicit qualifier if two using collide)
2: wildcard — `using static X.Y;` static-member imports
Within the surviving tier, de-dup by DefId (last-write-wins) so a
re-declared `using` cleanly replaces its earlier binding. Explicit
interface implementations bind under their qualified name in the
extractor layer, so they don't collide with plain simple names here.
7 new tests pin precedence + dedup semantics. 54/54 C# scope-resolution
unit tests pass.
Adds simple-hooks.ts mirroring Python's pattern:
- `csharpBindingScopeFor` — delegates to innermost (block scope is
already captured by @scope.block in the query).
- `csharpImportOwningScope` — binds `using` inside a namespace to that
namespace's scope so imports don't leak into sibling namespaces.
File-level using delegates to module. Function-body using (not legal
C# but possible from malformed input) attaches to the function.
- `csharpReceiverBinding` — looks up `this` / `base` in the function
scope's type bindings; returns null for statics, free functions, and
non-Function scopes. `this` / `base` synthesis itself is deferred to
a follow-up (matches Python's receiver-binding.ts pattern).
9 new tests pin delegation semantics. 47/47 C# scope-resolution unit
tests pass; tsc clean.
Adds the three files Unit 2 of the C# scope-resolution plan calls for:
- `import-decomposer.ts` — inspects each `using_directive` node and
synthesizes `@import.kind/source/name/alias` markers. Kinds:
`namespace` — `using X;` / `using X.Y.Z;`
`alias` — `using Alias = X.Y.Z;` (generics stripped)
`static` — `using static X.Y;`
`global using` maps to namespace (plan's deferred decision); the
`global::` qualifier is stripped before emitting.
- `interpret.ts` — reads the markers and builds `ParsedImport`. Static
using maps to `kind: 'wildcard'` since it brings members into
unqualified scope; Unit 4's merge-bindings tiers wildcards lowest.
Also provides `interpretCsharpTypeBinding` with nullable/single-arg
generic/qualifier stripping so receiver-typed resolution sees the
concrete class name.
- `import-target.ts` — suffix-match adapter returning a single primary
file. Cross-file partial-class aggregation runs later at graph-bridge
time (Unit 6). The csproj-based `resolveCSharpImportInternal` stays
on the legacy path until Unit 7's parity gate surfaces a gap.
- `captures.ts` routes `@import.statement` matches through the
decomposer so the interpreter sees the markers it needs.
Tests cover every using flavor + resolution edge cases. 38/38 scope-
resolution C# unit tests pass; tsc clean.
Adversarial review surfaced two Unit 1 bugs that would silently
corrupt the graph once C# is flipped on the scope-resolution path:
- `obj?.Save()` only emitted @reference.name, so receiver-bound
resolution downgraded to the free-call fallback and could mis-link
to an imported `Save`. Capture the conditional_access_expression
receiver under @reference.receiver.
- `operator_declaration` had @scope.function but no @declaration.method
owner, so calls inside operator bodies were attributed to the
enclosing class and the operator itself disappeared from method
lookup. Capture the operator token as @declaration.name (downstream
csharpMethodConfig normalizes to op_Addition etc.).
- `conversion_operator_declaration` was missing from both scope and
declaration sets. Added with the target type as the name anchor.
Arity metadata for overload resolution remains deferred to Unit 5 and
gated behind Unit 7's parity flip, as documented in captures.ts.
First slice of the C# scope-resolution migration (issue #934, RFC #909
Ring 3). Closes `Unit 1` of
docs/plans/2026-04-21-004-feat-csharp-scope-resolution-plan.md.
Adds:
- src/core/ingestion/languages/csharp/query.ts — tree-sitter scope
query covering compilation_unit, namespace (block + file-scoped),
class-like (class/interface/struct/record/enum), method-like
(method/constructor/destructor/local_function/operator), property
and field declarations, using directives, type bindings (parameter
annotations, local variable annotations, constructor inference,
invocation alias), and references (free call, member call including
null-conditional, constructor call, member write).
- src/core/ingestion/languages/csharp/captures.ts — pass-through
orchestrator mirroring python/captures.ts. Import decomposition
(Unit 2), receiver-type-binding synthesis (Unit 3), and arity
metadata synthesis (Unit 5) stub out for future units.
- src/core/ingestion/languages/csharp/cache-stats.ts — PROF
instrumentation mirror of python/cache-stats.ts.
Design notes:
- Return-type / field-type / property-type captures deferred.
tree-sitter-c-sharp does not expose these under a clean named field
that pattern-matches. When Unit 7 parity gate surfaces a gap, add
positional patterns or a post-hoc extractor lookup.
- object_creation_expression with qualified_name type — the qualified
name itself is the reference text; captured as a whole via a
dedicated tag so interpretation in later units can split namespace
+ name.
- Null-conditional calls use positional descendant patterns because
tree-sitter-c-sharp's member_binding_expression and
conditional_access_expression don't expose named fields.
Coverage:
- 23/23 new unit tests in
test/unit/scope-resolution/csharp/csharp-captures.test.ts cover
every capture tag. Confirmed against tree-sitter-c-sharp via the
probe-script loop during development; grammar drift would surface
as a capture-shape assertion failure.
- tsc --noEmit clean.
No changes to shared infrastructure. Resolver wiring + registration
land in Unit 6.