mirror of
https://github.com/DeusData/codebase-memory-mcp.git
synced 2026-11-04 10:48:20 +00:00
fix: preserve Cargo context in parallel Rust resolution
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
This commit is contained in:
1 parent
909051ccee
commit
0f1e65d625
5 files changed
+255
-107
No files matched your search
@@ -351,6 +351,12 @@ static cbm_gbuf_t *run_parallel_with_extract_opts_and_mutator(
|
||||
|
||||
harness_ctx_free_tables(&ctx);
|
||||
cbm_registry_free(reg);
|
||||
/* cbm_parallel_extract installs a process-global package map whose
|
||||
* production owner is the surrounding pipeline lifecycle. This direct
|
||||
* pass harness owns that lifecycle itself, so mirror production teardown
|
||||
* before returning the graph to the test. */
|
||||
cbm_pkgmap_free(cbm_pipeline_get_pkgmap());
|
||||
cbm_pipeline_set_pkgmap(NULL);
|
||||
return gbuf;
|
||||
}
|
||||
|
||||
@@ -925,6 +931,25 @@ static bool callable_has_call_target_fragment(const cbm_gbuf_t *gbuf, const char
|
||||
return false;
|
||||
}
|
||||
|
||||
static const cbm_gbuf_edge_t *find_call_edge_to_target_fragment(const cbm_gbuf_t *gbuf,
|
||||
const char *source_tail,
|
||||
const char *target_fragment) {
|
||||
const cbm_gbuf_node_t *source = find_unique_callable_node_by_tail(gbuf, source_tail);
|
||||
if (!source || !target_fragment)
|
||||
return NULL;
|
||||
const cbm_gbuf_edge_t **edges = NULL;
|
||||
int count = 0;
|
||||
if (cbm_gbuf_find_edges_by_source_type(gbuf, source->id, "CALLS", &edges, &count) != 0)
|
||||
return NULL;
|
||||
for (int i = 0; i < count; i++) {
|
||||
const cbm_gbuf_node_t *target =
|
||||
edges[i] ? cbm_gbuf_find_by_id(gbuf, edges[i]->target_id) : NULL;
|
||||
if (target && target->qualified_name && strstr(target->qualified_name, target_fragment))
|
||||
return edges[i];
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int count_calls_edges_to_tail(const cbm_gbuf_t *gbuf, const char *target_tail) {
|
||||
const cbm_gbuf_node_t *target = find_unique_callable_node_by_tail(gbuf, target_tail);
|
||||
if (!target)
|
||||
@@ -2534,6 +2559,138 @@ TEST(parallel_kotlin_nonbinary_operator_carriers_reach_graph) {
|
||||
PASS();
|
||||
}
|
||||
|
||||
/* Build the smallest real Cargo workspace that exercises the direct parallel
|
||||
* resolve API. crate_a is a declared workspace member, while unlisted is a
|
||||
* graph-visible decoy with the same helper name. That decoy is intentional:
|
||||
* without manifest-aware routing, a bare-name match cannot accidentally make
|
||||
* the test green. The optional crate_b-local helper pins the second failure
|
||||
* mode where a confident in-file resolution used to suppress cross-LSP. */
|
||||
static cbm_gbuf_t *run_issue56_parallel_workspace(bool local_decoy) {
|
||||
static const char workspace_toml[] =
|
||||
"[workspace]\n"
|
||||
"members = [\"crate_a\", \"crate_b\"]\n"
|
||||
"resolver = \"2\"\n";
|
||||
static const char crate_a_toml[] =
|
||||
"[package]\nname = \"crate_a\"\nversion = \"0.1.0\"\nedition = \"2021\"\n";
|
||||
static const char crate_b_toml[] =
|
||||
"[package]\nname = \"crate_b\"\nversion = \"0.1.0\"\nedition = \"2021\"\n"
|
||||
"\n[dependencies]\ncrate_a = { path = \"../crate_a\" }\n";
|
||||
static const char crate_a_source[] = "pub fn helper() {}\n";
|
||||
static const char unlisted_source[] = "pub fn helper() {}\n";
|
||||
static const char caller_without_local[] =
|
||||
"fn run() { crate_a::helper(); }\n"
|
||||
"fn main() { run(); }\n";
|
||||
static const char caller_with_local[] =
|
||||
"fn helper() {}\n"
|
||||
"fn run() { crate_a::helper(); }\n"
|
||||
"fn main() { run(); }\n";
|
||||
|
||||
char tmpdir[256];
|
||||
snprintf(tmpdir, sizeof(tmpdir), "/tmp/cbm_issue56_parallel_XXXXXX");
|
||||
if (!cbm_mkdtemp(tmpdir))
|
||||
return NULL;
|
||||
|
||||
char crate_a[512];
|
||||
char crate_a_src[512];
|
||||
char crate_b[512];
|
||||
char crate_b_src[512];
|
||||
char unlisted[512];
|
||||
char unlisted_src[512];
|
||||
snprintf(crate_a, sizeof(crate_a), "%s/crate_a", tmpdir);
|
||||
snprintf(crate_a_src, sizeof(crate_a_src), "%s/src", crate_a);
|
||||
snprintf(crate_b, sizeof(crate_b), "%s/crate_b", tmpdir);
|
||||
snprintf(crate_b_src, sizeof(crate_b_src), "%s/src", crate_b);
|
||||
snprintf(unlisted, sizeof(unlisted), "%s/unlisted", tmpdir);
|
||||
snprintf(unlisted_src, sizeof(unlisted_src), "%s/src", unlisted);
|
||||
if (cbm_mkdir(crate_a) != 0 || cbm_mkdir(crate_a_src) != 0 || cbm_mkdir(crate_b) != 0 ||
|
||||
cbm_mkdir(crate_b_src) != 0 || cbm_mkdir(unlisted) != 0 || cbm_mkdir(unlisted_src) != 0) {
|
||||
th_rmtree(tmpdir);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char root_manifest[512];
|
||||
char crate_a_manifest[512];
|
||||
char crate_b_manifest[512];
|
||||
char crate_a_file[512];
|
||||
char crate_b_file[512];
|
||||
char unlisted_file[512];
|
||||
snprintf(root_manifest, sizeof(root_manifest), "%s/Cargo.toml", tmpdir);
|
||||
snprintf(crate_a_manifest, sizeof(crate_a_manifest), "%s/Cargo.toml", crate_a);
|
||||
snprintf(crate_b_manifest, sizeof(crate_b_manifest), "%s/Cargo.toml", crate_b);
|
||||
snprintf(crate_a_file, sizeof(crate_a_file), "%s/lib.rs", crate_a_src);
|
||||
snprintf(crate_b_file, sizeof(crate_b_file), "%s/main.rs", crate_b_src);
|
||||
snprintf(unlisted_file, sizeof(unlisted_file), "%s/lib.rs", unlisted_src);
|
||||
if (th_write_file(root_manifest, workspace_toml) != 0 ||
|
||||
th_write_file(crate_a_manifest, crate_a_toml) != 0 ||
|
||||
th_write_file(crate_b_manifest, crate_b_toml) != 0 ||
|
||||
th_write_file(crate_a_file, crate_a_source) != 0 ||
|
||||
th_write_file(crate_b_file, local_decoy ? caller_with_local : caller_without_local) != 0 ||
|
||||
th_write_file(unlisted_file, unlisted_source) != 0) {
|
||||
th_rmtree(tmpdir);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cbm_discover_opts_t opts = {.mode = CBM_MODE_FULL};
|
||||
cbm_file_info_t *files = NULL;
|
||||
int file_count = 0;
|
||||
if (cbm_discover(tmpdir, &opts, &files, &file_count) != 0 || file_count < 3) {
|
||||
cbm_discover_free(files, file_count);
|
||||
th_rmtree(tmpdir);
|
||||
return NULL;
|
||||
}
|
||||
cbm_gbuf_t *gbuf =
|
||||
run_parallel("cbm_issue56_parallel", tmpdir, files, file_count, 2 /* real workers */);
|
||||
cbm_discover_free(files, file_count);
|
||||
th_rmtree(tmpdir);
|
||||
return gbuf;
|
||||
}
|
||||
|
||||
TEST(parallel_rust_cross_crate_worker_receives_workspace_manifest) {
|
||||
cbm_gbuf_t *gbuf = run_issue56_parallel_workspace(false);
|
||||
ASSERT_NOT_NULL(gbuf);
|
||||
|
||||
const cbm_gbuf_edge_t *correct =
|
||||
find_call_edge_to_target_fragment(gbuf, "main.run", ".crate_a.");
|
||||
const bool correct_found = correct != NULL;
|
||||
const bool unlisted =
|
||||
callable_has_call_target_fragment(gbuf, "main.run", ".unlisted.");
|
||||
const bool manifest_strategy =
|
||||
correct && correct->properties_json && strstr(correct->properties_json, "lsp_cross_crate");
|
||||
if (!correct || unlisted || !manifest_strategy) {
|
||||
printf(" issue56 manifest diagnostic: correct=%d unlisted=%d strategy=%d\n",
|
||||
correct_found, unlisted, manifest_strategy);
|
||||
}
|
||||
cbm_gbuf_free(gbuf);
|
||||
|
||||
ASSERT_TRUE(correct_found);
|
||||
ASSERT_FALSE(unlisted);
|
||||
ASSERT_TRUE(manifest_strategy);
|
||||
PASS();
|
||||
}
|
||||
|
||||
TEST(parallel_rust_cross_crate_manifest_beats_confident_local_resolution) {
|
||||
cbm_gbuf_t *gbuf = run_issue56_parallel_workspace(true);
|
||||
ASSERT_NOT_NULL(gbuf);
|
||||
|
||||
const cbm_gbuf_edge_t *correct =
|
||||
find_call_edge_to_target_fragment(gbuf, "main.run", ".crate_a.");
|
||||
const bool correct_found = correct != NULL;
|
||||
const bool wrong_local =
|
||||
callable_has_call_target_fragment(gbuf, "main.run", ".crate_b.");
|
||||
const bool manifest_strategy =
|
||||
correct && correct->properties_json && strstr(correct->properties_json, "lsp_cross_crate");
|
||||
if (!correct || wrong_local || !manifest_strategy) {
|
||||
printf(" issue56 local-shadow diagnostic: correct=%d wrong_local=%d strategy=%d\n",
|
||||
correct_found, wrong_local, manifest_strategy);
|
||||
}
|
||||
cbm_gbuf_free(gbuf);
|
||||
|
||||
ASSERT_TRUE(correct_found);
|
||||
ASSERT_FALSE(wrong_local);
|
||||
ASSERT_TRUE(manifest_strategy);
|
||||
PASS();
|
||||
}
|
||||
|
||||
/* A known external macro semantic owns its parser occurrence even when the
|
||||
* external target is not materialized as a graph node. It must not fall back
|
||||
* to a same-named local function in either pipeline. */
|
||||
@@ -3926,6 +4083,8 @@ SUITE(parallel) {
|
||||
RUN_TEST(parallel_tsx_import_namespace_exact_parity);
|
||||
RUN_TEST(parallel_kotlin_external_protocol_does_not_use_project_class_method_tail);
|
||||
RUN_TEST(parallel_kotlin_nonbinary_operator_carriers_reach_graph);
|
||||
RUN_TEST(parallel_rust_cross_crate_worker_receives_workspace_manifest);
|
||||
RUN_TEST(parallel_rust_cross_crate_manifest_beats_confident_local_resolution);
|
||||
RUN_TEST(parallel_rust_known_macro_does_not_fallback_to_local_function);
|
||||
RUN_TEST(parallel_rust_proc_macros_are_decorates_and_usage_only);
|
||||
RUN_TEST(parallel_c_preprocessed_coordinate_collision_preserves_hidden_target);
|
||||
|
||||
Reference in new issue
Block a user