Skip to content

fix(csharp): strip call-site type arguments from generic calls - #2676

Open
rohit-jsfreaky wants to merge 1 commit into
Graphify-Labs:v8from
rohit-jsfreaky:fix/csharp-call-site-type-arguments
Open

fix(csharp): strip call-site type arguments from generic calls#2676
rohit-jsfreaky wants to merge 1 commit into
Graphify-Labs:v8from
rohit-jsfreaky:fix/csharp-call-site-type-arguments

Conversation

@rohit-jsfreaky

Copy link
Copy Markdown
Contributor

Fixes #2624.

Summary

A C# call written with explicit type arguments produced no calls edge, while the same method called with an inferred type argument resolved fine — so the call-site syntax decided it, not the declaration.

tree-sitter wraps a name carrying a type-argument list in a generic_name node. The C# call handler read that node verbatim, so the callee became Fetch<Payload> while its declaration is stored bare as .Fetch(); the lookup key never matched and the edge was dropped silently.

A second, independent defect sat next to it: in Bag<Payload>.Make() the receiver is a generic_name, and a receiver was only captured when it was a plain identifier — so it was dropped entirely and _resolve_csharp_member_calls had nothing to bind.

Changes

  • Add _csharp_name_without_type_args(), returning the bare name of a call-site name node. It reads the name field first and falls back to the first identifier child — the same order _csharp_collect_type_refs uses (the pinned grammar exposes no name field on generic_name; trying the field first keeps this working on versions that do).
  • Apply it to the callee in recv.M<T>(), to a generic_name receiver (Bag<Payload>.Make()), and to an unqualified call (Local<Payload>()), which previously fell through to the raw-text scan and kept the <...>.

C# only — all three call sites sit inside the tree_sitter_c_sharp branch and the helper is used nowhere else. A node that is not a generic_name returns _read_text unchanged, so non-generic calls take exactly the path they did before.

This does not loosen resolution. Stripping the type-argument list only makes the lookup key match; the call still goes through receiver typing and the god-node guard, so an ambiguous or untypable receiver still yields no edge rather than a wrong one. A qualified generic (Demo.Bag<Payload>) has no bare identifier child and keeps its previous name minus the type arguments, so nothing that used to be recorded stops being recorded.

Reproduction

The issue's five files, graphify extract . --code-only --no-cluster:

call before after
A() -> Registry.Fetch<Payload>() missing resolved
B() -> box.Read<Payload>() missing resolved
C() -> Registry.Has() (control) resolved resolved
D() -> Registry.Pick(item) (control) resolved resolved
E() -> Bag<Payload>.Make() missing resolved

2 of 5 edges before, 5 of 5 after. Two further shapes were broken by the same cause and are not in the report — both now covered: Local<Payload>("k") (unqualified, no receiver) and this.Local<Payload>("k").

Tests

Five tests appended to tests/test_csharp_member_calls.py. Each was confirmed to fail with the source change reverted and the tests kept, and to pass with it.

One is a guard rather than a repro: a generic call on a typed field must still bind to that field's type and not to a same-named method on an unrelated class (#1609), now exercised through a generic call. The constructed-generic-receiver test is split across two files deliberately — in a single file the callee resolves by an in-file label match and never reaches receiver typing, so a same-file version passes even with the receiver dropped.

uv run --no-sync pytest tests/test_csharp_member_calls.py tests/test_csharp_type_resolution.py tests/test_csharp_partial_classes.py tests/test_dotnet.py -q — 126 passed.

uv run --no-sync ruff check graphify/extractors/engine.py tests/test_csharp_member_calls.py — passed.

Full suite: 22 failed, 4304 passed, 13 skipped, against a clean v8 baseline of 22 failed, 4299 passed, 13 skipped — the same 22 in both runs, all pre-existing and platform-sensitive on this Windows machine, none touching this path. The +5 are the new tests.

graphify update . re-run per AGENTS.md: 765 files, 13,426 nodes, no errors.

Tested against the pinned tree_sitter_c_sharp 0.23.5 on Windows; other grammar versions and platforms were not exercised.

@graphify-labs graphify-labs Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Graphify reviewed this change.

Looks safe to merge — no coupling regressions and no blocking issues, checked against the code graph (not a self-assessment).


Graphify review — findings

This PR addresses C# call-site handling when explicit type arguments are present (e.g., X.M<T>(), Bag<Payload>.Make(), Local<Payload>()). It adds a new helper _csharp_name_without_type_args that strips the type-argument list from generic_name nodes, and updates _extract_generic to use it for callee names and receivers, while also recognizing generic_name as a valid receiver/function node type alongside identifier. The test file adds a new suite of cases covering explicit type arguments on type receivers, typed local receivers, constructed generic type receivers (split across files), unqualified/this generic calls, and a check that type-argument stripping doesn't bypass receiver typing. The changed surface is limited to the C# path in graphify/extractors/engine.py and the corresponding tests in tests/test_csharp_member_calls.py.

No blocking issues surfaced. 1 lower-confidence candidate did not survive cross-model review.

Analysis details — impact, health, verification

Impact & health

Graphify review

Impact — 650 functions depend on the 255 functions this change touches.

Health — this change adds coupling hotspots:

  • worse: walk_calls() — 1 callers, 16 callees

Verification — 650 functions in the blast radius were not formally verified this run (proofs are advisory here).

Gate & verification

graphify gate

PASS — objectively clean (no health regressions, tests not run — proofs not run this pass (advisory)). Grounded, not self-assessed.

Advisory (not blocking):

  • verification_scope: 591 function(s) in the blast radius were not formally verified this run

· 1 more finding(s) on lines outside this diff (see the check run).

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.

C#: explicit type arguments at a call site (X.M<T>(...)) drop the calls edge

1 participant