From e906227cf65834d5e13008b2ef9f8e38ea85c383 Mon Sep 17 00:00:00 2001 From: guitelesc <158539422+guitelesc@users.noreply.github.com> Date: Thu, 27 Aug 2026 20:20:36 +0100 Subject: [PATCH] fix(commonlisp): derive node ids from the full path stem The Common Lisp extractor minted its id prefix from the bare path.stem instead of the canonical _file_stem used by every other extractor, so two same-basename .lisp files in different directories collided (one file's nodes vanished on merge) and nested .lisp files tripped a spurious legacy-id nudge. Use _make_id(_file_stem(path)) to match the shared convention; cross-file rewire is unaffected (it matches on label, not id). --- graphify/extractors/commonlisp.py | 8 ++++++-- tests/test_languages.py | 22 ++++++++++++++++++++++ 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/graphify/extractors/commonlisp.py b/graphify/extractors/commonlisp.py index f43e72c0..2ed9d9fb 100644 --- a/graphify/extractors/commonlisp.py +++ b/graphify/extractors/commonlisp.py @@ -4,7 +4,7 @@ from __future__ import annotations import warnings from pathlib import Path -from graphify.extractors.base import _make_id +from graphify.extractors.base import _file_stem, _make_id # Standard CL definer forms that introduce data/type/variable bindings @@ -84,7 +84,11 @@ def extract_commonlisp(path: Path) -> dict: except Exception as e: return {"nodes": [], "edges": [], "error": str(e)} - stem = path.stem + # Path-qualified, not the bare `path.stem`: same-named .lisp files in + # different directories must not collide (#1504). Pre-collapsed through + # `_make_id` because `_cl_id` would otherwise map the `/` separators to + # `_slash` via _CL_CHAR_MAP. + stem = _make_id(_file_stem(path)) str_path = str(path) nodes: list[dict] = [] edges: list[dict] = [] diff --git a/tests/test_languages.py b/tests/test_languages.py index 6fd3a45c..0f5c079e 100644 --- a/tests/test_languages.py +++ b/tests/test_languages.py @@ -3974,3 +3974,25 @@ def test_zig_enum_and_union_methods_are_extracted(tmp_path): for e in r["edges"] if e["relation"] == "calls" } assert (".area()", "helper()") in calls, "call from union method body dropped" + + +@_needs_commonlisp +def test_cl_ids_are_path_qualified_across_directories(tmp_path): + """Two same-named .lisp files in DIFFERENT directories must mint distinct + ids (#1504). The prefix was derived from the bare `path.stem`, so both + `a/sample.lisp` and `b/sample.lisp` minted `sample` / `sample_init`; when + they land in separate extract batches (what `graphify update` does) build() + merges them and one file's nodes are dropped.""" + a = tmp_path / "a" / "sample.lisp" + b = tmp_path / "b" / "sample.lisp" + for p in (a, b): + p.parent.mkdir(parents=True) + p.write_text("(defun init (x) (+ x 1))\n") + + ids_a = {n["id"] for n in extract_commonlisp(a)["nodes"]} + ids_b = {n["id"] for n in extract_commonlisp(b)["nodes"]} + + assert not (ids_a & ids_b), ( + f"same-named .lisp files in different dirs must not share ids, " + f"got overlap {sorted(ids_a & ids_b)}" + )