From f18b86ba6138a794b0e8aabeb884f2ba20cf41b1 Mon Sep 17 00:00:00 2001 From: abhay-codes07 Date: Tue, 1 Sep 2026 23:30:24 +0530 Subject: [PATCH] fix(extract): collapse byte-identical duplicate edges at emission (#3251) --- graphify/extractors/engine.py | 15 ++++ tests/test_csharp_call_site_generic_args.py | 15 +++- tests/test_duplicate_annotation_edges.py | 93 +++++++++++++++++++++ 3 files changed, 120 insertions(+), 3 deletions(-) create mode 100644 tests/test_duplicate_annotation_edges.py diff --git a/graphify/extractors/engine.py b/graphify/extractors/engine.py index 38e9a5420..c2ccdd755 100644 --- a/graphify/extractors/engine.py +++ b/graphify/extractors/engine.py @@ -3,6 +3,7 @@ import hashlib import importlib +import json from graphify.extractors.base import _LANGUAGE_BUILTIN_GLOBALS, _file_stem, _make_id, _read_text from graphify.ids import normalize_id from graphify.extractors.models import LanguageConfig @@ -5979,9 +5980,23 @@ def _scan_js_module_dispatch(n) -> None: # ── Clean edges ─────────────────────────────────────────────────────────── valid_ids = seen_ids clean_edges = [] + # Byte-identical duplicates collapse to one edge (#3251): a signature that + # annotates the same type twice (``def f(a: Path, b: Path)``) is ONE + # reference relationship at one location, but the per-occurrence emission + # loops above append it once per annotation — in every language block, since + # neither add_edge nor the raw appends de-duplicate. The copies carry zero + # information (build's dedup drops them anyway) and their only observable + # effect is tripping diagnose_extraction's exact_duplicate_edges health + # warning. Only edges whose ENTIRE payload is identical collapse; any + # differing field (source_location, context, metadata, …) keeps both. + _seen_edge_payloads: set[str] = set() for edge in edges: src, tgt = edge["source"], edge["target"] if src in valid_ids and (tgt in valid_ids or edge["relation"] in ("imports", "imports_from", "re_exports")): + payload = json.dumps(edge, sort_keys=True, default=str) + if payload in _seen_edge_payloads: + continue + _seen_edge_payloads.add(payload) clean_edges.append(edge) # Ruby mixins were collected during the node walk (before raw_calls existed); diff --git a/tests/test_csharp_call_site_generic_args.py b/tests/test_csharp_call_site_generic_args.py index 670f44711..0d070e585 100644 --- a/tests/test_csharp_call_site_generic_args.py +++ b/tests/test_csharp_call_site_generic_args.py @@ -213,7 +213,16 @@ def test_call_site_generic_args_appear_in_issue_repro(tmp_path): ), }) izeta_refs = [tgt for src, tgt, _ctx in all_di_refs if src == ".Di()" and tgt == "IZeta"] - assert len(izeta_refs) >= 2, ( - "s.AddScoped>() must link BOTH the outer IZeta " - f"and the inner IZeta (inside the Box<...> argument); got {izeta_refs!r}" + # The outer IZeta and the inner IZeta (inside Box<...>) are the same + # reference relationship at the same location, so since #3251 the two + # walk occurrences collapse into ONE edge at extraction (they were always + # collapsed by build, and the raw duplicate tripped + # diagnose_extraction's exact_duplicate_edges warning). The call-site bug + # this test guards emitted NO edge at all, so existence keeps the teeth. + assert len(izeta_refs) == 1, ( + "s.AddScoped>() must link IZeta exactly once " + f"(one relationship, no duplicate edge); got {izeta_refs!r}" + ) + assert (".Di()", "Box") in {(s, t) for s, t, _c in all_di_refs}, ( + "the Box<...> argument itself must still link from the Di method" ) diff --git a/tests/test_duplicate_annotation_edges.py b/tests/test_duplicate_annotation_edges.py new file mode 100644 index 000000000..70569562e --- /dev/null +++ b/tests/test_duplicate_annotation_edges.py @@ -0,0 +1,93 @@ +"""Exact-duplicate edge emission from repeated annotations (#3251). + +A signature that annotates the same type twice (``def f(a: Path, b: Path)``) +used to emit one identical ``references`` edge per occurrence — same source, +target, relation, source_location and context. build() drops the copy, but +diagnose_extraction first counts it under ``exact_duplicate_edges`` and the +run ends with an unexplained GRAPH HEALTH WARNING. Duplicates now collapse at +extraction; anything differing in any field still survives. +""" + +import collections + +from graphify.diagnostics import diagnose_extraction +from graphify.extract import extract + + +def _edge_counts(result): + return collections.Counter( + (e["source"], e["target"], e["relation"], + e.get("source_location"), e.get("context")) + for e in result["edges"] + ) + + +def test_python_repeated_parameter_annotation_emits_one_edge(tmp_path, monkeypatch): + """The issue's exact repro: two same-typed params, one references edge.""" + monkeypatch.chdir(tmp_path) + (tmp_path / "sample.py").write_text( + "from pathlib import Path\n\n\n" + "def two_params(a: Path, b: Path) -> None:\n" + " print(a, b)\n\n\n" + "def one_param(a: Path) -> None:\n" + " print(a)\n", + encoding="utf-8", + ) + r = extract([tmp_path / "sample.py"], cache_root=tmp_path) + counts = _edge_counts(r) + dupes = {k: v for k, v in counts.items() if v > 1} + assert dupes == {}, f"exact duplicate edges emitted: {dupes}" + assert counts[("sample_two_params", "path", "references", + "L4", "parameter_type")] == 1 + assert counts[("sample_one_param", "path", "references", + "L8", "parameter_type")] == 1 + + +def test_csharp_repeated_parameter_annotation_emits_one_edge(tmp_path, monkeypatch): + """The same disease exists in every language block — C# as the witness.""" + monkeypatch.chdir(tmp_path) + (tmp_path / "Svc.cs").write_text( + "public class Svc {\n" + " public void Copy(Widget a, Widget b) { }\n" + "}\n" + "public class Widget { }\n", + encoding="utf-8", + ) + r = extract([tmp_path / "Svc.cs"], cache_root=tmp_path) + dupes = {k: v for k, v in _edge_counts(r).items() if v > 1} + assert dupes == {}, f"exact duplicate edges emitted: {dupes}" + + +def test_diagnose_reports_zero_exact_duplicates(tmp_path, monkeypatch): + """The health warning the issue hit is gone at the source.""" + monkeypatch.chdir(tmp_path) + (tmp_path / "sample.py").write_text( + "from pathlib import Path\n\n\n" + "def two_params(a: Path, b: Path) -> None:\n" + " print(a, b)\n", + encoding="utf-8", + ) + r = extract([tmp_path / "sample.py"], cache_root=tmp_path) + summary = diagnose_extraction(r) + assert summary["exact_duplicate_edges"] == 0 + + +def test_differing_locations_and_contexts_survive(tmp_path, monkeypatch): + """Only byte-identical edges collapse: the same type referenced at two + locations, or under two contexts at one location, keeps every edge.""" + monkeypatch.chdir(tmp_path) + (tmp_path / "sample.py").write_text( + "from pathlib import Path\n\n\n" + "def alpha(a: Path) -> None:\n" + " print(a)\n\n\n" + "def beta(b: Path) -> Path:\n" + " return b\n", + encoding="utf-8", + ) + r = extract([tmp_path / "sample.py"], cache_root=tmp_path) + counts = _edge_counts(r) + # Two locations → two edges. + assert counts[("sample_alpha", "path", "references", "L4", "parameter_type")] == 1 + assert counts[("sample_beta", "path", "references", "L8", "parameter_type")] == 1 + # Same location, different context (parameter vs return) → both edges. + assert counts[("sample_beta", "path", "references", "L8", "return_type")] == 1