mirror of
https://github.com/DeusData/codebase-memory-mcp.git
synced 2026-11-04 10:48:20 +00:00
perf(rust-lsp): share the all_defs cross registry across null-filter files
Profiling the kernel showed 4 tiny trait-heavy rust files at ~90 s EACH in cross-file resolve. Live call trees put the time not in lookups but in REGISTRY CONSTRUCTION: cbm_pxc_filter_defs_for_file returns NULL for these files (their path-derived module QNs never match the def_module_qn index keys), so each one built its own registry over the ENTIRE project def set (millions of entries at kernel scale). Original premise corrected during implementation: only 4/87 rust files (on the kernel rust subtree) hit the NULL-filter path; 83/87 resolve against proper import-filtered subsets. A registry shared across ALL rust files would hand the subset files a superset and could add edges. The fix is therefore CONDITIONAL, byte-identical for every file by construction: - filter NULL -> ONE shared all_defs registry (built once in pipeline.c, finalized, read_only-sealed; module_qn=NULL is safe: an instrumented probe found 0 NULL def_module_qn across all_defs, so the per-file def_mod fallback never fired anyway). Kills the amplifier. - filter SUBSET -> today's per-file subset build, unchanged. The cross-crate Cargo manifest reaches the shared path through cbm_pxc_get_rust_manifest() (same worker-thread thread-local the fallback read), preserving cross-crate resolution exactly. Byte-identity verified empirically: single-threaded sorted edge-set diff on the kernel rust subtree, fix off vs on -- zero difference outside SEMANTICALLY_RELATED edges, which self-differ between two runs of the SAME unfixed binary (the pre-existing semantic non-determinism guarded by the repro_parallel_edge_determinism RED test). New suite test rustlsp_shared_registry_resolves_like_per_file exercises the shared path. Suites: rust_lsp 506, c_lsp 750, registry 54, pipeline 216, java_lsp 94, kotlin_lsp 78, php_lsp 278, store_nodes 56 -- zero failures. Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
This commit is contained in:
1 parent
c50c6ed9b9
commit
1cb0740c87
7 files changed
+252
-40
No files matched your search
+142
-40
@@ -5294,36 +5294,17 @@ void cbm_run_rust_lsp(CBMArena *arena, CBMFileResult *result, const char *source
|
||||
|
||||
extern const TSLanguage *tree_sitter_rust(void);
|
||||
|
||||
void cbm_run_rust_lsp_cross_with_manifest(CBMArena *arena, const char *source, int source_len,
|
||||
const char *module_qn, CBMRustLSPDef *defs, int def_count,
|
||||
const char **import_names, const char **import_qns,
|
||||
int import_count, TSTree *cached_tree,
|
||||
const struct CBMCargoManifest *manifest,
|
||||
CBMResolvedCallArray *out) {
|
||||
if (!source || source_len <= 0 || !out)
|
||||
return;
|
||||
|
||||
TSParser *parser = NULL;
|
||||
TSTree *tree = cached_tree;
|
||||
bool owns_tree = false;
|
||||
if (!tree) {
|
||||
parser = ts_parser_new();
|
||||
if (!parser)
|
||||
return;
|
||||
ts_parser_set_language(parser, tree_sitter_rust());
|
||||
tree = ts_parser_parse_string(parser, NULL, source, source_len);
|
||||
owns_tree = true;
|
||||
if (!tree) {
|
||||
ts_parser_delete(parser);
|
||||
return;
|
||||
}
|
||||
}
|
||||
TSNode root = ts_tree_root_node(tree);
|
||||
|
||||
/* Build registry from cross-file defs + stdlib. */
|
||||
CBMTypeRegistry reg;
|
||||
cbm_registry_init(®, arena);
|
||||
cbm_rust_stdlib_register(®, arena);
|
||||
/* Populate + finalize a Rust cross-file type registry from `defs`. Shared by the
|
||||
* per-file resolver (cbm_run_rust_lsp_cross_with_manifest) and the build-once shared
|
||||
* registry (cbm_rust_build_cross_registry) so both produce a byte-identical registry.
|
||||
* `module_qn` is ONLY the fallback used to qualify a def's return type when that def
|
||||
* carries no def_module_qn; pass NULL for the shared build (all_defs always carry
|
||||
* def_module_qn — verified: 0 NULL across the C + Rust kernel corpora). */
|
||||
static void rust_populate_cross_registry(CBMTypeRegistry *reg, CBMArena *arena,
|
||||
CBMRustLSPDef *defs, int def_count,
|
||||
const char *module_qn) {
|
||||
cbm_registry_init(reg, arena);
|
||||
cbm_rust_stdlib_register(reg, arena);
|
||||
|
||||
for (int i = 0; i < def_count; i++) {
|
||||
CBMRustLSPDef *d = &defs[i];
|
||||
@@ -5340,7 +5321,7 @@ void cbm_run_rust_lsp_cross_with_manifest(CBMArena *arena, const char *source, i
|
||||
rt.short_name = cbm_arena_strdup(arena, d->short_name);
|
||||
rt.is_interface = d->is_interface || strcmp(d->label, "Trait") == 0 ||
|
||||
strcmp(d->label, "Interface") == 0;
|
||||
cbm_registry_add_type(®, rt);
|
||||
cbm_registry_add_type(reg, rt);
|
||||
}
|
||||
|
||||
if (strcmp(d->label, "Function") == 0 || strcmp(d->label, "Method") == 0) {
|
||||
@@ -5381,25 +5362,25 @@ void cbm_run_rust_lsp_cross_with_manifest(CBMArena *arena, const char *source, i
|
||||
|
||||
if (strcmp(d->label, "Method") == 0 && d->receiver_type && d->receiver_type[0]) {
|
||||
rf.receiver_type = cbm_arena_strdup(arena, d->receiver_type);
|
||||
if (!cbm_registry_lookup_type(®, rf.receiver_type)) {
|
||||
if (!cbm_registry_lookup_type(reg, rf.receiver_type)) {
|
||||
CBMRegisteredType auto_t;
|
||||
memset(&auto_t, 0, sizeof(auto_t));
|
||||
auto_t.qualified_name = rf.receiver_type;
|
||||
const char *dot = strrchr(d->receiver_type, '.');
|
||||
auto_t.short_name = dot ? cbm_arena_strdup(arena, dot + 1) : rf.receiver_type;
|
||||
cbm_registry_add_type(®, auto_t);
|
||||
cbm_registry_add_type(reg, auto_t);
|
||||
}
|
||||
}
|
||||
|
||||
cbm_registry_add_func(®, rf);
|
||||
cbm_registry_add_func(reg, rf);
|
||||
|
||||
/* If trait_qn set: encode embedded_type linkage on receiver. */
|
||||
if (rf.receiver_type && d->trait_qn && d->trait_qn[0]) {
|
||||
CBMRegisteredType *rt = NULL;
|
||||
for (int ti = 0; ti < reg.type_count; ti++) {
|
||||
if (reg.types[ti].qualified_name &&
|
||||
strcmp(reg.types[ti].qualified_name, rf.receiver_type) == 0) {
|
||||
rt = ®.types[ti];
|
||||
for (int ti = 0; ti < reg->type_count; ti++) {
|
||||
if (reg->types[ti].qualified_name &&
|
||||
strcmp(reg->types[ti].qualified_name, rf.receiver_type) == 0) {
|
||||
rt = ®->types[ti];
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -5420,9 +5401,130 @@ void cbm_run_rust_lsp_cross_with_manifest(CBMArena *arena, const char *source, i
|
||||
}
|
||||
}
|
||||
|
||||
/* Run the per-file walker. */
|
||||
/* Finalise the cross-file registry now that all defs are added. */
|
||||
cbm_registry_finalize(®);
|
||||
cbm_registry_finalize(reg);
|
||||
}
|
||||
|
||||
/* Resolve one Rust file against an ALREADY-built (per-file or shared) registry. */
|
||||
static void rust_resolve_against_registry(CBMArena *arena, const char *source, int source_len,
|
||||
const char *module_qn, const CBMTypeRegistry *reg,
|
||||
const char **import_names, const char **import_qns,
|
||||
int import_count, TSNode root,
|
||||
const struct CBMCargoManifest *manifest,
|
||||
CBMResolvedCallArray *out, CBMFileResult *result) {
|
||||
RustLSPContext ctx;
|
||||
rust_lsp_init(&ctx, arena, source, source_len, reg, module_qn, out);
|
||||
ctx.cargo_manifest = manifest;
|
||||
rust_collect_uses(&ctx, root);
|
||||
for (int i = 0; i < import_count; i++) {
|
||||
if (import_names[i] && import_qns[i]) {
|
||||
rust_lsp_add_use(&ctx, import_names[i], import_qns[i]);
|
||||
}
|
||||
}
|
||||
rust_lsp_process_file(&ctx, root);
|
||||
if (result)
|
||||
cbm_rust_synth_proc_macro_edges(arena, result);
|
||||
}
|
||||
|
||||
/* Tier-2: build the Rust cross registry ONCE from all project defs, sealed
|
||||
* read-only, and shared across every Rust file's resolve (mirrors C/py/cs/ts).
|
||||
* Converts the pipeline's CBMLSPDef into CBMRustLSPDef inline (same field copy as
|
||||
* pass_lsp_cross.c's pxc_lspdefs_to_rust, incl. trait_qn=NULL). module_qn=NULL is
|
||||
* byte-identical because all_defs always carry def_module_qn. */
|
||||
CBMTypeRegistry *cbm_rust_build_cross_registry(CBMArena *arena, CBMLSPDef *defs, int def_count) {
|
||||
if (!arena)
|
||||
return NULL;
|
||||
CBMTypeRegistry *reg = (CBMTypeRegistry *)cbm_arena_alloc(arena, sizeof(*reg));
|
||||
if (!reg)
|
||||
return NULL;
|
||||
CBMRustLSPDef *rdefs = NULL;
|
||||
if (def_count > 0) {
|
||||
rdefs = (CBMRustLSPDef *)cbm_arena_alloc(arena, (size_t)def_count * sizeof(CBMRustLSPDef));
|
||||
if (!rdefs)
|
||||
return NULL;
|
||||
for (int i = 0; i < def_count; i++) {
|
||||
rdefs[i].qualified_name = defs[i].qualified_name;
|
||||
rdefs[i].short_name = defs[i].short_name;
|
||||
rdefs[i].label = defs[i].label;
|
||||
rdefs[i].receiver_type = defs[i].receiver_type;
|
||||
rdefs[i].def_module_qn = defs[i].def_module_qn;
|
||||
rdefs[i].return_types = defs[i].return_types;
|
||||
rdefs[i].embedded_types = defs[i].embedded_types;
|
||||
rdefs[i].field_defs = defs[i].field_defs;
|
||||
rdefs[i].method_names_str = defs[i].method_names_str;
|
||||
rdefs[i].trait_qn = NULL;
|
||||
rdefs[i].is_interface = defs[i].is_interface;
|
||||
}
|
||||
}
|
||||
rust_populate_cross_registry(reg, arena, rdefs, def_count, /*module_qn=*/NULL);
|
||||
reg->read_only = true; /* seal: shared Tier-2 registry is read-only during resolve */
|
||||
return reg;
|
||||
}
|
||||
|
||||
/* Cross-file Rust resolve using a pre-built shared registry (Tier-2). Skips the
|
||||
* per-file registry build; just parse + resolve. Mirrors cbm_run_c_lsp_cross_with_registry. */
|
||||
void cbm_run_rust_lsp_cross_with_registry(CBMArena *arena, const char *source, int source_len,
|
||||
const char *module_qn, const CBMTypeRegistry *reg,
|
||||
const char **import_names, const char **import_qns,
|
||||
int import_count, TSTree *cached_tree,
|
||||
const struct CBMCargoManifest *manifest,
|
||||
CBMResolvedCallArray *out, CBMFileResult *result) {
|
||||
if (!source || source_len <= 0 || !out || !reg)
|
||||
return;
|
||||
TSParser *parser = NULL;
|
||||
TSTree *tree = cached_tree;
|
||||
bool owns_tree = false;
|
||||
if (!tree) {
|
||||
parser = ts_parser_new();
|
||||
if (!parser)
|
||||
return;
|
||||
ts_parser_set_language(parser, tree_sitter_rust());
|
||||
tree = ts_parser_parse_string(parser, NULL, source, source_len);
|
||||
owns_tree = true;
|
||||
if (!tree) {
|
||||
ts_parser_delete(parser);
|
||||
return;
|
||||
}
|
||||
}
|
||||
TSNode root = ts_tree_root_node(tree);
|
||||
rust_resolve_against_registry(arena, source, source_len, module_qn, reg, import_names,
|
||||
import_qns, import_count, root, manifest, out, result);
|
||||
if (owns_tree) {
|
||||
ts_tree_delete(tree);
|
||||
if (parser)
|
||||
ts_parser_delete(parser);
|
||||
}
|
||||
}
|
||||
|
||||
void cbm_run_rust_lsp_cross_with_manifest(CBMArena *arena, const char *source, int source_len,
|
||||
const char *module_qn, CBMRustLSPDef *defs, int def_count,
|
||||
const char **import_names, const char **import_qns,
|
||||
int import_count, TSTree *cached_tree,
|
||||
const struct CBMCargoManifest *manifest,
|
||||
CBMResolvedCallArray *out) {
|
||||
if (!source || source_len <= 0 || !out)
|
||||
return;
|
||||
|
||||
TSParser *parser = NULL;
|
||||
TSTree *tree = cached_tree;
|
||||
bool owns_tree = false;
|
||||
if (!tree) {
|
||||
parser = ts_parser_new();
|
||||
if (!parser)
|
||||
return;
|
||||
ts_parser_set_language(parser, tree_sitter_rust());
|
||||
tree = ts_parser_parse_string(parser, NULL, source, source_len);
|
||||
owns_tree = true;
|
||||
if (!tree) {
|
||||
ts_parser_delete(parser);
|
||||
return;
|
||||
}
|
||||
}
|
||||
TSNode root = ts_tree_root_node(tree);
|
||||
|
||||
/* Build registry from cross-file defs + stdlib (per-file). */
|
||||
CBMTypeRegistry reg;
|
||||
rust_populate_cross_registry(®, arena, defs, def_count, module_qn);
|
||||
|
||||
RustLSPContext ctx;
|
||||
rust_lsp_init(&ctx, arena, source, source_len, ®, module_qn, out);
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#include "scope.h"
|
||||
#include "type_registry.h"
|
||||
#include "../cbm.h"
|
||||
#include "go_lsp.h" // for CBMLSPDef (pipeline def), used by the Tier-2 builder
|
||||
|
||||
/* Forward declaration — defined in rust_cargo.h. We keep it forward
|
||||
* here to avoid pulling rust_cargo.h into every consumer that only
|
||||
@@ -296,6 +297,22 @@ void cbm_run_rust_lsp_cross_with_manifest(CBMArena *arena, const char *source, i
|
||||
const struct CBMCargoManifest *manifest,
|
||||
CBMResolvedCallArray *out);
|
||||
|
||||
/* Tier-2: build the project-wide Rust cross registry ONCE from all defs, finalize,
|
||||
* and seal read-only. Shared across every Rust file's resolve (mirrors C/py/cs/ts).
|
||||
* Def-driven → byte-identical entries to the per-file build. */
|
||||
CBMTypeRegistry *cbm_rust_build_cross_registry(CBMArena *arena, CBMLSPDef *defs, int def_count);
|
||||
|
||||
/* Cross-file Rust resolve using a pre-built shared registry (Tier-2). Skips the
|
||||
* per-file registry build; just parse + resolve. `manifest` = the same Cargo manifest
|
||||
* the per-file path uses (cross-crate #56). `result` may be NULL (the cross path does
|
||||
* not synthesise proc-macro edges). Mirrors cbm_run_c_lsp_cross_with_registry. */
|
||||
void cbm_run_rust_lsp_cross_with_registry(CBMArena *arena, const char *source, int source_len,
|
||||
const char *module_qn, const CBMTypeRegistry *reg,
|
||||
const char **import_names, const char **import_qns,
|
||||
int import_count, TSTree *cached_tree,
|
||||
const struct CBMCargoManifest *manifest,
|
||||
CBMResolvedCallArray *out, CBMFileResult *result);
|
||||
|
||||
/* Per-file input for batch cross-file Rust LSP processing. */
|
||||
typedef struct {
|
||||
const char *source;
|
||||
|
||||
@@ -492,6 +492,10 @@ void cbm_pxc_set_rust_manifest(const CBMCargoManifest *m) {
|
||||
g_pxc_rust_manifest = m;
|
||||
}
|
||||
|
||||
const struct CBMCargoManifest *cbm_pxc_get_rust_manifest(void) {
|
||||
return g_pxc_rust_manifest;
|
||||
}
|
||||
|
||||
/* Convert a CBMLSPDef array (the pipeline's lingua franca, go_lsp.h:73)
|
||||
* into a CBMRustLSPDef array (rust_lsp.h) inside `arena`. The two structs
|
||||
* share their first 9 string fields; CBMRustLSPDef adds `trait_qn` before
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
#include "lsp/c_lsp.h" /* cbm_c_build_cross_registry / cbm_run_c_lsp_cross_with_registry */
|
||||
#include "lsp/cs_lsp.h" /* cbm_cs_build_cross_registry / cbm_run_cs_lsp_cross_with_registry */
|
||||
#include "lsp/ts_lsp.h" /* cbm_ts_build_cross_registry / cbm_run_ts_lsp_cross_with_registry */
|
||||
#include "lsp/rust_lsp.h" /* cbm_rust_build_cross_registry / cbm_run_rust_lsp_cross_with_registry */
|
||||
#include "pipeline/pipeline_internal.h"
|
||||
#include <stdbool.h>
|
||||
|
||||
@@ -107,6 +108,7 @@ typedef struct {
|
||||
CBMTypeRegistry *ts; /* CBM_LANG_JAVASCRIPT, TYPESCRIPT, TSX */
|
||||
CBMTypeRegistry *php; /* CBM_LANG_PHP */
|
||||
CBMTypeRegistry *cs; /* CBM_LANG_CSHARP */
|
||||
CBMTypeRegistry *rust; /* CBM_LANG_RUST */
|
||||
} CBMCrossLspRegistries;
|
||||
|
||||
/* Return the appropriate pre-built registry for a language, or NULL
|
||||
@@ -132,11 +134,19 @@ static inline CBMTypeRegistry *cbm_pxc_registry_for_lang(const CBMCrossLspRegist
|
||||
return r->php;
|
||||
case CBM_LANG_CSHARP:
|
||||
return r->cs;
|
||||
case CBM_LANG_RUST:
|
||||
return r->rust;
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Borrow the (thread-local) Rust Cargo manifest the cross-file LSP pass set for
|
||||
* cross-crate (#56) routing. The Tier-2 prebuilt Rust resolve reads it so it sees
|
||||
* exactly what the per-file fallback (cbm_pxc_run_one) would on the same thread. */
|
||||
struct CBMCargoManifest;
|
||||
const struct CBMCargoManifest *cbm_pxc_get_rust_manifest(void);
|
||||
|
||||
/* Run the cross-file LSP resolver for non-TS languages. Appends
|
||||
* resolved CALLS into r->resolved_calls (lives in r->arena). Caller
|
||||
* owns source, module_qn, all_defs, imp_keys, imp_vals.
|
||||
|
||||
@@ -2738,6 +2738,36 @@ static void resolve_worker(int worker_id, void *ctx_ptr) {
|
||||
result->cached_tree, &result->resolved_calls);
|
||||
used_prebuilt = true;
|
||||
break;
|
||||
case CBM_LANG_RUST: {
|
||||
/* Byte-identity-preserving Tier-2. The rust per-file build uses
|
||||
* all_defs ONLY when the filter returns NULL (the ~90s amplifier
|
||||
* files, e.g. rust kernel modules); those share the once-built all_defs
|
||||
* registry. Files that filter to a SUBSET (the majority) resolve
|
||||
* against that subset today, so they keep their per-file build —
|
||||
* feeding them the shared all_defs registry would be a SUPERSET and
|
||||
* could change the graph. (See report cc799e17.) Both paths read the
|
||||
* same worker-thread manifest as the fallback. */
|
||||
CBMLSPDef *r_filtered = NULL;
|
||||
int r_fc = 0;
|
||||
if (rc->module_def_index) {
|
||||
r_filtered = cbm_pxc_filter_defs_for_file(
|
||||
rc->module_def_index, rc->all_defs, lang, result->namespace_name,
|
||||
def_module, imp_vals, imp_count, &r_fc);
|
||||
}
|
||||
if (!r_filtered) {
|
||||
cbm_run_rust_lsp_cross_with_registry(
|
||||
&result->arena, lsp_source, lsp_source_len, def_module, prebuilt,
|
||||
imp_keys, imp_vals, imp_count, result->cached_tree,
|
||||
cbm_pxc_get_rust_manifest(), &result->resolved_calls,
|
||||
/*result=*/NULL);
|
||||
} else {
|
||||
cbm_pxc_run_one(lang, result, lsp_source, lsp_source_len, def_module,
|
||||
r_filtered, r_fc, imp_keys, imp_vals, imp_count);
|
||||
free(r_filtered);
|
||||
}
|
||||
used_prebuilt = true;
|
||||
break;
|
||||
}
|
||||
case CBM_LANG_CSHARP:
|
||||
cbm_run_cs_lsp_cross_with_registry(
|
||||
&result->arena, lsp_source, lsp_source_len, def_module, prebuilt,
|
||||
|
||||
@@ -903,6 +903,13 @@ static int run_parallel_pipeline(cbm_pipeline_t *p, cbm_pipeline_ctx_t *ctx,
|
||||
cross_registries.c = cbm_c_build_cross_registry(&cross_lsp_arena, all_defs, def_count);
|
||||
cross_registries.cs = cbm_cs_build_cross_registry(&cross_lsp_arena, all_defs, def_count);
|
||||
cross_registries.ts = cbm_ts_build_cross_registry(&cross_lsp_arena, all_defs, def_count);
|
||||
cross_registries.rust = cbm_rust_build_cross_registry(&cross_lsp_arena, all_defs, def_count);
|
||||
if (cross_registries.rust) {
|
||||
char _rsb[96];
|
||||
snprintf(_rsb, sizeof(_rsb), "types=%d funcs=%d", cross_registries.rust->type_count,
|
||||
cross_registries.rust->func_count);
|
||||
cbm_log_info("cross_lsp.rust_registry", "scale", _rsb);
|
||||
}
|
||||
}
|
||||
cbm_log_info("pass.timing", "pass", "lsp_cross_prepare", "elapsed_ms",
|
||||
itoa_buf((int)elapsed_ms(*t)));
|
||||
|
||||
@@ -518,6 +518,47 @@ TEST(rustlsp_crossfile_method_dispatch) {
|
||||
PASS();
|
||||
}
|
||||
|
||||
/* F4 Tier-2: the build-once shared registry path (cbm_rust_build_cross_registry +
|
||||
* cbm_run_rust_lsp_cross_with_registry) must resolve IDENTICALLY to the per-file
|
||||
* cbm_run_rust_lsp_cross — same fixture as rustlsp_crossfile_method_dispatch. Also
|
||||
* asserts the registry is sealed read-only. */
|
||||
TEST(rustlsp_shared_registry_resolves_like_per_file) {
|
||||
const char *caller = "fn run(d: &demo::Database) { d.query(\"select\"); }\n";
|
||||
CBMArena a;
|
||||
cbm_arena_init(&a);
|
||||
|
||||
CBMLSPDef defs[2];
|
||||
memset(defs, 0, sizeof(defs));
|
||||
defs[0].qualified_name = "test.demo.Database";
|
||||
defs[0].short_name = "Database";
|
||||
defs[0].label = "Type";
|
||||
defs[0].def_module_qn = "test.demo";
|
||||
defs[1].qualified_name = "test.demo.Database.query";
|
||||
defs[1].short_name = "query";
|
||||
defs[1].label = "Method";
|
||||
defs[1].receiver_type = "test.demo.Database";
|
||||
defs[1].def_module_qn = "test.demo";
|
||||
defs[1].return_types = "alloc.string.String";
|
||||
|
||||
const char *imp_names[] = {"demo"};
|
||||
const char *imp_qns[] = {"test::demo"};
|
||||
|
||||
CBMTypeRegistry *reg = cbm_rust_build_cross_registry(&a, defs, 2);
|
||||
ASSERT_NOT_NULL(reg);
|
||||
ASSERT_TRUE(reg->read_only);
|
||||
|
||||
CBMResolvedCallArray out;
|
||||
memset(&out, 0, sizeof(out));
|
||||
cbm_run_rust_lsp_cross_with_registry(&a, caller, (int)strlen(caller), "test.caller", reg,
|
||||
imp_names, imp_qns, 1, NULL, /*manifest=*/NULL, &out,
|
||||
/*result=*/NULL);
|
||||
|
||||
ASSERT_GTE(find_confident(&out, "run", "Database.query"), 0);
|
||||
|
||||
cbm_arena_destroy(&a);
|
||||
PASS();
|
||||
}
|
||||
|
||||
TEST(rustlsp_crossfile_free_function) {
|
||||
const char *caller = "fn main() { utils::greet(\"x\"); }\n";
|
||||
CBMArena a;
|
||||
@@ -5973,6 +6014,7 @@ void suite_rust_lsp(void) {
|
||||
|
||||
/* Cross-file */
|
||||
RUN_TEST(rustlsp_crossfile_method_dispatch);
|
||||
RUN_TEST(rustlsp_shared_registry_resolves_like_per_file);
|
||||
RUN_TEST(rustlsp_crossfile_free_function);
|
||||
|
||||
/* Robustness */
|
||||
|
||||
Reference in new issue
Block a user