mirror of
https://github.com/DeusData/codebase-memory-mcp.git
synced 2026-11-03 08:03:38 +08:00
fix(extract): write C variants in graph_buffer's schema
Main's graph_buffer.c ("Definition variants") records a definition's
variant spans as [{"file_path","start_line","end_line"}], and the
test-impact engine reads that schema to find every TEST(...) branch of one
test. This branch's extractor wrote [{"start","end"}]. On the merge with
main, graph_buffer's same-QN merge found a non-empty variants list it could
not read and dropped every span, the definition's own included. The engine
then no longer found the second #if branch of TEST(alpha_uses_lib), so
test_impact_engine_variant_tests_are_one_test reported suite alpha
UNMAPPED, in CI on macOS and Windows and locally.
The extractor now writes the shared schema, with the file path
JSON-escaped. The C-preprocessor-only gate and the one-carrier rule are
unchanged, and the variants assertions use the shared format.
test_impact_engine, extraction, pipeline, graph_buffer and
conditional_variants: 857 passed.
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
This commit is contained in:
+21
-4
@@ -26,12 +26,14 @@
|
||||
#include "foundation/compat.h"
|
||||
#include "foundation/compat_fs.h" // cbm_fopen — crash-supervisor per-file marker write
|
||||
#include "foundation/hash_table.h" // CBMHashTable — crash-supervisor quarantine set
|
||||
#include "foundation/str_util.h" // cbm_json_escape — variants file_path
|
||||
#include "tree_sitter/api.h" // TSParser, TSNode, TSTree, TSInput, TSLanguage, TSPoint, TSParseOptions, TSParseState
|
||||
#include "foundation/constants.h"
|
||||
#include "mimalloc.h" // mi_malloc/mi_calloc/mi_realloc/mi_free/mi_usable_size — bind 3rd-party allocators (#424)
|
||||
#if defined(CBM_BIND_TS_ALLOCATOR) && CBM_BIND_TS_ALLOCATOR
|
||||
#include "sqlite3.h" // sqlite3_mem_methods, sqlite3_config, SQLITE_CONFIG_MALLOC — bind sqlite to mimalloc
|
||||
#endif
|
||||
#include <limits.h> // INT_MAX
|
||||
#include <stdint.h> // uint32_t, uint64_t, int64_t
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
@@ -2165,7 +2167,9 @@ static uint64_t cbm_variant_key(const char *qn, const char *label) {
|
||||
/* refs[0..n) is one (QN, label) group in span order: give every member the
|
||||
* list of the group's distinct spans. */
|
||||
static void cbm_variant_assign(CBMFileResult *result, const cbm_variant_ref_t *refs, int n) {
|
||||
enum { VARIANT_ENTRY_MAX = 40 }; /* ,{"start":4294967295,"end":4294967295} */
|
||||
/* ,{"file_path":"","start_line":4294967295,"end_line":4294967295} + path;
|
||||
* cbm_json_escape writes at most 6 bytes per source byte (\u00XX). */
|
||||
enum { VARIANT_ENTRY_FIXED = 64, JSON_ESCAPE_GROWTH = 6 };
|
||||
int distinct = 0;
|
||||
for (int i = 0; i < n; i++) {
|
||||
if (i == 0 || refs[i].start != refs[i - 1].start || refs[i].end != refs[i - 1].end) {
|
||||
@@ -2175,7 +2179,17 @@ static void cbm_variant_assign(CBMFileResult *result, const cbm_variant_ref_t *r
|
||||
if (distinct < 2) {
|
||||
return;
|
||||
}
|
||||
size_t cap = (size_t)distinct * VARIANT_ENTRY_MAX + 3;
|
||||
const char *path = result->defs.items[refs[0].idx].file_path;
|
||||
size_t path_cap = (path ? strlen(path) : 0) * JSON_ESCAPE_GROWTH + 1;
|
||||
if (path_cap > INT_MAX) {
|
||||
return;
|
||||
}
|
||||
char *esc = (char *)cbm_arena_alloc(&result->arena, path_cap);
|
||||
if (!esc) {
|
||||
return;
|
||||
}
|
||||
(void)cbm_json_escape(esc, (int)path_cap, path ? path : "");
|
||||
size_t cap = (size_t)distinct * (VARIANT_ENTRY_FIXED + strlen(esc)) + 3;
|
||||
char *json = (char *)cbm_arena_alloc(&result->arena, cap);
|
||||
if (!json) {
|
||||
return;
|
||||
@@ -2186,8 +2200,11 @@ static void cbm_variant_assign(CBMFileResult *result, const cbm_variant_ref_t *r
|
||||
if (i > 0 && refs[i].start == refs[i - 1].start && refs[i].end == refs[i - 1].end) {
|
||||
continue;
|
||||
}
|
||||
int w = snprintf(json + pos, cap - pos, "%s{\"start\":%u,\"end\":%u}", pos > 1 ? "," : "",
|
||||
refs[i].start, refs[i].end);
|
||||
/* graph_buffer.c's variants schema, so its same-QN merge and every
|
||||
* reader of the node (the test-impact engine) see one format. */
|
||||
int w = snprintf(json + pos, cap - pos,
|
||||
"%s{\"file_path\":\"%s\",\"start_line\":%u,\"end_line\":%u}",
|
||||
pos > 1 ? "," : "", esc, refs[i].start, refs[i].end);
|
||||
if (w <= 0 || (size_t)w >= cap - pos) {
|
||||
return; /* cannot happen: cap covers the longest entry */
|
||||
}
|
||||
|
||||
+3
-1
@@ -271,7 +271,9 @@ typedef struct {
|
||||
* (`#if`/`#else` twins, a macro redefined per platform, overloads): the
|
||||
* graph keeps one node per QN, and this lists every one of those
|
||||
* definitions' line spans, the surviving one included, as a JSON array
|
||||
* sorted by start line: [{"start":10,"end":14},{"start":20,"end":26}].
|
||||
* sorted by start line, in graph_buffer.c's variants schema:
|
||||
* [{"file_path":"a.c","start_line":10,"end_line":14},
|
||||
* {"file_path":"a.c","start_line":20,"end_line":26}].
|
||||
* Carried by the definition the graph keeps for the file (the last by
|
||||
* start line), which makes it the node's `variants` property. NULL on
|
||||
* every other definition, and in every language outside
|
||||
|
||||
@@ -7743,15 +7743,18 @@ TEST(extract_c_variants_lists_same_file_duplicates_c1) {
|
||||
ASSERT_NOT_NULL(kept);
|
||||
ASSERT_NOT_NULL(other);
|
||||
ASSERT_NOT_NULL(kept->variants);
|
||||
ASSERT_STR_EQ(kept->variants, "[{\"start\":2,\"end\":2},{\"start\":4,\"end\":6}]");
|
||||
ASSERT_STR_EQ(kept->variants, "[{\"file_path\":\"v.c\",\"start_line\":2,\"end_line\":2},"
|
||||
"{\"file_path\":\"v.c\",\"start_line\":4,\"end_line\":6}]");
|
||||
ASSERT_NULL(other->variants); /* one carrier per group: the node the graph keeps */
|
||||
|
||||
const CBMDefinition *lim_a = c1_def_at(r, "Macro", "LIMIT", 9);
|
||||
const CBMDefinition *lim_b = c1_def_at(r, "Macro", "LIMIT", 11);
|
||||
ASSERT_NOT_NULL(lim_a);
|
||||
ASSERT_NOT_NULL(lim_b);
|
||||
char want[128];
|
||||
snprintf(want, sizeof(want), "[{\"start\":%u,\"end\":%u},{\"start\":%u,\"end\":%u}]",
|
||||
char want[256];
|
||||
snprintf(want, sizeof(want),
|
||||
"[{\"file_path\":\"v.c\",\"start_line\":%u,\"end_line\":%u},"
|
||||
"{\"file_path\":\"v.c\",\"start_line\":%u,\"end_line\":%u}]",
|
||||
lim_a->start_line, lim_a->end_line, lim_b->start_line, lim_b->end_line);
|
||||
ASSERT_NOT_NULL(lim_b->variants);
|
||||
ASSERT_STR_EQ(lim_b->variants, want);
|
||||
|
||||
+10
-5
@@ -10536,9 +10536,12 @@ static c1_variants_obs_t c1_observe_variants(const char *repo, const char *db_na
|
||||
return obs;
|
||||
}
|
||||
obs.opened = true;
|
||||
obs.pick_lists_both = c1_props_contain(
|
||||
s, proj, "v.pick", "\"variants\":[{\"start\":2,\"end\":2},{\"start\":4,\"end\":6}]");
|
||||
obs.limit_has_list = c1_props_contain(s, proj, "v.LIMIT#macro", "\"variants\":[{\"start\":9,");
|
||||
obs.pick_lists_both =
|
||||
c1_props_contain(s, proj, "v.pick",
|
||||
"\"variants\":[{\"file_path\":\"v.c\",\"start_line\":2,\"end_line\":2},"
|
||||
"{\"file_path\":\"v.c\",\"start_line\":4,\"end_line\":6}]");
|
||||
obs.limit_has_list = c1_props_contain(s, proj, "v.LIMIT#macro",
|
||||
"\"variants\":[{\"file_path\":\"v.c\",\"start_line\":9,");
|
||||
obs.once_has_list = c1_props_contain(s, proj, "v.once", "\"variants\"");
|
||||
cbm_node_t *nodes = NULL;
|
||||
int count = 0;
|
||||
@@ -10680,8 +10683,10 @@ TEST(pipeline_cpp_overloads_are_listed_as_variants_c1) {
|
||||
int scale_nodes = n;
|
||||
int kept_start = n > 0 ? nodes[0].start_line : -1;
|
||||
cbm_store_free_nodes(nodes, n);
|
||||
bool lists_both = c1_props_contain(
|
||||
s, proj, "ov.scale", "\"variants\":[{\"start\":1,\"end\":1},{\"start\":2,\"end\":4}]");
|
||||
bool lists_both =
|
||||
c1_props_contain(s, proj, "ov.scale",
|
||||
"\"variants\":[{\"file_path\":\"ov.cpp\",\"start_line\":1,\"end_line\":1},"
|
||||
"{\"file_path\":\"ov.cpp\",\"start_line\":2,\"end_line\":4}]");
|
||||
bool once_has_list = c1_props_contain(s, proj, "ov.once", "\"variants\"");
|
||||
|
||||
cbm_store_close(s);
|
||||
|
||||
Reference in New Issue
Block a user