test(java): make the annotation no-dup test effective + add string/enum guard (#2426)

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) <noreply@anthropic.com>
This commit is contained in:
safishamsi
2026-08-17 18:42:21 +01:00
co-authored by Claude Opus 4.8
parent eadef76a68
commit 356b50df6f
2 changed files with 30 additions and 6 deletions
+1
View File
@@ -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.
+29 -6
View File
@@ -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):