diff --git a/graphify/extractors/rust.py b/graphify/extractors/rust.py index 621d10167..181d2addd 100644 --- a/graphify/extractors/rust.py +++ b/graphify/extractors/rust.py @@ -262,6 +262,8 @@ def extract_rust(path: Path) -> dict: name_node = node.child_by_field_name("name") if name_node: name = _read_text(name_node, source) + if t in ("static_item", "const_item") and name == "_": + return nid = _make_id(stem, name) is_type = t in ("struct_item", "enum_item", "trait_item") items_by_id.setdefault(nid, set()).add((is_type, name)) @@ -558,6 +560,8 @@ def extract_rust(path: Path) -> dict: name_node = node.child_by_field_name("name") if name_node: item_name = _read_text(name_node, source) + if item_name == "_": + return line = node.start_point[0] + 1 if parent_impl_nid: item_nid = _make_id(parent_impl_nid, item_name) diff --git a/tests/test_rust_anonymous_const.py b/tests/test_rust_anonymous_const.py new file mode 100644 index 000000000..c6cdfb20b --- /dev/null +++ b/tests/test_rust_anonymous_const.py @@ -0,0 +1,24 @@ +"""Anonymous Rust constants must not replace their owning file node.""" + +from graphify.build import build_from_json +from graphify.extract import extract + + +def test_anonymous_constants_preserve_file_identity(tmp_path): + source = tmp_path / "codec.rs" + source.write_text( + "pub const WIDTH: usize = 8;\n" + "const _: () = assert!(WIDTH == 8);\n" + "const _: () = ();\n" + "static READY: bool = true;\n" + "pub fn encode() {}\n", + encoding="utf-8", + ) + extracted = extract([source], root=tmp_path, cache_root=tmp_path / "cache") + graph = build_from_json(extracted, root=tmp_path, directed=False) + + assert len({node["id"] for node in extracted["nodes"]}) == len(extracted["nodes"]) + labels = {data["label"] for _, data in graph.nodes(data=True)} + assert {"codec.rs", "WIDTH", "READY", "encode()"} <= labels + assert "_" not in labels + assert all(source != target for source, target in graph.edges())