mirror of
https://github.com/DeusData/codebase-memory-mcp.git
synced 2026-11-06 08:35:21 +08:00
Merge pull request #2083 from DeusData/fix/semantic-edges-qsort-null-guard
fix(pipeline): skip the function sort when the graph has no functions
This commit is contained in:
@@ -992,8 +992,12 @@ static int phase1_scan_functions(cbm_gbuf_t *gbuf, cbm_sem_func_t **out_funcs,
|
||||
* sequence — so an unstable order changes WHICH semantic edges are
|
||||
* emitted. Sort the cheap pointer array by qualified name (unique) and
|
||||
* re-derive the three fields set so far; the heavy per-func payloads are
|
||||
* filled in later phases, so no 12.7 KB structs are moved. */
|
||||
qsort(node_ptrs, (size_t)func_count, sizeof(node_ptrs[0]), cmp_node_ptr_by_qn);
|
||||
* filled in later phases, so no 12.7 KB structs are moved.
|
||||
* A graph with no Function/Method nodes never allocates node_ptrs, and
|
||||
* qsort's base is declared nonnull (glibc), so skip the sort outright. */
|
||||
if (func_count > 0) {
|
||||
qsort(node_ptrs, (size_t)func_count, sizeof(node_ptrs[0]), cmp_node_ptr_by_qn);
|
||||
}
|
||||
for (int k = 0; k < func_count; k++) {
|
||||
funcs[k].node_id = node_ptrs[k]->id;
|
||||
funcs[k].file_path = node_ptrs[k]->file_path;
|
||||
|
||||
@@ -13409,6 +13409,54 @@ TEST(pipeline_markdown_and_config_prose_reaches_fts_body) {
|
||||
PASS();
|
||||
}
|
||||
|
||||
/* A graph with no Function or Method nodes at all -- a struct-only or
|
||||
* config-only project -- must run pass_semantic_edges cleanly. Its phase-1
|
||||
* scan used to hand qsort() a NULL base with count 0, before the func_count
|
||||
* early-out in phase 1b could run. glibc declares qsort's base nonnull, so
|
||||
* UBSan on the Linux leg reports that call; the macOS SDK carries no such
|
||||
* attribute, so the sanitizer is silent there. The label counts pin the
|
||||
* fixture to what it claims: at least one Struct and zero Function/Method,
|
||||
* i.e. the scan genuinely finds nothing to sort. */
|
||||
TEST(pipeline_semantic_edges_no_functions) {
|
||||
char tmp[256];
|
||||
snprintf(tmp, sizeof(tmp), "/tmp/cbm_nofunc_XXXXXX");
|
||||
ASSERT_NOT_NULL(cbm_mkdtemp(tmp));
|
||||
char path[512];
|
||||
char dbpath[512];
|
||||
snprintf(dbpath, sizeof(dbpath), "%s/nofunc.db", tmp);
|
||||
|
||||
snprintf(path, sizeof(path), "%s/main.go", tmp);
|
||||
ASSERT_EQ(th_write_file(path, "package main\n\ntype Widget struct{}\n"), 0);
|
||||
|
||||
cbm_pipeline_t *p = cbm_pipeline_new(tmp, dbpath, CBM_MODE_FULL);
|
||||
ASSERT_NOT_NULL(p);
|
||||
ASSERT_EQ(cbm_pipeline_run(p), 0);
|
||||
cbm_pipeline_free(p);
|
||||
|
||||
cbm_store_t *s = cbm_store_open_path(dbpath);
|
||||
ASSERT_NOT_NULL(s);
|
||||
sqlite3_stmt *st = NULL;
|
||||
ASSERT_EQ(sqlite3_prepare_v2(cbm_store_get_db(s), "SELECT COUNT(*) FROM nodes WHERE label = ?1",
|
||||
-1, &st, NULL),
|
||||
SQLITE_OK);
|
||||
const char *labels[] = {"Struct", "Function", "Method"};
|
||||
int counts[3] = {0, 0, 0};
|
||||
for (size_t i = 0; i < sizeof(labels) / sizeof(labels[0]); i++) {
|
||||
sqlite3_reset(st);
|
||||
sqlite3_bind_text(st, 1, labels[i], -1, SQLITE_TRANSIENT);
|
||||
ASSERT_EQ(sqlite3_step(st), SQLITE_ROW);
|
||||
counts[i] = sqlite3_column_int(st, 0);
|
||||
}
|
||||
sqlite3_finalize(st);
|
||||
cbm_store_close(s);
|
||||
ASSERT_GT(counts[0], 0);
|
||||
ASSERT_EQ(counts[1], 0);
|
||||
ASSERT_EQ(counts[2], 0);
|
||||
|
||||
th_rmtree(tmp);
|
||||
PASS();
|
||||
}
|
||||
|
||||
#if defined(CBM_COVERAGE_MARKER_TEST_API) && CBM_COVERAGE_MARKER_TEST_API
|
||||
/* Join two Studio Export range strings and hand back the result. The caller
|
||||
* owns nothing: the string lives in the aggregate's arena, so copy it out
|
||||
@@ -13827,6 +13875,7 @@ SUITE(pipeline) {
|
||||
RUN_TEST(pipeline_ensemble_routing_unterminated_item_is_safe);
|
||||
RUN_TEST(pipeline_delta_patch_indexes_docstring_into_fts_body);
|
||||
RUN_TEST(pipeline_markdown_and_config_prose_reaches_fts_body);
|
||||
RUN_TEST(pipeline_semantic_edges_no_functions);
|
||||
}
|
||||
|
||||
/* Focused semantic-manifest and publication contracts. Kept separate from the
|
||||
|
||||
Reference in New Issue
Block a user