Skip to content
7 changes: 5 additions & 2 deletions src/pipeline/pass_definitions.c
Original file line number Diff line number Diff line change
Expand Up @@ -521,13 +521,16 @@ static void process_def(cbm_pipeline_ctx_t *ctx, const CBMDefinition *def, const
* field resolution), Variable/Field (READS/WRITES resolution), and Table/View
* (SQL FROM/JOIN lineage). pass_parallel.c and pipeline_incremental.c seed
* through the same predicate, so the three registries cannot diverge.
* A generated member is registered by qualified name only, at all three. */
* A generated member is registered by qualified name only, at all three.
* The registry's test verdict is the extractor's OR the test-double path
* rule (cbm_registry_test_double_path) on the node's file, at all three. */
if (node_id > 0 && cbm_label_is_registry_symbol(def->label)) {
if (cbm_def_props_generated(def->extra_props)) {
cbm_registry_add_qn_only(ctx->registry, def->qualified_name, def->label);
} else {
cbm_registry_add_lang_in_module(ctx->registry, def->name, def->qualified_name,
def->label, lang, module_prefix_len);
def->label, lang, module_prefix_len,
def->is_test || cbm_registry_test_double_path(def_rel));
}
if (def->qn_sig_off) {
cbm_registry_set_swift_signature(ctx->registry, def->qualified_name,
Expand Down
13 changes: 8 additions & 5 deletions src/pipeline/pass_parallel.c
Original file line number Diff line number Diff line change
Expand Up @@ -1816,22 +1816,25 @@ int cbm_parallel_extract(cbm_pipeline_ctx_t *ctx, const cbm_file_info_t *files,
/* `file_node_id` is the defining file's node id (0 when it has none), looked up
* once per file by the caller: computing the file QN and finding its node for
* every definition was 700 k allocations and lookups on the Go corpus. */
static int register_and_link_def(cbm_pipeline_ctx_t *ctx, const CBMDefinition *def,
static int register_and_link_def(cbm_pipeline_ctx_t *ctx, const CBMDefinition *def, const char *rel,
int64_t file_node_id, CBMLanguage lang, size_t module_prefix_len,
int *reg_entries) {
int edges = 0;
if (!def->name || !def->qualified_name || !def->label) {
return 0;
}
/* Registry membership is defined ONCE by cbm_label_is_registry_symbol
* (helpers.c) — see pass_definitions.c for the per-label rationale, and
* for the generated member registered by qualified name only. */
* (helpers.c) — see pass_definitions.c for the per-label rationale, for
* the generated member registered by qualified name only, and for the
* test-double path rule, judged on the path the def's node stores. */
if (cbm_label_is_registry_symbol(def->label)) {
if (cbm_def_props_generated(def->extra_props)) {
cbm_registry_add_qn_only(ctx->registry, def->qualified_name, def->label);
} else {
const char *def_rel = def->file_path ? def->file_path : rel;
cbm_registry_add_lang_in_module(ctx->registry, def->name, def->qualified_name,
def->label, lang, module_prefix_len);
def->label, lang, module_prefix_len,
def->is_test || cbm_registry_test_double_path(def_rel));
}
if (def->qn_sig_off) {
cbm_registry_set_swift_signature(ctx->registry, def->qualified_name,
Expand Down Expand Up @@ -2050,7 +2053,7 @@ int cbm_build_registry_from_cache(cbm_pipeline_ctx_t *ctx, const cbm_file_info_t
: 0;
for (int d = 0; d < result->defs.count; d++) {
defines_edges +=
register_and_link_def(ctx, &result->defs.items[d], file_node_id,
register_and_link_def(ctx, &result->defs.items[d], rel, file_node_id,
files[i].language, py_prefix_len, &reg_entries);
}
}
Expand Down
6 changes: 3 additions & 3 deletions src/pipeline/pass_tests.c
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ static const char *itoa_log(int val) {
}

/* Check if a node has is_test:true in its properties JSON. */
static bool node_is_test(const cbm_gbuf_node_t *n) {
bool cbm_node_is_test(const cbm_gbuf_node_t *n) {
if (!n || !n->properties_json) {
return false;
}
Expand Down Expand Up @@ -270,13 +270,13 @@ static int create_tests_edges(cbm_pipeline_ctx_t *ctx) {
}

bool src_is_test =
node_is_test(src) || (src->file_path && cbm_is_test_path(src->file_path));
cbm_node_is_test(src) || (src->file_path && cbm_is_test_path(src->file_path));
if (!src_is_test) {
continue;
}

bool tgt_is_test =
node_is_test(tgt) || (tgt->file_path && cbm_is_test_path(tgt->file_path));
cbm_node_is_test(tgt) || (tgt->file_path && cbm_is_test_path(tgt->file_path));
if (tgt_is_test) {
continue;
}
Expand Down
23 changes: 19 additions & 4 deletions src/pipeline/pipeline.h
Original file line number Diff line number Diff line change
Expand Up @@ -283,9 +283,24 @@ cbm_registry_t *cbm_registry_new(void);
void cbm_registry_free(cbm_registry_t *r);

/* Register a function/method/class. All strings are copied. The entry's
* language is unknown, so name-based resolution never filters it out. */
* language is unknown, so name-based resolution never filters it out.
* `is_test` is the extractor's verdict for this symbol (CBMDefinition.is_test:
* the file's test-ness OR the definition's own test attributes). The registry
* stores it and scores with it; it does not derive test-ness from the QN. */
void cbm_registry_add(cbm_registry_t *r, const char *name, const char *qualified_name,
const char *label);
const char *label, bool is_test);

/* The test-double path rule, for the registry's ranking only: true when the
* repo-relative path of a symbol's defining file has a directory segment named
* exactly `fake`, `mock`, `mocks`, `testing` or `testutil`, or its file name
* matches `mock_*.go` or `fake_*.go`. Go projects keep generated fake
* clientsets and mock packages in ordinary files the extractor rightly does
* not call tests; a call should still prefer real code over them. The three
* registry seeders (pass_definitions.c, pass_parallel.c,
* pipeline_incremental.c) pass the extractor's verdict OR this, so full and
* incremental indexes rank alike. It never decides what a node is: the stored
* is_test property, TESTS edges and importance stay with cbm_is_test_path. */
bool cbm_registry_test_double_path(const char *rel_path);

/* Swift-only metadata and label-compatible overload selection. The returned
* QNs are borrowed from the registry and all belong to one base callable.
Expand All @@ -306,7 +321,7 @@ int cbm_registry_swift_candidates_in_file(const cbm_registry_t *r, const CBMCall
* function-local symbols: that needs the defining file's module QN, see
* cbm_registry_add_lang_in_module. */
void cbm_registry_add_lang(cbm_registry_t *r, const char *name, const char *qualified_name,
const char *label, CBMLanguage lang);
const char *label, CBMLanguage lang, bool is_test);

/* As cbm_registry_add_lang. A Python symbol declared inside a function body of
* its own file (cbm_registry_qn_is_function_local, bounded by
Expand All @@ -318,7 +333,7 @@ void cbm_registry_add_lang(cbm_registry_t *r, const char *name, const char *qual
* indexes agree. 0 = no local detection. */
void cbm_registry_add_lang_in_module(cbm_registry_t *r, const char *name,
const char *qualified_name, const char *label,
CBMLanguage lang, size_t module_prefix_len);
CBMLanguage lang, size_t module_prefix_len, bool is_test);

/* Register a symbol for exact (qualified-name) lookups only: it is never
* offered to the short-name strategies (unique_name, suffix_match,
Expand Down
48 changes: 45 additions & 3 deletions src/pipeline/pipeline_delta.c
Original file line number Diff line number Diff line change
Expand Up @@ -623,6 +623,28 @@ static char *delta_generated_props(const char *props, const char *generated) {
return out;
}

/* `props` (a JSON object) with the extractor's test verdict added as
* `"is_test":true` (CBM_MEM_CLASS_OTHER; the caller frees), NULL on allocation
* failure. The incremental registry seed reads the verdict back
* (cbm_node_is_test) so a re-resolved caller ranks a test definition below
* real code exactly as the full build does; an absent key reads as false. */
static char *delta_test_props(const char *props) {
static const char member[] = "\"is_test\":true";
size_t pl = props ? strlen(props) : 0;
bool has_members = pl > PAIR_LEN && props[0] == '{' && props[pl - SKIP_ONE] == '}';
size_t cap = pl + sizeof(member) + CBM_SZ_4;
char *out = (char *)cbm_alloc(CBM_MEM_CLASS_OTHER, cap);
if (!out) {
return NULL;
}
if (has_members) {
snprintf(out, cap, "%.*s,%s}", (int)(pl - SKIP_ONE), props, member);
} else {
snprintf(out, cap, "{%s}", member);
}
return out;
}

int64_t cbm_delta_preseed(cbm_store_t *store, const char *project, cbm_gbuf_t *gbuf) {
sqlite3 *db = cbm_store_get_db(store);
if (!db) {
Expand Down Expand Up @@ -673,7 +695,13 @@ int64_t cbm_delta_preseed(cbm_store_t *store, const char *project, cbm_gbuf_t *g
* And a generated member's mark (a Java record's implicit accessor, a
* Method node): the registry seed reads it to register the member by
* qualified name only, as the full build does (cbm_node_props_generated);
* without it the proxy came back into the short-name index. */
* without it the proxy came back into the short-name index.
*
* And the extractor's test verdict: the registry seed reads it back
* (cbm_node_is_test) to rank a test definition below real code, as the
* full build does with CBMDefinition.is_test; without it every proxy
* scored as production and a re-resolved caller could bind a test helper
* the full build ranks last. */
sqlite3_stmt *stmt = NULL;
if (sqlite3_prepare_v2(
db,
Expand Down Expand Up @@ -702,7 +730,8 @@ int64_t cbm_delta_preseed(cbm_store_t *store, const char *project, cbm_gbuf_t *g
" THEN json_extract(properties, '$.parent_class') END,"
" CASE WHEN label = 'Method'"
" AND instr(properties, '\"" CBM_PROP_GENERATED "\"') > 0"
" THEN json_extract(properties, '$." CBM_PROP_GENERATED "') END"
" THEN json_extract(properties, '$." CBM_PROP_GENERATED "') END,"
" instr(properties, '\"is_test\":true') > 0"
" FROM nodes"
" WHERE project = ?1 AND label NOT IN"
" ('Macro','Comment','Section','Branch','Commit','Tag')"
Expand All @@ -727,14 +756,27 @@ int64_t cbm_delta_preseed(cbm_store_t *store, const char *project, cbm_gbuf_t *g
int end_line = sqlite3_column_int(stmt, 7);
const char *parent = (const char *)sqlite3_column_text(stmt, 8);
const char *generated = (const char *)sqlite3_column_text(stmt, 9);
bool is_test = sqlite3_column_int(stmt, 10) != 0;
char *owner_props =
parent ? delta_owner_props(project, name, qn, parent, client_props) : NULL;
const char *props = owner_props ? owner_props : (client_props ? client_props : "{}");
char *generated_props = delta_generated_props(props, generated);
const char *seed_props = generated_props ? generated_props : props;
char *test_props = is_test ? delta_test_props(seed_props) : NULL;
if (is_test && !test_props) {
/* A proxy without its verdict would rank as production code;
* the run cannot be trusted, as with an id mismatch below. */
cbm_free(CBM_MEM_CLASS_OTHER, generated_props);
cbm_free(CBM_MEM_CLASS_OTHER, owner_props);
sqlite3_finalize(stmt);
cbm_log_error("delta.preseed_verdict_alloc_failed", "qn", qn ? qn : "");
return -1;
}
/* Pin the gbuf id to the database id: proxies ARE their rows. */
cbm_gbuf_set_next_id(gbuf, id);
int64_t got = cbm_gbuf_upsert_node(gbuf, label, name, qn, fp ? fp : "", start_line,
end_line, generated_props ? generated_props : props);
end_line, test_props ? test_props : seed_props);
cbm_free(CBM_MEM_CLASS_OTHER, test_props);
cbm_free(CBM_MEM_CLASS_OTHER, generated_props);
cbm_free(CBM_MEM_CLASS_OTHER, owner_props);
if (got != id) {
Expand Down
5 changes: 4 additions & 1 deletion src/pipeline/pipeline_incremental.c
Original file line number Diff line number Diff line change
Expand Up @@ -1396,8 +1396,11 @@ static void registry_visitor(const cbm_gbuf_node_t *node, void *userdata) {
size_t prefix_len = lang == CBM_LANG_PYTHON
? cbm_pipeline_fqn_symbol_prefix_len(seed->project, node->file_path)
: 0;
/* The full index's verdict: the extractor's (stored as is_test) OR the
* test-double path rule on the node's file (pass_definitions.c). */
bool is_test = cbm_node_is_test(node) || cbm_registry_test_double_path(node->file_path);
cbm_registry_add_lang_in_module(seed->registry, node->name, node->qualified_name, node->label,
lang, prefix_len);
lang, prefix_len, is_test);
if (node->properties_json) {
const char *mask = strstr(node->properties_json, "\"param_default_mask\":\"");
const char *count = strstr(node->properties_json, "\"selector_param_count\":");
Expand Down
6 changes: 6 additions & 0 deletions src/pipeline/pipeline_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -657,6 +657,12 @@ static inline bool cbm_pipeline_test_force_def_modules_null(const cbm_pipeline_c
typedef bool (*cbm_result_want_fn)(const CBMFileResult *header);
CBMFileResult *cbm_pipeline_result_acquire(const cbm_pipeline_ctx_t *ctx, CBMFileResult **cache,
int i, cbm_result_want_fn want, bool *loaded);
/* True when a graph-buffer node carries is_test:true in its properties JSON.
* The incremental registry seed has only the persisted node, not the
* CBMDefinition the definition passes hand to cbm_registry_add, and both must
* give the registry the same verdict for the same symbol. */
bool cbm_node_is_test(const cbm_gbuf_node_t *n);

void cbm_pipeline_result_release(CBMFileResult *r, bool loaded);

/* Log the store counters, close and delete the store, drop the latch. */
Expand Down
Loading
Loading