fix(rust): emit field type references for tuple structs

extract_rust() only traversed field_declaration_list (named-struct
bodies), so tuple structs -- whose positional fields nest under
ordered_field_declaration_list -- had every field type reference
silently dropped from the graph.

This is the same node shape the enum handler already accounts for
(tuple variants nest their types under ordered_field_declaration_list);
the struct path was simply left behind. Add an additive branch that,
for each type node in a tuple struct's ordered_field_declaration_list,
collects type refs via _rust_collect_type_refs and emits references
edges with the appropriate field / generic_arg context. The
named-struct path is untouched.

For `struct Wrapper(Logger, Config);` with Logger/Config defined
in-file, no field edges were produced before; both are now emitted.

Adds test_rust_tuple_struct_field_references and a tuple struct to the
shared Rust fixture covering plain and generic positional field types.
This commit is contained in:
Synvoya
2026-07-01 22:00:01 +10:00
committed by safishamsi
parent 984a6a8f0a
commit 7eb847bcf7
3 changed files with 36 additions and 0 deletions
+21
View File
@@ -8180,6 +8180,27 @@ def extract_rust(path: Path) -> dict:
if tgt != item_nid:
add_edge(item_nid, tgt, "references",
field.start_point[0] + 1, context=ctx)
# Tuple structs (`struct Wrapper(pub Logger, Config);`) nest their
# positional field types directly under ordered_field_declaration_list
# with no field_declaration wrapper -- the same shape handled for tuple
# enum variants below. Without this branch these field type references
# are silently dropped.
for c in node.children:
if c.type != "ordered_field_declaration_list":
continue
fline = c.start_point[0] + 1
for tc in c.children:
if tc.type not in ("type_identifier", "generic_type",
"scoped_type_identifier", "reference_type",
"primitive_type", "tuple_type", "array_type"):
continue
refs = []
_rust_collect_type_refs(tc, source, False, refs)
for ref_name, role in refs:
ctx = "generic_arg" if role == "generic_arg" else "field"
tgt = ensure_named_node(ref_name, fline)
if tgt != item_nid:
add_edge(item_nid, tgt, "references", fline, context=ctx)
if t == "enum_item":
# Variant payload types nest under enum_variant_list ->
# enum_variant -> ordered_field_declaration_list (tuple variant,
+2
View File
@@ -56,3 +56,5 @@ enum GraphEvent {
NodeAdded(Graph),
Processed { proc: DataProcessor },
}
struct GraphPair(Graph, Result<DataProcessor>);
+13
View File
@@ -365,6 +365,19 @@ def test_rust_struct_field_emits_field_context():
assert ("DataProcessor", "DataProcessor") not in _edge_labels(r, "references", "field")
def test_rust_tuple_struct_field_references():
"""Tuple struct fields (`struct Wrapper(A, B);`) nest their positional types
under ordered_field_declaration_list with no field_declaration wrapper -- the
same shape as tuple enum variants. That path was not traversed for structs, so
tuple-struct field type references were silently dropped.
"""
r = extract_rust(FIXTURES / "sample.rs")
field_refs = _edge_labels(r, "references", "field")
assert ("GraphPair", "Graph") in field_refs, "tuple-struct field reference missing"
assert ("GraphPair", "Result") in field_refs, "tuple-struct generic field reference missing"
assert ("GraphPair", "DataProcessor") in _edge_labels(r, "references", "generic_arg")
def test_rust_method_parameter_return_and_generic_contexts():
r = extract_rust(FIXTURES / "sample.rs")
assert ("build", "DataProcessor") in _edge_labels(r, "references", "parameter_type")