From 356b50df6faf415fad876e3737d72c0f953c9935 Mon Sep 17 00:00:00 2001 From: safishamsi Date: Mon, 17 Aug 2026 18:42:21 +0100 Subject: [PATCH] test(java): make the annotation no-dup test effective + add string/enum guard (#2426) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The no-dup test asserted on _edge_labels (a set), so count()==1 was trivially true whether or not the dedup ran. Count raw edge occurrences (normalized labels) so a regressed dedup — @Uses({X, X}) emitting 2 edges — actually fails the test. Adds a guard that string/enum annotation arguments (@RequestMapping( "/x"), @Retention(RetentionPolicy.RUNTIME)) do not fabricate type refs. Adds the CHANGELOG entry. Co-Authored-By: Claude Opus 4.8 (1M context) --- CHANGELOG.md | 1 + tests/test_languages.py | 35 +++++++++++++++++++++++++++++------ 2 files changed, 30 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f19adb7..9453fa4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ Full release notes with details on each version: [GitHub Releases](https://githu ## 0.9.46 (unreleased) +- Fix: Java annotations now emit `references` edges to their type — including class-literal arguments (`@Repeatable(Foo.class)`, `@Uses({A.class, B.class})`) and annotation-member return types — so a container annotation is no longer a disconnected island; string/enum arguments are not mistaken for type references (#2426, thanks @oleksii-tumanov). - Fix: `graphify query` treats `_` as a token separator (like `-`), so an underscore-spelled query (`user_service`) matches a hyphenated label (`user-service`); coverage-scaling keeps the broader tokenization from surfacing unrelated single-token noise (#2473, thanks @nadiadatepe-eng). - Fix: the `post-checkout` hook skips its rebuild when HEAD is unchanged (e.g. `git checkout -b` with no start point), so creating a branch no longer triggers a full graph rebuild (#2421, thanks @nothariharan). - Feature: Markdown nodes now carry a `node_kind` (`page` vs `heading`) attribute so a docs corpus can be filtered by kind, and leading YAML frontmatter is parsed onto the page node as bounded, sanitized attributes; a `#` comment inside frontmatter is no longer mis-extracted as a heading (thanks @evanthomasgelders). Node ids are unchanged, so existing markdown graphs are not re-keyed. diff --git a/tests/test_languages.py b/tests/test_languages.py index fa97697..870bac5 100644 --- a/tests/test_languages.py +++ b/tests/test_languages.py @@ -625,13 +625,36 @@ def test_java_annotation_references_are_not_duplicated(tmp_path): result = extract([annotation, consumer], cache_root=tmp_path / "cache") - refs = [ - pair - for pair in _edge_labels(result, "references", "attribute") - if pair in {("Consumer", "Uses"), ("apply", "Uses")} + # Count RAW edge occurrences, not _edge_labels (which returns a set and would + # trivially satisfy count()==1 whether or not the dedup ran). @Uses({X, X}) + # names the same class literal twice, so without dedup this would be 2. + lab = {n["id"]: _normalize_symbol_label(n["label"]) for n in result["nodes"]} + raw_pairs = [ + (lab.get(e["source"]), lab.get(e["target"])) + for e in result["edges"] + if e["relation"] == "references" and e.get("context") == "attribute" ] - assert refs.count(("Consumer", "Uses")) == 1 - assert refs.count(("apply", "Uses")) == 1 + assert raw_pairs.count(("Consumer", "Uses")) == 1, raw_pairs + assert raw_pairs.count(("apply", "Uses")) == 1, raw_pairs + + +def test_java_annotation_string_and_enum_args_are_not_type_refs(tmp_path): + """Only class-literal args (Foo.class) are type references. String and + enum-constant annotation arguments must NOT fabricate reference edges.""" + source = tmp_path / "Config.java" + source.write_text( + "class Config {\n" + ' @RequestMapping("/users")\n' + " @Retention(RetentionPolicy.RUNTIME)\n" + " void handle() {}\n" + "}\n" + ) + result = extract_java(source) + ref_targets = {t for _s, t in _edge_labels(result, "references", "attribute")} + # the string literal and the enum path must not become type-reference targets + assert "/users" not in ref_targets + assert "RUNTIME" not in ref_targets + assert "RetentionPolicy.RUNTIME" not in ref_targets def test_java_annotation_class_literal_keeps_qualified_type_identity(tmp_path):