From 674184d462539f09f35cf53b6926451ef0ae2516 Mon Sep 17 00:00:00 2001 From: Synvoya <16019863+Synvoya@users.noreply.github.com> Date: Wed, 1 Jul 2026 20:51:23 +1000 Subject: [PATCH] fix(rust): emit references edges for enum variant field types MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Enum variant payload types were silently dropped — `struct_item` and `trait_item` had type-reference handlers but `enum_item` had none, so the variant field types were never traversed. Add an `enum_item` branch that walks `enum_variant_list -> enum_variant -> ordered_field_declaration_list` (tuple variants, `Click(Logger)`) and `field_declaration_list` (struct variants, `Resize { size: Dim }`), emitting a `references` edge from the enum to each field type. Reuses the same type collection as the struct path. Adds an enum to the fixture and a regression test. Co-Authored-By: Claude Opus 4.8 (1M context) --- graphify/extract.py | 39 +++++++++++++++++++++++++++++++++++++++ tests/fixtures/sample.rs | 5 +++++ tests/test_multilang.py | 13 +++++++++++++ 3 files changed, 57 insertions(+) diff --git a/graphify/extract.py b/graphify/extract.py index 96a72336..99419885 100644 --- a/graphify/extract.py +++ b/graphify/extract.py @@ -8160,6 +8160,45 @@ def extract_rust(path: Path) -> dict: if tgt != item_nid: add_edge(item_nid, tgt, "references", field.start_point[0] + 1, context=ctx) + if t == "enum_item": + # Variant payload types nest under enum_variant_list -> + # enum_variant -> ordered_field_declaration_list (tuple variant, + # `Click(Logger)`) | field_declaration_list (struct variant, + # `Resize { size: Dim }`). Neither was traversed, so every + # enum-variant type reference was silently dropped. + _TYPE_NODES = ("type_identifier", "generic_type", + "scoped_type_identifier", "reference_type", + "primitive_type", "tuple_type", "array_type") + + def _emit_enum_type(type_node, at_line): + if type_node is None: + return + refs2: list[tuple[str, str]] = [] + _rust_collect_type_refs(type_node, source, False, refs2) + for ref_name, role in refs2: + ctx = "generic_arg" if role == "generic_arg" else "field" + tgt = ensure_named_node(ref_name, at_line) + if tgt != item_nid: + add_edge(item_nid, tgt, "references", at_line, context=ctx) + + for c in node.children: + if c.type != "enum_variant_list": + continue + for variant in c.children: + if variant.type != "enum_variant": + continue + vline = variant.start_point[0] + 1 + for vc in variant.children: + if vc.type == "ordered_field_declaration_list": + for tc in vc.children: + if tc.type in _TYPE_NODES: + _emit_enum_type(tc, vline) + elif vc.type == "field_declaration_list": + for field in vc.children: + if field.type != "field_declaration": + continue + type_node = field.child_by_field_name("type") + _emit_enum_type(type_node, field.start_point[0] + 1) return if t == "impl_item": diff --git a/tests/fixtures/sample.rs b/tests/fixtures/sample.rs index 16e7fac2..9c1e39d8 100644 --- a/tests/fixtures/sample.rs +++ b/tests/fixtures/sample.rs @@ -51,3 +51,8 @@ impl DataProcessor { Result { value: input } } } + +enum GraphEvent { + NodeAdded(Graph), + Processed { proc: DataProcessor }, +} diff --git a/tests/test_multilang.py b/tests/test_multilang.py index c30b9e10..216ba95f 100644 --- a/tests/test_multilang.py +++ b/tests/test_multilang.py @@ -346,6 +346,19 @@ def test_rust_supertrait_emits_inherits(): assert ("Logger", "Processor") in _edge_labels(r, "inherits") +def test_rust_enum_variant_references(): + """Enum variant payload types must emit `references` edges. + + Tuple variants (`Click(T)`) and struct variants (`Resize { x: T }`) nest + their field types under enum_variant_list -> enum_variant; that path was + never traversed, so every enum-variant type reference was dropped. + """ + r = extract_rust(FIXTURES / "sample.rs") + refs = _edge_labels(r, "references") + assert ("GraphEvent", "Graph") in refs, "tuple-variant reference missing" + assert ("GraphEvent", "DataProcessor") in refs, "struct-variant reference missing" + + def test_rust_struct_field_emits_field_context(): r = extract_rust(FIXTURES / "sample.rs") assert ("DataProcessor", "Result") in _edge_labels(r, "references", "field")