Skip to content

Resolve C# cross-file type references and extract enum/struct/record declarations#1466

Closed
TheFedaikin wants to merge 1 commit into
Graphify-Labs:v8from
TheFedaikin:v8
Closed

Resolve C# cross-file type references and extract enum/struct/record declarations#1466
TheFedaikin wants to merge 1 commit into
Graphify-Labs:v8from
TheFedaikin:v8

Conversation

@TheFedaikin

Copy link
Copy Markdown
Contributor

Summary

Adds a deterministic C# cross-file type-reference resolver so dangling inherits / implements
/ references edges re-point from no-source "shadow" stubs to their real definitions,
disambiguating same-named collisions (e.g. WeaponData defined in two namespaces) by the
referencing file's using directives + enclosing namespace. This is the C# counterpart to the
Java type-reference resolver (#1318).

Motivation

Cross-file C# type references resolve by bare name and fall back to a no-source shadow stub.
_rewire_unique_stub_nodes repairs that only when a name is globally unique; when two
namespaces define a same-named type it bails, so the edge stays stuck on the shadow node and
the real type is wrongly isolated. A using N; directive (plus the enclosing namespace) names
the scope precisely, so it disambiguates where bare-name matching cannot.

What changed

  • graphify/extractors/csharp.py (new) — _resolve_csharp_type_references(per_file, paths,
    all_nodes, all_edges). Mirrors _resolve_java_type_references. Runs after id-disambiguation
    and _rewire_unique_stub_nodes, so it only handles the ambiguous remainder. Mutates
    nodes/edges in place and drops shadow stubs no edge references anymore.
  • graphify/extract.py — wires the resolver into the cross-file pass, immediately after the
    Java type-reference resolution, behind a try/except that logs-and-skips so extraction never
    breaks. extract.py re-exports the resolver name (callers/imports unchanged).
  • graphify/extract.py — _CSHARP_CONFIG — broadened class_types to also extract
    enum_declaration, struct_declaration, and record_declaration as type definitions (previously
    only class/interface). References to C# enums, structs, and records now get real definition
    nodes the resolver can re-point to.

Design notes

C# deltas from the Java resolver:

  • A plain using N; is namespace-wide: a bare T is resolved by trying (N, T) for each open
    namespace and accepting only a unique hit — the god-node guardrail (ambiguous matches are
    refused, not picked arbitrarily).
  • using X = N.T; is a single-type alias.
  • global using … is normalized (the global prefix is stripped).
  • using static N.T; is ignored (it imports members, not a namespace/type).
  • The global namespace is keyed as the bare label.

Placement follows the extractor-split conventions (#1212): csharp.py imports shared
primitives (_read_text) from extractors.base, keeps the one-way import direction (extract.py
→ extractors/, never the reverse), and extract.py re-exports the moved name. The
config-driven extract_csharp extractor stays in extract.py for now — per
extractors/MIGRATION.md, config-driven languages can't be ported until the shared
_extract_generic core moves as its own coordinated batch; extractors/csharp.py is its
eventual home.

Tests

tests/test_csharp_type_resolution.py — 7 cases:

  1. cross-file inherits resolves to the real def
  2. same-name collision disambiguated by using
  3. global using + global namespace
  4. cross-namespace enum reference resolves
  5. struct + record references resolve
  6. ambiguity guardrail — two candidates → refuse, leave dangling (no wrong edge)
  7. using X = N.T; alias resolution

Verification

  • Full test suite passes; ruff lint and skillgen --check (the two pre-commit gates) clean.
  • Behavior-neutral for non-C# languages — no extractor changes, and the new pass only runs on
    .cs inputs.
  • Validated on a real Unity 6.3 C# fixture: same-named internal collisions re-point to the
    correct definition via using directives, while genuinely-external types (Unity/.NET)
    correctly stay unresolved.

@TheFedaikin TheFedaikin changed the title feat(extract): C# cross-file type-ref resolution + enum/struct/record extraction Resolve C# cross-file type references and extract enum/struct/record declarations Jun 25, 2026
safishamsi pushed a commit that referenced this pull request Jun 27, 2026
…cord (#1466)

Adds _resolve_csharp_type_references (graphify/extractors/csharp.py), the C#
counterpart to _resolve_java_type_references. It re-points dangling
inherits/implements/references edges from no-source shadow stubs onto the real
definitions, disambiguating same-named types across namespaces using the
referencing file's `using` directives + enclosing namespace; ambiguous matches
are refused (unique-hit guardrail), not guessed. Runs after id-disambiguation and
the sourceless-stub rewire, on the ambiguous remainder, behind a log-and-skip
try/except. _CSHARP_CONFIG is broadened to extract enum/struct/record as type
definitions so references to them resolve too.

Ported from PR #1466 by @TheFedaikin. Known follow-up: types declared in
nested/multiple namespaces in one file aren't registered as targets yet (fails
safe — under-resolves to a stub, never mis-resolves). Advances #1318 for C#.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@safishamsi

Copy link
Copy Markdown
Collaborator

Thanks @TheFedaikin — faithful C# port of the Java type-reference resolver, with the unique-hit guardrail correctly refusing ambiguous using matches. Landed on v8 in 76b6eab with your authorship. Verified: same-named types across namespaces resolve via using (shadow stub dropped), ambiguous using refuses without crashing, and enum/struct/record now extract. Known follow-up (noted in the commit): types in nested/multiple namespaces in one file aren't registered yet (fails safe). Reviewed alongside #1501/#1479 to confirm they compose. Closing as merged-by-port.

@safishamsi safishamsi closed this Jun 27, 2026
safishamsi added a commit that referenced this pull request Jun 28, 2026
Dates the 0.8.51 CHANGELOG and bumps the version. Highlights: Obsidian export no
longer overwrites user notes / .obsidian config in an existing vault (#1506);
cross-file node-ID cluster fixes — C# resolver (#1466), Go sourceless stubs
(#1500), import-stub disambiguation (#1462); Java enum/annotation type nodes
(#1512) + generic parents (#1510); explain/affected source-file path lookup
(#1503); claude-cli Windows GBK fix (#1505); macOS install docs (#1471) and the
global-add docs fix (#1489).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
safishamsi pushed a commit that referenced this pull request Jun 30, 2026
Extends the C# type-reference resolver (#1466) to be namespace-aware,
advancing the #1318 shadow-node umbrella for C#:

- The namespace is folded into the C# node id (_make_id(stem, namespace,
  name)), so two same-named types in different namespaces in one file no
  longer collapse — replacing #1466's detect-and-skip workaround for
  multi-namespace files.
- Lexical per-block using-scope: a `using` applies only where it is in
  scope (file-level, or the enclosing namespace block via a scope chain),
  so sibling namespace blocks no longer share each other's usings.
- Qualified references (`Namespace.Type`) resolve via in-scope aliases
  (`using Q = X.Y`) then exact known namespaces; generics are stripped.

Preserves (and tightens) the refuse-rather-than-guess discipline: a bare
reference resolves only when exactly one in-scope namespace provides the
type; an ambiguous reference (e.g. `using A; using B;` both defining
`Widget`) resolves to nothing rather than fanning out. Verified: `using A`
-> A.Widget only; ambiguous -> no edge; qualified `B.Widget` -> B.Widget
regardless of usings; sibling-block using-scope isolated; no dangling
edges or fan-out.

Reconciled onto current v8 (the PR predated the C++/ObjC member-call
resolvers); full suite green, the C++/ObjC resolution coexists.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants