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")