From 1eb356cde18481c9f293770f747eb74dc9e0f2c8 Mon Sep 17 00:00:00 2001 From: safishamsi Date: Tue, 18 Aug 2026 18:07:12 +0100 Subject: [PATCH] test(csharp): cover builtin-not-fabricated and struct primary-ctor (#2829) Adds the two cases the deep-dive flagged: a built-in-typed primary-ctor param (int count) must not fabricate a referenced node, and the branch's struct_declaration claim is locked by a struct primary-ctor test. Also adds the CHANGELOG entry. Co-Authored-By: Claude Opus 4.8 (1M context) --- CHANGELOG.md | 1 + tests/test_csharp_member_calls.py | 36 +++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index cf08f13b..7cfafde0 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.47 (unreleased) +- Fix: a C# 12 primary constructor's parameters are now walked, so `class Svc(IRepo repo)` emits the `references` edge to `IRepo` and calls through `repo` resolve — previously the class silently dropped its dependency; built-in and type-parameter types are not fabricated (#2829, thanks @brobl2008). - Fix: AST-derived INFERRED edges now carry a rubric `confidence_score` keyed to the relation (`uses` 0.95, `indirect_call`/unresolved cross-file `calls` 0.85) instead of landing at the rubric-forbidden 0.5 or a flat 0.8; the INFERRED default moves 0.5→0.55 so every score-less INFERRED edge is on the discrete rubric set (#2813, thanks @abhay-codes07). - Fix: a legacy `graph.json` that stored a numeric edge `confidence` (from a pre-enum version) no longer warns once per edge on every incremental reload; the numeric value is normalized to the `INFERRED` tag with the original float preserved in `confidence_score` (thanks @Trantor-develops). diff --git a/tests/test_csharp_member_calls.py b/tests/test_csharp_member_calls.py index 263f2311..7f319123 100644 --- a/tests/test_csharp_member_calls.py +++ b/tests/test_csharp_member_calls.py @@ -740,3 +740,39 @@ def test_primary_constructor_type_parameter_is_not_referenced(tmp_path): labels = {n["id"]: n["label"] for n in r["nodes"]} assert not [t for s, t in refs if s == holder and labels.get(t) == "T"], \ "a type parameter must not be emitted as a referenced type" + + +def test_primary_constructor_builtin_param_is_not_fabricated(tmp_path): + """A built-in-typed primary-ctor parameter (`int count`) must not fabricate a + referenced type node — only real type dependencies get a references edge.""" + _, r = _calls(tmp_path, { + "S.cs": ( + "public interface IDep { bool Plain(); }\n" + "public class Holder(IDep dep, int count) {\n" + " public bool Run() { return dep.Plain(); }\n" + "}\n" + ) + }) + holder = _find(r, "Holder", "holder") + refs = _refs(r) + idep = _find(r, "IDep", "idep") + assert (holder, idep) in refs, "the real dependency must still be referenced" + labels = {n["id"]: n["label"] for n in r["nodes"]} + assert not [t for s, t in refs if s == holder and labels.get(t) in ("int", "Int32")], \ + "a built-in parameter type must not become a referenced node" + + +def test_struct_primary_constructor_parameter_emits_references_edge(tmp_path): + """The branch also covers `struct` primary constructors, not just class/record.""" + _, r = _calls(tmp_path, { + "S.cs": ( + "public interface IDep { bool Plain(); }\n" + "public struct Holder(IDep dep) {\n" + " public bool Run() { return dep.Plain(); }\n" + "}\n" + ) + }) + holder = _find(r, "Holder", "holder") + idep = _find(r, "IDep", "idep") + assert (holder, idep) in _refs(r), \ + "a struct primary-constructor parameter type must produce a references edge"