fix(csharp): emit type references for properties

The C# class-body walker only handled field_declaration, so a
property's type produced no references(field) edge. In idiomatic C#,
auto-properties (`public Widget Main { get; set; }`) — not bare fields
— are the standard way to declare state, so this silently dropped most
of a class's type relationships.

Add a property_declaration branch alongside the field_declaration
handler, guarded the same way (ts_module == tree_sitter_c_sharp,
parent_class_nid set). A property exposes its type on the node directly
(no variable_declaration wrapper), so read it via
child_by_field_name("type") and collect refs with
_csharp_collect_type_refs, mirroring the Java/PHP/Kotlin siblings so
List<Widget> yields both the List field ref and the Widget generic_arg
ref. Only emit when target != parent_class_nid.
This commit is contained in:
Synvoya
2026-07-02 00:24:22 +10:00
committed by safishamsi
parent 51f805e953
commit bb5e5192df
3 changed files with 44 additions and 0 deletions
+29
View File
@@ -3786,6 +3786,35 @@ def _extract_generic(
"references", line, context="field", metadata=metadata)
return
if (config.ts_module == "tree_sitter_c_sharp"
and t == "property_declaration"
and parent_class_nid):
# C# auto-properties (`public Widget Main { get; set; }`) are the
# idiomatic way to declare state, yet only field_declaration was
# handled — so property types produced no references edge. Unlike a
# field, a property exposes its type on the node directly (no
# variable_declaration wrapper), so read it straight off the `type`
# field. Use _csharp_collect_type_refs (like the Java/PHP/Kotlin
# siblings) so `List<Widget>` yields both the List field ref and the
# Widget generic_arg ref.
type_node = node.child_by_field_name("type")
if type_node is not None:
line = node.start_point[0] + 1
refs: list[tuple[str, str, bool, str]] = []
_csharp_collect_type_refs(type_node, source, False, refs)
for ref_name, role, qualified, qualifier in refs:
ctx = "generic_arg" if role == "generic_arg" else "field"
target_nid = ensure_named_node(ref_name, line)
if target_nid != parent_class_nid:
metadata = {"ref_token": ref_name}
if qualified:
metadata["qualified"] = True
if qualifier:
metadata["ref_qualifier"] = qualifier
add_edge(parent_class_nid, target_nid, "references",
line, context=ctx, metadata=metadata)
return
if (config.ts_module == "tree_sitter_java"
and t == "field_declaration"
and parent_class_nid):
+4
View File
@@ -21,6 +21,10 @@ namespace GraphifyDemo
{
private readonly HttpClient _client;
public Processor Owner { get; set; }
public List<Processor> Workers { get; set; }
public DataProcessor()
{
_client = new HttpClient();
+11
View File
@@ -542,6 +542,17 @@ def test_csharp_field_type_references_have_field_context():
), "DataProcessor field declarations should reference HttpClient with field context"
def test_csharp_property_type_references_have_field_context():
r = extract_csharp(FIXTURES / "sample.cs")
field_refs = _edge_labels(r, "references", "field")
# `public Processor Owner { get; set; }` — property type -> field ref.
assert ("DataProcessor", "Processor") in field_refs
# `public List<Processor> Workers { get; set; }` — the List container -> field.
assert ("DataProcessor", "List") in field_refs
# ...and the generic argument -> generic_arg.
assert ("DataProcessor", "Processor") in _edge_labels(r, "references", "generic_arg")
def test_csharp_call_edges_have_call_context():
r = extract_csharp(FIXTURES / "sample.cs")
node_by_id = {n["id"]: n["label"] for n in r["nodes"]}