|
1 | 1 | /* |
2 | | - * repro_issue56.c — Reproduce-first case for OPEN bug #56. |
| 2 | + * repro_issue56.c — production-pipeline regression for Rust cross-crate calls. |
3 | 3 | * |
4 | | - * Bug #56: "Cross-crate call graphs stop at boundaries" (Rust) |
| 4 | + * The fixture is a two-member Cargo workspace. crate_b calls |
| 5 | + * `crate_a::helper()` and also declares a same-named local helper, so generic |
| 6 | + * bare-name resolution cannot accidentally satisfy the assertion. The test |
| 7 | + * requires a persisted CALLS edge into crate_a's namespace. |
5 | 8 | * |
6 | | - * ROOT CAUSE (pipeline / Rust LSP path): |
7 | | - * The tree-sitter-only Rust extractor has no access to Cargo metadata |
8 | | - * at extraction time, so when it sees `crate_a::helper()` inside |
9 | | - * crate_b, it records a raw call-site for the path but has no registry |
10 | | - * entry for `crate_a::helper` — only the definitions in the *same file* |
11 | | - * were seeded. The LSP resolver therefore cannot match the call-site to |
12 | | - * a callee QN across the crate boundary, and the resulting |
13 | | - * CBMResolvedCall is either absent or marked with low confidence and |
14 | | - * discarded. When the pipeline writes graph edges for this project, no |
15 | | - * CALLS edge is minted for the cross-crate call — the call graph stops |
16 | | - * at the crate edge. |
17 | | - * |
18 | | - * v0.8.1 added a hybrid-LSP Rust path that "materially improves" this |
19 | | - * (issue comment, maintainer 2026-06-25), but the reporter was asked to |
20 | | - * retest; the issue remains OPEN because no retest confirming resolution |
21 | | - * was provided. The workspace-member wiring test |
22 | | - * (rustlsp_extra_cargo_wires_workspace_member in test_rust_lsp.c) only |
23 | | - * exercises the *single-file LSP* layer with a manually-parsed manifest; |
24 | | - * it does NOT verify that the full production pipeline (rh_index_files → |
25 | | - * cbm_pipeline → graph store) persists a cross-crate CALLS edge for a |
26 | | - * real multi-file Cargo workspace fixture. That gap is what this test |
27 | | - * fills. |
28 | | - * |
29 | | - * FIXTURE: |
30 | | - * A minimal Cargo workspace with two crates: |
31 | | - * |
32 | | - * [workspace Cargo.toml] — workspace root, declares members |
33 | | - * crate_a/Cargo.toml — library crate "crate_a" |
34 | | - * crate_a/src/lib.rs — exposes `pub fn helper() {}` |
35 | | - * crate_b/Cargo.toml — binary crate "crate_b", depends on crate_a |
36 | | - * crate_b/src/main.rs — calls `crate_a::helper()` from `fn run()`; |
37 | | - * also defines a LOCAL `fn helper()` to break |
38 | | - * bare-name uniqueness (see note below) |
39 | | - * |
40 | | - * The only meaningful cross-crate CALLS edge is: |
41 | | - * crate_b::run → crate_a::helper |
42 | | - * |
43 | | - * EXPECTED (correct) behaviour: |
44 | | - * After indexing the workspace through the production MCP pipeline, the |
45 | | - * graph store must contain at least one CALLS edge whose TARGET node's |
46 | | - * qualified_name contains "crate_a" (i.e. routes into the crate_a |
47 | | - * namespace, not into crate_b's local helper). |
48 | | - * |
49 | | - * ACTUAL (buggy) behaviour: |
50 | | - * The pipeline extracts both files, but the cross-crate path |
51 | | - * `crate_a::helper` in crate_b/src/main.rs is not resolved to a graph |
52 | | - * node in crate_a because Cargo workspace member metadata is not |
53 | | - * plumbed into the per-file extraction phase. Result: zero CALLS edges |
54 | | - * to the crate_a namespace. |
55 | | - * |
56 | | - * WHY THIS IS RED ON CURRENT CODE (even post-v0.8.1): |
57 | | - * The rustlsp_extra_cargo_wires_workspace_member unit test exercises only |
58 | | - * the LSP layer (cbm_run_rust_lsp_with_manifest called with a parsed |
59 | | - * CBMCargoManifest) and confirms the resolver *can* route |
60 | | - * `engine::boot()` to `engine.boot` when given the manifest explicitly. |
61 | | - * BUT: the production pipeline's per-file extraction path |
62 | | - * (cbm_extract_file → cbm_run_rust_lsp) does NOT receive a pre-parsed |
63 | | - * workspace manifest — it only gets the individual file's content. |
64 | | - * Additionally, cbm_pxc_has_cross_lsp() returns false for CBM_LANG_RUST |
65 | | - * (pass_lsp_cross.c), so the cross-file LSP pass is never invoked for |
66 | | - * Rust. Therefore a real workspace indexed through index_repository |
67 | | - * produces no CALLS edges crossing into crate_a, and this test is RED. |
68 | | - * |
69 | | - * WHY THE OLD >= 2 COUNT TEST FALSE-PASSED: |
70 | | - * With a unique `helper` name in the project (one definition in crate_a, |
71 | | - * no other `helper` anywhere), the generic pipeline name resolver |
72 | | - * (registry.c, resolve_name_lookup) resolves `crate_a::helper` to the |
73 | | - * sole `helper` candidate by bare-name suffix scoring — WITHOUT needing |
74 | | - * any cross-crate workspace metadata. This produced calls >= 2 (the |
75 | | - * intra-file main→run plus the bare-name-resolved run→helper), making |
76 | | - * the old ASSERT_GTE(calls, 2) GREEN even though the bug was not fixed. |
77 | | - * |
78 | | - * Fix: add a LOCAL `fn helper()` in crate_b/src/main.rs so there are |
79 | | - * now TWO `helper` candidates in the project registry. The generic |
80 | | - * resolver either picks the wrong one (crate_b-local) or abstains |
81 | | - * (ambiguous). Only a correctly crate-qualified resolver routes |
82 | | - * `crate_a::helper` specifically to crate_a's node. The assertion then |
83 | | - * checks the TARGET node's qualified_name contains "crate_a" — a count |
84 | | - * check is no longer sufficient because the local helper also contributes |
85 | | - * a CALLS edge (run_local→helper). |
86 | | - * |
87 | | - * UNCERTAINTY: |
88 | | - * If a future version plumbs workspace metadata or wires Rust lsp_cross |
89 | | - * correctly, this test will go GREEN — that is the intended outcome. |
| 9 | + * The direct parallel regression lives in test_parallel.c because the bug was |
| 10 | + * specific to worker-local pipeline context: the root Cargo manifest was |
| 11 | + * parsed by the sequential cross-LSP driver but was not propagated into |
| 12 | + * parallel resolve workers. A confident local result could also suppress the |
| 13 | + * manifest-aware cross pass. This repro remains as an end-to-end guard for the |
| 14 | + * same graph contract through the production harness. |
90 | 15 | */ |
91 | 16 |
|
92 | 17 | #include "test_framework.h" |
|
108 | 33 | * CALLS edge's TARGET node has a qualified_name containing "crate_a", |
109 | 34 | * proving the cross-crate boundary was traversed. |
110 | 35 | * |
111 | | - * RED condition: |
112 | | - * No CALLS edge whose target QN contains "crate_a" exists in the store. |
113 | | - * |
114 | | - * This test is RED on current code because: |
115 | | - * 1. cbm_run_rust_lsp is called with NULL manifest (cbm.c:645), so no |
116 | | - * workspace metadata is available at extraction time. |
117 | | - * 2. cbm_pxc_has_cross_lsp returns false for CBM_LANG_RUST |
118 | | - * (pass_lsp_cross.c:281), so the cross-file LSP pass never runs for |
119 | | - * Rust and cannot seed crate_a defs into crate_b's resolver context. |
120 | | - * 3. With two `helper` candidates (crate_a and crate_b-local), the |
121 | | - * generic resolver's qualified_suffix_match fails (neither QN ends |
122 | | - * with ".crate_a.helper") and bare-name scoring picks the crate_b- |
123 | | - * local one or abstains, never routing to crate_a. |
| 36 | + * Regression condition: no CALLS edge whose target QN contains "crate_a" |
| 37 | + * exists in the store. |
124 | 38 | */ |
125 | 39 | TEST(repro_issue56_cross_crate_calls) { |
126 | 40 | /* |
|
0 commit comments