mirror of
https://github.com/DeusData/codebase-memory-mcp.git
synced 2026-11-15 13:56:34 +08:00
Merge pull request #2427 from BobbieBarker/fix/registry-test-bit
fix(registry): score a candidate on the extractor's test verdict, not its name
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -1816,7 +1816,7 @@ 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;
|
||||
@@ -1824,14 +1824,17 @@ static int register_and_link_def(cbm_pipeline_ctx_t *ctx, const CBMDefinition *d
|
||||
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,
|
||||
@@ -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, ®_entries);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
+19
-4
@@ -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.
|
||||
@@ -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
|
||||
@@ -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,
|
||||
|
||||
@@ -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) {
|
||||
@@ -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,
|
||||
@@ -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')"
|
||||
@@ -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) {
|
||||
|
||||
@@ -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\":");
|
||||
|
||||
@@ -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. */
|
||||
|
||||
+151
-141
@@ -30,6 +30,7 @@ enum { REG_MAX_CANDIDATES = 256 };
|
||||
#include "foundation/compat.h" /* CBM_TLS */
|
||||
#include "foundation/hash_table.h"
|
||||
#include "foundation/arena.h"
|
||||
#include "foundation/log.h"
|
||||
#include "foundation/dyn_array.h"
|
||||
#include "foundation/mem_core.h"
|
||||
#include "foundation/platform.h"
|
||||
@@ -81,16 +82,16 @@ const char *cbm_confidence_band(double score) {
|
||||
|
||||
/* ── Internal types ──────────────────────────────────────────────── */
|
||||
|
||||
/* Array of QN strings for byName index, with the test/mock verdict of each
|
||||
* name cached beside it: the scorer asked is_test_qn for every candidate of
|
||||
* every unresolved call, and the answer depends only on the name, which never
|
||||
* changes once registered (waste sanitizer scaling lane, 2026-09-17). Field
|
||||
* order and names match CBM_DYN_ARRAY(char *) so cbm_da_push works on it. */
|
||||
/* Array of QN strings for byName index, with each entry's test verdict beside
|
||||
* it. The verdict is the extractor's, handed to cbm_registry_add by the caller
|
||||
* that already computed it from the file path and the definition's attributes;
|
||||
* it is stored rather than derived, so every entry always has one. Field order
|
||||
* and names match CBM_DYN_ARRAY(char *) so cbm_da_push works on it. */
|
||||
typedef struct {
|
||||
char **items;
|
||||
int count;
|
||||
int cap;
|
||||
uint8_t *is_test; /* is_test_qn(items[i]), one byte per entry */
|
||||
uint8_t *is_test; /* the caller's verdict for items[i], one byte per entry */
|
||||
int is_test_cap;
|
||||
/* Language of the file that defined items[i] (a CBMLanguage; CBM_LANG_COUNT
|
||||
* = unknown, compatible with every caller), one byte per entry. Grown in
|
||||
@@ -204,84 +205,64 @@ static int common_prefix_len(const char *a, const char *b) {
|
||||
|
||||
enum { REG_TEST_PENALTY = 1000 };
|
||||
|
||||
/* Check if a qualified name looks like a test/mock path: does it contain any of
|
||||
* Test, test, Mock, mock, Stub, stub, Fake, fake, Fixture, spec? One pass that
|
||||
* dispatches on the first character instead of ten strstr scans of the whole
|
||||
* name (this ran for every candidate of every unresolved call: the scaling
|
||||
* lane's x4.0, waste sanitizer 2026-09-17). */
|
||||
static bool is_test_qn(const char *qn) {
|
||||
if (!qn) {
|
||||
return false;
|
||||
}
|
||||
for (const char *p = qn; *p; p++) {
|
||||
switch (*p) {
|
||||
case 'T':
|
||||
if (strncmp(p, "Test", 4) == 0) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case 't':
|
||||
if (strncmp(p, "test", 4) == 0) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case 'M':
|
||||
if (strncmp(p, "Mock", 4) == 0) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case 'm':
|
||||
if (strncmp(p, "mock", 4) == 0) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case 'S':
|
||||
if (strncmp(p, "Stub", 4) == 0) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case 's':
|
||||
if (strncmp(p, "stub", 4) == 0 || strncmp(p, "spec", 4) == 0) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case 'F':
|
||||
if (strncmp(p, "Fake", 4) == 0 || strncmp(p, "Fixture", 7) == 0) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case 'f':
|
||||
if (strncmp(p, "fake", 4) == 0) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
/* A directory segment that, by name alone, holds test doubles: Go's generated
|
||||
* fake clientsets and listers (pkg/client/.../fake), mock packages, and the
|
||||
* testing / testutil helper packages. Whole segments only, compared exactly. */
|
||||
static bool test_double_dir_segment(const char *seg, size_t len) {
|
||||
static const char *const dirs[] = {"fake", "mock", "mocks", "testing", "testutil"};
|
||||
for (size_t i = 0; i < sizeof(dirs) / sizeof(dirs[0]); i++) {
|
||||
if (strlen(dirs[i]) == len && memcmp(seg, dirs[i], len) == 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* The cached test verdicts, only when they cover every entry: a failed growth
|
||||
* leaves the cache short, and then the scorer asks per candidate as before. */
|
||||
static const uint8_t *qn_test_flags(const qn_array_t *arr) {
|
||||
return arr->is_test && arr->is_test_cap >= arr->count ? arr->is_test : NULL;
|
||||
/* `base` is a Go file named `prefix`*.go. */
|
||||
static bool go_file_with_prefix(const char *base, const char *prefix) {
|
||||
static const char go_ext[] = ".go";
|
||||
size_t blen = strlen(base);
|
||||
size_t plen = strlen(prefix);
|
||||
size_t elen = sizeof(go_ext) - SKIP_ONE;
|
||||
return blen >= plen + elen && strncmp(base, prefix, plen) == 0 &&
|
||||
strcmp(base + blen - elen, go_ext) == 0;
|
||||
}
|
||||
|
||||
/* The cached candidate languages, under the same coverage rule: both side
|
||||
* arrays grow together and share is_test_cap. */
|
||||
bool cbm_registry_test_double_path(const char *rel_path) {
|
||||
if (!rel_path) {
|
||||
return false;
|
||||
}
|
||||
const char *seg = rel_path;
|
||||
for (const char *slash = strchr(seg, '/'); slash; slash = strchr(seg, '/')) {
|
||||
if (test_double_dir_segment(seg, (size_t)(slash - seg))) {
|
||||
return true;
|
||||
}
|
||||
seg = slash + SKIP_ONE;
|
||||
}
|
||||
/* `seg` is the file name now. */
|
||||
return go_file_with_prefix(seg, "mock_") || go_file_with_prefix(seg, "fake_");
|
||||
}
|
||||
|
||||
/* The stored test verdicts. index_under_name drops an entry it cannot record a
|
||||
* verdict for, so this covers every entry of a non-empty bucket. */
|
||||
static const uint8_t *qn_test_flags(const qn_array_t *arr) {
|
||||
return arr->is_test;
|
||||
}
|
||||
|
||||
/* The stored candidate languages. Both side arrays grow together and share
|
||||
* is_test_cap, and index_under_name drops an entry that does not fit both, so
|
||||
* this covers every entry of a non-empty bucket too. */
|
||||
static const uint8_t *qn_lang_flags(const qn_array_t *arr) {
|
||||
return arr->lang && arr->is_test_cap >= arr->count ? arr->lang : NULL;
|
||||
return arr->lang;
|
||||
}
|
||||
|
||||
/* Score a candidate for tiebreaking. Higher = better.
|
||||
* Layer 1: Non-test code preferred over test code (+1000)
|
||||
* Layer 2: Namespace proximity via common prefix length (+plen)
|
||||
* `is_test` is the cached verdict when the caller has one (-1: ask). */
|
||||
static int candidate_score(const char *candidate_qn, const char *module_qn, int is_test) {
|
||||
* `is_test` is the verdict stored for this candidate at registration. */
|
||||
static int candidate_score(const char *candidate_qn, const char *module_qn, bool is_test) {
|
||||
int score = 0;
|
||||
bool test = is_test >= 0 ? is_test != 0 : is_test_qn(candidate_qn);
|
||||
if (!test) {
|
||||
if (!is_test) {
|
||||
score += REG_TEST_PENALTY;
|
||||
}
|
||||
score += common_prefix_len(candidate_qn, module_qn);
|
||||
@@ -351,9 +332,10 @@ static bool candidate_outranks_on_tie(const cbm_registry_t *r, const char *candi
|
||||
}
|
||||
|
||||
/* Pick candidate with highest composite score (test-deprioritization + namespace
|
||||
* proximity), ties settled by candidate_outranks_on_tie. `is_test_flags` and
|
||||
* `lang_flags` are the candidates' cached test verdicts and languages, or NULL
|
||||
* when the caller has none (a filtered subset carries its own copies). */
|
||||
* proximity), ties settled by candidate_outranks_on_tie. `is_test_flags` are
|
||||
* the candidates' stored test verdicts, always given: every by-name entry has
|
||||
* one, and a filtered subset carries its own copies. `lang_flags` are their
|
||||
* languages, or NULL when the caller has none. */
|
||||
static const char *best_by_import_distance(const cbm_registry_t *r, const char **candidates,
|
||||
const uint8_t *is_test_flags, const uint8_t *lang_flags,
|
||||
int count, const char *module_qn) {
|
||||
@@ -361,8 +343,7 @@ static const char *best_by_import_distance(const cbm_registry_t *r, const char *
|
||||
uint8_t best_lang = (uint8_t)CBM_LANG_COUNT;
|
||||
int best_score = CBM_NOT_FOUND;
|
||||
for (int i = 0; i < count; i++) {
|
||||
int score =
|
||||
candidate_score(candidates[i], module_qn, is_test_flags ? (int)is_test_flags[i] : -1);
|
||||
int score = candidate_score(candidates[i], module_qn, is_test_flags[i] != 0);
|
||||
uint8_t lang = lang_flags ? lang_flags[i] : (uint8_t)CBM_LANG_COUNT;
|
||||
if (score > best_score ||
|
||||
(score == best_score && best &&
|
||||
@@ -1719,11 +1700,11 @@ void cbm_registry_free(cbm_registry_t *r) {
|
||||
/* ── Registration ────────────────────────────────────────────────── */
|
||||
|
||||
/* Record `owned_qn` in the by-name bucket for `key`, keeping the bucket's
|
||||
* is_test flags in step with its entries.
|
||||
* is_test and lang side arrays in step with its entries.
|
||||
*
|
||||
* No array dedup needed: cbm_registry_add's exact-map check guarantees the QN
|
||||
* is new, and it calls this at most once per distinct key. */
|
||||
static void index_under_name(cbm_registry_t *r, const char *key, const char *owned_qn,
|
||||
static void index_under_name(cbm_registry_t *r, const char *key, const char *owned_qn, bool is_test,
|
||||
CBMLanguage lang) {
|
||||
qn_array_t *arr = cbm_ht_get(r->by_name, key);
|
||||
if (!arr) {
|
||||
@@ -1733,7 +1714,7 @@ static void index_under_name(cbm_registry_t *r, const char *key, const char *own
|
||||
int before = arr->count;
|
||||
cbm_da_push(arr, (char *)owned_qn);
|
||||
if (arr->count == before) {
|
||||
return; /* the name could not be recorded: no verdict to cache */
|
||||
return; /* the name could not be recorded: no verdict to store */
|
||||
}
|
||||
if (arr->count > arr->is_test_cap) {
|
||||
int want = arr->cap > 0 ? arr->cap : arr->count;
|
||||
@@ -1744,19 +1725,25 @@ static void index_under_name(cbm_registry_t *r, const char *key, const char *own
|
||||
uint8_t *grown_lang =
|
||||
cbm_realloc(CBM_MEM_CLASS_DYN_ARRAY, arr->lang, (size_t)want * sizeof(uint8_t));
|
||||
/* Both side arrays share is_test_cap: on failure it stays below
|
||||
* count, so this name serves neither cached test verdicts nor
|
||||
* language filtering, exactly the unfiltered answer. */
|
||||
* count, and the entry is dropped below. */
|
||||
if (grown_lang) {
|
||||
arr->lang = grown_lang;
|
||||
arr->is_test_cap = want;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (arr->count <= arr->is_test_cap) {
|
||||
arr->is_test[arr->count - SKIP_ONE] = is_test_qn(owned_qn) ? 1 : 0;
|
||||
arr->lang[arr->count - SKIP_ONE] =
|
||||
(uint8_t)(lang >= 0 && lang < CBM_LANG_COUNT ? lang : CBM_LANG_COUNT);
|
||||
if (arr->count > arr->is_test_cap) {
|
||||
/* The verdict can no longer be recomputed from the QN, so an entry
|
||||
* without one would be scored as non-test and outrank real code. Drop
|
||||
* it instead: one missing candidate beats a wrong winner. The language
|
||||
* shares is_test_cap, so the drop covers it as well. */
|
||||
arr->count = before;
|
||||
cbm_log_warn("registry.verdict_alloc_failed", "key", key);
|
||||
return;
|
||||
}
|
||||
arr->is_test[arr->count - SKIP_ONE] = is_test ? 1 : 0;
|
||||
arr->lang[arr->count - SKIP_ONE] =
|
||||
(uint8_t)(lang >= 0 && lang < CBM_LANG_COUNT ? lang : CBM_LANG_COUNT);
|
||||
}
|
||||
|
||||
static bool registry_label_is_callable(const char *label) {
|
||||
@@ -1844,13 +1831,13 @@ static const char *registry_add_exact(cbm_registry_t *r, const char *qualified_n
|
||||
}
|
||||
|
||||
void cbm_registry_add(cbm_registry_t *r, const char *name, const char *qualified_name,
|
||||
const char *label) {
|
||||
cbm_registry_add_lang_in_module(r, name, qualified_name, label, CBM_LANG_COUNT, 0);
|
||||
const char *label, bool is_test) {
|
||||
cbm_registry_add_lang_in_module(r, name, qualified_name, label, CBM_LANG_COUNT, 0, is_test);
|
||||
}
|
||||
|
||||
void cbm_registry_add_lang(cbm_registry_t *r, const char *name, const char *qualified_name,
|
||||
const char *label, CBMLanguage lang) {
|
||||
cbm_registry_add_lang_in_module(r, name, qualified_name, label, lang, 0);
|
||||
const char *label, CBMLanguage lang, bool is_test) {
|
||||
cbm_registry_add_lang_in_module(r, name, qualified_name, label, lang, 0, is_test);
|
||||
}
|
||||
|
||||
/* Python nests defs: a def or class declared in a function body -- a closure,
|
||||
@@ -1869,7 +1856,7 @@ void cbm_registry_add_lang(cbm_registry_t *r, const char *name, const char *qual
|
||||
* index seconds for nothing. */
|
||||
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) {
|
||||
if (!r || !qualified_name || !label) {
|
||||
return;
|
||||
}
|
||||
@@ -1913,7 +1900,7 @@ void cbm_registry_add_lang_in_module(cbm_registry_t *r, const char *name,
|
||||
const char *derived = simple_name(qualified_name);
|
||||
const char *primary =
|
||||
name && cbm_qn_callable_base_len_named(owned_qn, name) < strlen(owned_qn) ? name : derived;
|
||||
index_under_name(r, primary, owned_qn, lang);
|
||||
index_under_name(r, primary, owned_qn, is_test, lang);
|
||||
/* '#' is a QN fence; no extractor mints one for a REGISTERED definition
|
||||
* today (Rust cfg twins are variants of one plain QN; a C macro's QN ends in
|
||||
* "#macro", but Macro is no registry label, so it never gets here). A
|
||||
@@ -1925,7 +1912,7 @@ void cbm_registry_add_lang_in_module(cbm_registry_t *r, const char *name,
|
||||
* signature-qualified callable is filed under `name` already, and its
|
||||
* capped suffix may carry a '#' of its own. */
|
||||
if (name && name[0] && strchr(derived, '#') && strcmp(name, primary) != 0) {
|
||||
index_under_name(r, name, owned_qn, lang);
|
||||
index_under_name(r, name, owned_qn, is_test, lang);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2296,12 +2283,8 @@ static cbm_resolution_t resolve_multi_with_imports(const cbm_registry_t *r, cons
|
||||
int fcount = 0;
|
||||
for (int i = 0; i < arr->count && fcount < CBM_SZ_256; i++) {
|
||||
if (is_import_reachable(arr->items[i], import_vals, import_count)) {
|
||||
if (flags) {
|
||||
filtered_test[fcount] = flags[i];
|
||||
}
|
||||
if (langs) {
|
||||
filtered_lang[fcount] = langs[i];
|
||||
}
|
||||
filtered_test[fcount] = flags[i];
|
||||
filtered_lang[fcount] = langs[i];
|
||||
filtered[fcount] = arr->items[i];
|
||||
fcount++;
|
||||
}
|
||||
@@ -2311,8 +2294,8 @@ static cbm_resolution_t resolve_multi_with_imports(const cbm_registry_t *r, cons
|
||||
return (cbm_resolution_t){filtered[0], "suffix_match", conf, arr->count};
|
||||
}
|
||||
if (fcount > SKIP_ONE) {
|
||||
const char *best = best_by_import_distance(r, filtered, flags ? filtered_test : NULL,
|
||||
langs ? filtered_lang : NULL, fcount, module_qn);
|
||||
const char *best =
|
||||
best_by_import_distance(r, filtered, filtered_test, filtered_lang, fcount, module_qn);
|
||||
if (best) {
|
||||
double conf = candidate_count_penalty(CONF_SUFFIX_MATCH, fcount);
|
||||
return (cbm_resolution_t){best, "suffix_match", conf, fcount};
|
||||
@@ -2507,14 +2490,13 @@ static bool receiver_chain_admits(const char *callee_name, const char *candidate
|
||||
|
||||
/* The same-name candidates without the members -- Field and Method nodes --
|
||||
* in `view` (backed by the caller's buffers, at most REG_MAX_CANDIDATES
|
||||
* entries). The side arrays are carried over only when the bucket has them
|
||||
* for every entry. */
|
||||
* entries). Every entry of a non-empty bucket has its stored verdict and
|
||||
* language (index_under_name), and the view carries them over. */
|
||||
static void candidates_without_members(const cbm_registry_t *r, const qn_array_t *arr,
|
||||
qn_array_t *view, char **items, uint8_t *is_test,
|
||||
uint8_t *lang) {
|
||||
const uint8_t *flags = qn_test_flags(arr);
|
||||
const uint8_t *langs = qn_lang_flags(arr);
|
||||
bool side = flags && langs;
|
||||
int n = 0;
|
||||
for (int i = 0; i < arr->count; i++) {
|
||||
const char *label = cbm_registry_label_of(r, arr->items[i]);
|
||||
@@ -2522,18 +2504,12 @@ static void candidates_without_members(const cbm_registry_t *r, const qn_array_t
|
||||
continue;
|
||||
}
|
||||
items[n] = arr->items[i];
|
||||
if (side) {
|
||||
is_test[n] = flags[i];
|
||||
lang[n] = langs[i];
|
||||
}
|
||||
is_test[n] = flags[i];
|
||||
lang[n] = langs[i];
|
||||
n++;
|
||||
}
|
||||
*view = (qn_array_t){.items = items,
|
||||
.count = n,
|
||||
.cap = n,
|
||||
.is_test = side ? is_test : NULL,
|
||||
.is_test_cap = side ? n : 0,
|
||||
.lang = side ? lang : NULL};
|
||||
*view = (qn_array_t){
|
||||
.items = items, .count = n, .cap = n, .is_test = is_test, .is_test_cap = n, .lang = lang};
|
||||
}
|
||||
|
||||
/* Strategies 3.5, 3 and 4 over one same-name candidate set. `lang` reaches
|
||||
@@ -2669,7 +2645,7 @@ static cbm_resolution_t registry_resolve_chain(const cbm_registry_t *r, const ch
|
||||
* at most REG_MAX_CANDIDATES long whenever a name strategy produced `qn`. */
|
||||
static CBMLanguage registry_entry_language(const cbm_registry_t *r, const char *qn) {
|
||||
const qn_array_t *arr = cbm_ht_get(r->by_name, simple_name(qn));
|
||||
if (!arr || !arr->lang || arr->is_test_cap < arr->count) {
|
||||
if (!arr) {
|
||||
return CBM_LANG_COUNT;
|
||||
}
|
||||
for (int i = 0; i < arr->count; i++) {
|
||||
@@ -3071,22 +3047,28 @@ static bool swift_signature_matches(const swift_signature_t *meta, const CBMCall
|
||||
}
|
||||
|
||||
/* Keep receiver choice ahead of general import/name choice. Without a module,
|
||||
* this path still uses the deterministic distance tie-break, as before. */
|
||||
static const char *swift_choose_receiver(const cbm_registry_t *r, const char **matches, int matched,
|
||||
const char *name, const CBMCall *call,
|
||||
const char *module_qn, const char **import_vals,
|
||||
int import_count, const char **scratch) {
|
||||
* this path still uses the deterministic distance tie-break, as before.
|
||||
* `match_test` holds the stored test verdict of each of `matches`, and
|
||||
* `scratch_test` is the matching scratch, so every filtered list the scorer
|
||||
* sees carries its own verdicts. */
|
||||
static const char *swift_choose_receiver(const cbm_registry_t *r, const char **matches,
|
||||
const uint8_t *match_test, int matched, const char *name,
|
||||
const CBMCall *call, const char *module_qn,
|
||||
const char **import_vals, int import_count,
|
||||
const char **scratch, uint8_t *scratch_test) {
|
||||
if (!strchr(call->callee_name, '.')) {
|
||||
return NULL;
|
||||
}
|
||||
size_t callee_len = strlen(call->callee_name);
|
||||
const char **receiver_matches = scratch;
|
||||
uint8_t *receiver_test = scratch_test;
|
||||
int receiver_count = 0;
|
||||
for (int i = 0; i < matched; i++) {
|
||||
size_t base = cbm_qn_callable_base_len_named(matches[i], name);
|
||||
if (base >= callee_len &&
|
||||
memcmp(matches[i] + base - callee_len, call->callee_name, callee_len) == 0 &&
|
||||
(base == callee_len || matches[i][base - callee_len - SKIP_ONE] == '.')) {
|
||||
receiver_test[receiver_count] = match_test[i];
|
||||
receiver_matches[receiver_count++] = matches[i];
|
||||
}
|
||||
}
|
||||
@@ -3094,45 +3076,53 @@ static const char *swift_choose_receiver(const cbm_registry_t *r, const char **m
|
||||
return NULL;
|
||||
}
|
||||
const char **reachable = scratch + matched;
|
||||
uint8_t *reachable_test = scratch_test + matched;
|
||||
int reachable_count = 0;
|
||||
if (import_vals) {
|
||||
for (int i = 0; i < receiver_count; i++) {
|
||||
if (is_import_reachable(receiver_matches[i], import_vals, import_count)) {
|
||||
reachable_test[reachable_count] = receiver_test[i];
|
||||
reachable[reachable_count++] = receiver_matches[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
const char **preferred = reachable_count > 0 ? reachable : receiver_matches;
|
||||
const uint8_t *preferred_test = reachable_count > 0 ? reachable_test : receiver_test;
|
||||
int preferred_count = reachable_count > 0 ? reachable_count : receiver_count;
|
||||
return best_by_import_distance(r, preferred, NULL, NULL, preferred_count, module_qn);
|
||||
return best_by_import_distance(r, preferred, preferred_test, NULL, preferred_count, module_qn);
|
||||
}
|
||||
|
||||
static const char *swift_choose_candidate(const cbm_registry_t *r, const char **matches,
|
||||
int matched, const char *name, const CBMCall *call,
|
||||
const char *module_qn, const char **import_vals,
|
||||
int import_count, const char **scratch) {
|
||||
const char *chosen = swift_choose_receiver(r, matches, matched, name, call, module_qn,
|
||||
import_vals, import_count, scratch);
|
||||
const uint8_t *match_test, int matched, const char *name,
|
||||
const CBMCall *call, const char *module_qn,
|
||||
const char **import_vals, int import_count,
|
||||
const char **scratch, uint8_t *scratch_test) {
|
||||
const char *chosen =
|
||||
swift_choose_receiver(r, matches, match_test, matched, name, call, module_qn, import_vals,
|
||||
import_count, scratch, scratch_test);
|
||||
if (chosen) {
|
||||
return chosen;
|
||||
}
|
||||
const char **reachable = scratch;
|
||||
uint8_t *reachable_test = scratch_test;
|
||||
int reachable_count = 0;
|
||||
if (import_vals) {
|
||||
for (int i = 0; i < matched; i++) {
|
||||
if (is_import_reachable(matches[i], import_vals, import_count)) {
|
||||
reachable_test[reachable_count] = match_test[i];
|
||||
reachable[reachable_count++] = matches[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
if (reachable_count > 0) {
|
||||
chosen = module_qn
|
||||
? best_by_import_distance(r, reachable, NULL, NULL, reachable_count, module_qn)
|
||||
: reachable[0];
|
||||
chosen = module_qn ? best_by_import_distance(r, reachable, reachable_test, NULL,
|
||||
reachable_count, module_qn)
|
||||
: reachable[0];
|
||||
}
|
||||
if (!chosen) {
|
||||
chosen = module_qn ? best_by_import_distance(r, matches, NULL, NULL, matched, module_qn)
|
||||
: matches[0];
|
||||
chosen = module_qn
|
||||
? best_by_import_distance(r, matches, match_test, NULL, matched, module_qn)
|
||||
: matches[0];
|
||||
}
|
||||
return chosen;
|
||||
}
|
||||
@@ -3762,15 +3752,20 @@ int cbm_registry_swift_candidates(const cbm_registry_t *r, const CBMCall *call,
|
||||
if (!bucket) {
|
||||
return 0;
|
||||
}
|
||||
/* Three QN lists (the matches and the choosers' two scratch lists), each
|
||||
* with a parallel byte per entry for its stored test verdict, in one block. */
|
||||
enum { SWIFT_MATCH_BUFFERS = 3 };
|
||||
if ((size_t)bucket->count > SIZE_MAX / sizeof(const char *) / SWIFT_MATCH_BUFFERS) {
|
||||
const size_t slot = sizeof(const char *) + sizeof(uint8_t);
|
||||
if ((size_t)bucket->count > SIZE_MAX / slot / SWIFT_MATCH_BUFFERS) {
|
||||
return SWIFT_INCOMPATIBLE;
|
||||
}
|
||||
const char **matches = cbm_alloc(
|
||||
CBM_MEM_CLASS_OTHER, (size_t)bucket->count * SWIFT_MATCH_BUFFERS * sizeof(*matches));
|
||||
size_t slots = (size_t)bucket->count * SWIFT_MATCH_BUFFERS;
|
||||
const char **matches = cbm_alloc(CBM_MEM_CLASS_OTHER, slots * slot);
|
||||
if (!matches) {
|
||||
return SWIFT_INCOMPATIBLE;
|
||||
}
|
||||
uint8_t *match_test = (uint8_t *)(matches + slots);
|
||||
const uint8_t *bucket_test = qn_test_flags(bucket);
|
||||
int matched = 0;
|
||||
bool has_swift = false;
|
||||
for (int i = 0; i < bucket->count; i++) {
|
||||
@@ -3779,6 +3774,7 @@ int cbm_registry_swift_candidates(const cbm_registry_t *r, const CBMCall *call,
|
||||
has_swift = has_swift || meta != NULL;
|
||||
if (meta && receiver_chain_admits(call->callee_name, qn, CBM_LANG_SWIFT) &&
|
||||
swift_signature_matches(meta, call)) {
|
||||
match_test[matched] = bucket_test[i];
|
||||
matches[matched++] = qn;
|
||||
}
|
||||
}
|
||||
@@ -3786,8 +3782,9 @@ int cbm_registry_swift_candidates(const cbm_registry_t *r, const CBMCall *call,
|
||||
cbm_free(CBM_MEM_CLASS_OTHER, matches);
|
||||
return has_swift ? SWIFT_INCOMPATIBLE : 0;
|
||||
}
|
||||
const char *chosen = swift_choose_candidate(r, matches, matched, name, call, module_qn,
|
||||
import_vals, import_count, matches + bucket->count);
|
||||
const char *chosen =
|
||||
swift_choose_candidate(r, matches, match_test, matched, name, call, module_qn, import_vals,
|
||||
import_count, matches + bucket->count, match_test + bucket->count);
|
||||
if (!chosen) {
|
||||
cbm_free(CBM_MEM_CLASS_OTHER, matches);
|
||||
return 0;
|
||||
@@ -4760,11 +4757,18 @@ cbm_resolution_t cbm_registry_resolve_call_scoped(const cbm_registry_t *r, const
|
||||
/* ── Fuzzy Resolve ──────────────────────────────────────────────── */
|
||||
|
||||
/* Filter candidates by import reachability. Returns count of reachable. */
|
||||
static int filter_import_reachable(const char **candidates, int count, const char **import_vals,
|
||||
int import_count, const char **out, int max_out) {
|
||||
/* Keeps `out_flags` and `out_langs` in step with `out`: the verdicts are
|
||||
* stored per entry and cannot be recomputed from the QN, so a filtered view has
|
||||
* to carry its own, and the languages travel with them. */
|
||||
static int filter_import_reachable(const char **candidates, const uint8_t *flags,
|
||||
const uint8_t *langs, int count, const char **import_vals,
|
||||
int import_count, const char **out, uint8_t *out_flags,
|
||||
uint8_t *out_langs, int max_out) {
|
||||
int n = 0;
|
||||
for (int i = 0; i < count && n < max_out; i++) {
|
||||
if (is_import_reachable(candidates[i], import_vals, import_count)) {
|
||||
out_flags[n] = flags[i];
|
||||
out_langs[n] = langs[i];
|
||||
out[n++] = candidates[i];
|
||||
}
|
||||
}
|
||||
@@ -4804,13 +4808,21 @@ cbm_fuzzy_result_t cbm_registry_fuzzy_resolve(const cbm_registry_t *r, const cha
|
||||
|
||||
/* Multiple candidates: filter by import reachability */
|
||||
const char *filtered[CBM_SZ_256];
|
||||
uint8_t filtered_flags[CBM_SZ_256];
|
||||
uint8_t filtered_langs[CBM_SZ_256];
|
||||
int fcount = arr->count;
|
||||
const char **fptr = (const char **)arr->items;
|
||||
const uint8_t *fflags = qn_test_flags(arr);
|
||||
const uint8_t *flangs = qn_lang_flags(arr);
|
||||
|
||||
if (have_imports) {
|
||||
fcount = filter_import_reachable((const char **)arr->items, arr->count, import_map_vals,
|
||||
import_map_count, filtered, CBM_SZ_256);
|
||||
fcount = filter_import_reachable((const char **)arr->items, qn_test_flags(arr),
|
||||
qn_lang_flags(arr), arr->count, import_map_vals,
|
||||
import_map_count, filtered, filtered_flags, filtered_langs,
|
||||
CBM_SZ_256);
|
||||
fptr = filtered;
|
||||
fflags = filtered_flags;
|
||||
flangs = filtered_langs;
|
||||
}
|
||||
|
||||
if (fcount == 0) {
|
||||
@@ -4830,9 +4842,7 @@ cbm_fuzzy_result_t cbm_registry_fuzzy_resolve(const cbm_registry_t *r, const cha
|
||||
{fptr[0], "fuzzy", candidate_count_penalty(CONF_FUZZY_SINGLE, arr->count), arr->count},
|
||||
true};
|
||||
}
|
||||
const bool all = fptr == (const char **)arr->items;
|
||||
const char *best = best_by_import_distance(r, fptr, all ? qn_test_flags(arr) : NULL,
|
||||
all ? qn_lang_flags(arr) : NULL, fcount, module_qn);
|
||||
const char *best = best_by_import_distance(r, fptr, fflags, flangs, fcount, module_qn);
|
||||
if (!best) {
|
||||
return no_match;
|
||||
}
|
||||
|
||||
@@ -434,9 +434,9 @@ TEST(callable_sig_leaf_splitters_skip_suffix) {
|
||||
* keeps its historical key (last '.' or "::" segment). */
|
||||
TEST(callable_sig_registry_by_name_uses_base_leaf) {
|
||||
cbm_registry_t *r = cbm_registry_new();
|
||||
cbm_registry_add(r, "upload", "p.Session.upload(_:Data,to:URL)", "Method");
|
||||
cbm_registry_add(r, "upload", "p.Session.upload(_:Data,with:URLRequest)", "Method");
|
||||
cbm_registry_add(r, "square", "p.Math::square", "Function");
|
||||
cbm_registry_add(r, "upload", "p.Session.upload(_:Data,to:URL)", "Method", false);
|
||||
cbm_registry_add(r, "upload", "p.Session.upload(_:Data,with:URLRequest)", "Method", false);
|
||||
cbm_registry_add(r, "square", "p.Math::square", "Function", false);
|
||||
const char **out = NULL;
|
||||
int count = 0;
|
||||
cbm_registry_find_by_name(r, "upload", &out, &count);
|
||||
|
||||
+173
-45
@@ -6619,6 +6619,132 @@ TEST(pipeline_incremental_cross_file_call_reference_matches_fresh_full) {
|
||||
PASS();
|
||||
}
|
||||
|
||||
/* #2427 rank probes: one bare call, two same-named candidates, and the
|
||||
* registry's test ranking alone decides which one the call binds. Three runs
|
||||
* on one database: a full index, then a comment-only edit of the caller on
|
||||
* the closure-repair route, then another on the forced legacy-partial route.
|
||||
* Both incremental routes re-resolve the caller against a registry seeded from
|
||||
* stored nodes rather than the extractor's definitions, which is where a
|
||||
* seeder that loses the verdict shows. */
|
||||
typedef struct {
|
||||
int want[3];
|
||||
int other[3];
|
||||
cbm_incremental_route_t route[3];
|
||||
} rank_probe_t;
|
||||
|
||||
static int rank_probe_count(const char *db, const char *project, const char *source,
|
||||
const char *target, const char *file) {
|
||||
cbm_store_t *s = cbm_store_open_path(db);
|
||||
if (!s) {
|
||||
return -1;
|
||||
}
|
||||
int n = named_edge_to_file_count(s, project, "CALLS", source, target, file);
|
||||
cbm_store_close(s);
|
||||
return n;
|
||||
}
|
||||
|
||||
static void rank_probe(const char *tmp, const char *caller_path, const char *caller_src,
|
||||
const char *source, const char *target, const char *want_file,
|
||||
const char *other_file, rank_probe_t *out) {
|
||||
char db[512];
|
||||
snprintf(db, sizeof(db), "%s/rank.db", tmp);
|
||||
for (int run = 0; run < 3; run++) {
|
||||
out->want[run] = -1;
|
||||
out->other[run] = -1;
|
||||
out->route[run] = CBM_INCREMENTAL_ROUTE_NONE;
|
||||
if (run > 0) {
|
||||
char edited[512];
|
||||
snprintf(edited, sizeof(edited), "%s// edit %d\n", caller_src, run);
|
||||
write_temp_file(tmp, caller_path, edited);
|
||||
}
|
||||
cbm_pipeline_incremental_test_reset_faults();
|
||||
#if defined(CBM_INCREMENTAL_TEST_API) && CBM_INCREMENTAL_TEST_API
|
||||
if (run == 2) {
|
||||
cbm_pipeline_incremental_test_force_legacy_partial_once();
|
||||
}
|
||||
#endif
|
||||
cbm_pipeline_t *p = cbm_pipeline_new(tmp, db, CBM_MODE_FULL);
|
||||
if (!p) {
|
||||
return;
|
||||
}
|
||||
int rc = cbm_pipeline_run(p);
|
||||
out->route[run] = cbm_pipeline_incremental_test_last_route();
|
||||
char project[256];
|
||||
snprintf(project, sizeof(project), "%s", cbm_pipeline_project_name(p));
|
||||
cbm_pipeline_free(p);
|
||||
if (rc != 0) {
|
||||
return;
|
||||
}
|
||||
out->want[run] = rank_probe_count(db, project, source, target, want_file);
|
||||
out->other[run] = rank_probe_count(db, project, source, target, other_file);
|
||||
}
|
||||
}
|
||||
|
||||
/* "latest" spells "test", so the QN scan the extractor's verdict replaced
|
||||
* ranked the production Parse below a helper in a _test.go file, whose Go QN
|
||||
* names only its package and so spells nothing. The verdict ranks them the
|
||||
* right way round, and both incremental routes must read the same verdict
|
||||
* back from the stored nodes: the closure-repair proxies once carried no
|
||||
* is_test, every candidate scored as production, and the smaller QN -- the
|
||||
* test helper -- won. */
|
||||
TEST(pipeline_registry_ranks_by_extractor_verdict_full_and_incremental) {
|
||||
char tmp[256];
|
||||
snprintf(tmp, sizeof(tmp), "/tmp/cbm_rank_verdict_XXXXXX");
|
||||
ASSERT_NOT_NULL(cbm_mkdtemp(tmp));
|
||||
static const char caller[] = "package app\n\nfunc Run() int { return Parse() }\n";
|
||||
write_temp_file(tmp, "go.mod", "module example.com/rank\n\ngo 1.22\n");
|
||||
write_temp_file(tmp, "latest/parse.go", "package latest\n\nfunc Parse() int { return 1 }\n");
|
||||
write_temp_file(tmp, "harness/parse_test.go",
|
||||
"package harness\n\nfunc Parse() int { return 2 }\n");
|
||||
write_temp_file(tmp, "app/run.go", caller);
|
||||
rank_probe_t r;
|
||||
rank_probe(tmp, "app/run.go", caller, "Run", "Parse", "latest/parse.go",
|
||||
"harness/parse_test.go", &r);
|
||||
th_rmtree(tmp);
|
||||
ASSERT_EQ(r.want[0], 1); /* full */
|
||||
ASSERT_EQ(r.other[0], 0);
|
||||
ASSERT_EQ(r.route[1], CBM_INCREMENTAL_ROUTE_CLOSURE_REPAIR);
|
||||
ASSERT_EQ(r.want[1], 1);
|
||||
ASSERT_EQ(r.other[1], 0);
|
||||
#if defined(CBM_INCREMENTAL_TEST_API) && CBM_INCREMENTAL_TEST_API
|
||||
ASSERT_EQ(r.route[2], CBM_INCREMENTAL_ROUTE_LEGACY_PARTIAL);
|
||||
#endif
|
||||
ASSERT_EQ(r.want[2], 1);
|
||||
ASSERT_EQ(r.other[2], 0);
|
||||
PASS();
|
||||
}
|
||||
|
||||
/* A generated fake beside its callers: client/fake shares more of the caller's
|
||||
* package path than the real store does, so on the extractor's verdict alone
|
||||
* (a fake is not a test file) the fake wins on proximity. The test-double path
|
||||
* rule ranks it last in the full index and, through the incremental seed's own
|
||||
* use of the rule, on both incremental routes. */
|
||||
TEST(pipeline_registry_ranks_test_doubles_last_full_and_incremental) {
|
||||
char tmp[256];
|
||||
snprintf(tmp, sizeof(tmp), "/tmp/cbm_rank_double_XXXXXX");
|
||||
ASSERT_NOT_NULL(cbm_mkdtemp(tmp));
|
||||
static const char caller[] = "package client\n\nfunc Run() int { return Load() }\n";
|
||||
write_temp_file(tmp, "go.mod", "module example.com/rank\n\ngo 1.22\n");
|
||||
write_temp_file(tmp, "store/load.go", "package store\n\nfunc Load() int { return 1 }\n");
|
||||
write_temp_file(tmp, "client/fake/load.go", "package fake\n\nfunc Load() int { return 2 }\n");
|
||||
write_temp_file(tmp, "client/run.go", caller);
|
||||
rank_probe_t r;
|
||||
rank_probe(tmp, "client/run.go", caller, "Run", "Load", "store/load.go", "client/fake/load.go",
|
||||
&r);
|
||||
th_rmtree(tmp);
|
||||
ASSERT_EQ(r.want[0], 1); /* full */
|
||||
ASSERT_EQ(r.other[0], 0);
|
||||
ASSERT_EQ(r.route[1], CBM_INCREMENTAL_ROUTE_CLOSURE_REPAIR);
|
||||
ASSERT_EQ(r.want[1], 1);
|
||||
ASSERT_EQ(r.other[1], 0);
|
||||
#if defined(CBM_INCREMENTAL_TEST_API) && CBM_INCREMENTAL_TEST_API
|
||||
ASSERT_EQ(r.route[2], CBM_INCREMENTAL_ROUTE_LEGACY_PARTIAL);
|
||||
#endif
|
||||
ASSERT_EQ(r.want[2], 1);
|
||||
ASSERT_EQ(r.other[2], 0);
|
||||
PASS();
|
||||
}
|
||||
|
||||
/* A semantic control file can change an unchanged source file's exact target.
|
||||
* Both target modules deliberately export the same short name: without the
|
||||
* tsconfig path mapping, project-wide unique-name fallback is ambiguous and
|
||||
@@ -21834,7 +21960,7 @@ static int python_typed_field_scope_case(bool external) {
|
||||
if (result && reg && gbuf && result->field_types.count == 1) {
|
||||
for (int i = 0; i < result->defs.count; i++) {
|
||||
const CBMDefinition *def = &result->defs.items[i];
|
||||
cbm_registry_add(reg, def->name, def->qualified_name, def->label);
|
||||
cbm_registry_add(reg, def->name, def->qualified_name, def->label, false);
|
||||
}
|
||||
const char **keys = NULL;
|
||||
const char **vals = NULL;
|
||||
@@ -27801,8 +27927,8 @@ TEST(helm_parse_chart_many_dependencies) {
|
||||
|
||||
TEST(registry_resolve_single_candidate) {
|
||||
cbm_registry_t *reg = cbm_registry_new();
|
||||
cbm_registry_add(reg, "CreateOrder", "svcA.handlers.CreateOrder", "Function");
|
||||
cbm_registry_add(reg, "ValidateOrder", "svcB.validators.ValidateOrder", "Function");
|
||||
cbm_registry_add(reg, "CreateOrder", "svcA.handlers.CreateOrder", "Function", false);
|
||||
cbm_registry_add(reg, "ValidateOrder", "svcB.validators.ValidateOrder", "Function", false);
|
||||
|
||||
/* Normal resolve unique name */
|
||||
cbm_resolution_t r = cbm_registry_resolve(reg, "CreateOrder", "svcC.caller", NULL, NULL, 0);
|
||||
@@ -27820,7 +27946,7 @@ TEST(registry_resolve_single_candidate) {
|
||||
|
||||
TEST(registry_fuzzy_nonexistent) {
|
||||
cbm_registry_t *reg = cbm_registry_new();
|
||||
cbm_registry_add(reg, "CreateOrder", "svcA.handlers.CreateOrder", "Function");
|
||||
cbm_registry_add(reg, "CreateOrder", "svcA.handlers.CreateOrder", "Function", false);
|
||||
|
||||
cbm_fuzzy_result_t fr =
|
||||
cbm_registry_fuzzy_resolve(reg, "NonExistent", "svcC.caller", NULL, NULL, 0);
|
||||
@@ -27832,8 +27958,8 @@ TEST(registry_fuzzy_nonexistent) {
|
||||
|
||||
TEST(registry_fuzzy_multiple_best_by_distance) {
|
||||
cbm_registry_t *reg = cbm_registry_new();
|
||||
cbm_registry_add(reg, "Process", "svcA.handlers.Process", "Function");
|
||||
cbm_registry_add(reg, "Process", "svcB.handlers.Process", "Function");
|
||||
cbm_registry_add(reg, "Process", "svcA.handlers.Process", "Function", false);
|
||||
cbm_registry_add(reg, "Process", "svcB.handlers.Process", "Function", false);
|
||||
|
||||
/* Caller in svcA → prefer svcA */
|
||||
cbm_fuzzy_result_t fr =
|
||||
@@ -27852,7 +27978,7 @@ TEST(registry_fuzzy_multiple_best_by_distance) {
|
||||
|
||||
TEST(registry_fuzzy_simple_name_extraction) {
|
||||
cbm_registry_t *reg = cbm_registry_new();
|
||||
cbm_registry_add(reg, "DoWork", "myproject.utils.DoWork", "Function");
|
||||
cbm_registry_add(reg, "DoWork", "myproject.utils.DoWork", "Function", false);
|
||||
|
||||
/* Deeply qualified name → extract "DoWork" */
|
||||
cbm_fuzzy_result_t fr = cbm_registry_fuzzy_resolve(reg, "some.deep.module.DoWork",
|
||||
@@ -27877,8 +28003,8 @@ TEST(registry_fuzzy_empty) {
|
||||
|
||||
TEST(registry_exists) {
|
||||
cbm_registry_t *reg = cbm_registry_new();
|
||||
cbm_registry_add(reg, "Foo", "pkg.module.Foo", "Function");
|
||||
cbm_registry_add(reg, "Bar", "pkg.module.Bar", "Method");
|
||||
cbm_registry_add(reg, "Foo", "pkg.module.Foo", "Function", false);
|
||||
cbm_registry_add(reg, "Bar", "pkg.module.Bar", "Method", false);
|
||||
|
||||
ASSERT_TRUE(cbm_registry_exists(reg, "pkg.module.Foo"));
|
||||
ASSERT_TRUE(cbm_registry_exists(reg, "pkg.module.Bar"));
|
||||
@@ -27891,7 +28017,7 @@ TEST(registry_exists) {
|
||||
|
||||
TEST(registry_confidence_import_map) {
|
||||
cbm_registry_t *reg = cbm_registry_new();
|
||||
cbm_registry_add(reg, "Foo", "proj.other.Foo", "Function");
|
||||
cbm_registry_add(reg, "Foo", "proj.other.Foo", "Function", false);
|
||||
|
||||
const char *keys[] = {"other"};
|
||||
const char *vals[] = {"proj.other"};
|
||||
@@ -27906,7 +28032,7 @@ TEST(registry_confidence_import_map) {
|
||||
|
||||
TEST(registry_confidence_import_map_suffix) {
|
||||
cbm_registry_t *reg = cbm_registry_new();
|
||||
cbm_registry_add(reg, "Foo", "proj.other.sub.Foo", "Function");
|
||||
cbm_registry_add(reg, "Foo", "proj.other.sub.Foo", "Function", false);
|
||||
|
||||
const char *keys[] = {"other"};
|
||||
const char *vals[] = {"proj.other"};
|
||||
@@ -27921,7 +28047,7 @@ TEST(registry_confidence_import_map_suffix) {
|
||||
|
||||
TEST(registry_confidence_same_module) {
|
||||
cbm_registry_t *reg = cbm_registry_new();
|
||||
cbm_registry_add(reg, "Foo", "proj.pkg.Foo", "Function");
|
||||
cbm_registry_add(reg, "Foo", "proj.pkg.Foo", "Function", false);
|
||||
|
||||
cbm_resolution_t r = cbm_registry_resolve(reg, "Foo", "proj.pkg", NULL, NULL, 0);
|
||||
ASSERT_STR_EQ(r.qualified_name, "proj.pkg.Foo");
|
||||
@@ -27934,7 +28060,7 @@ TEST(registry_confidence_same_module) {
|
||||
|
||||
TEST(registry_confidence_unique_name) {
|
||||
cbm_registry_t *reg = cbm_registry_new();
|
||||
cbm_registry_add(reg, "Bar", "proj.pkg.Bar", "Function");
|
||||
cbm_registry_add(reg, "Bar", "proj.pkg.Bar", "Function", false);
|
||||
|
||||
cbm_resolution_t r = cbm_registry_resolve(reg, "Bar", "proj.unrelated", NULL, NULL, 0);
|
||||
ASSERT_STR_EQ(r.qualified_name, "proj.pkg.Bar");
|
||||
@@ -27947,8 +28073,8 @@ TEST(registry_confidence_unique_name) {
|
||||
|
||||
TEST(registry_confidence_suffix_match) {
|
||||
cbm_registry_t *reg = cbm_registry_new();
|
||||
cbm_registry_add(reg, "Process", "proj.svcA.Process", "Function");
|
||||
cbm_registry_add(reg, "Process", "proj.svcB.Process", "Function");
|
||||
cbm_registry_add(reg, "Process", "proj.svcA.Process", "Function", false);
|
||||
cbm_registry_add(reg, "Process", "proj.svcB.Process", "Function", false);
|
||||
|
||||
cbm_resolution_t r = cbm_registry_resolve(reg, "Process", "proj.svcA.caller", NULL, NULL, 0);
|
||||
ASSERT_STR_EQ(r.qualified_name, "proj.svcA.Process");
|
||||
@@ -27964,7 +28090,7 @@ TEST(registry_confidence_suffix_match) {
|
||||
* wrong target — and with one candidate it won the top name-only confidence. */
|
||||
TEST(registry_receiver_chain_refuses_library_unique_name_issue1893) {
|
||||
cbm_registry_t *reg = cbm_registry_new();
|
||||
cbm_registry_add(reg, "data", "HomeboxUI.PickedFile.data", "Variable");
|
||||
cbm_registry_add(reg, "data", "HomeboxUI.PickedFile.data", "Variable", false);
|
||||
|
||||
cbm_resolution_t r =
|
||||
cbm_registry_resolve(reg, "URLSession.shared.data", "HomeboxUI.Net", NULL, NULL, 0);
|
||||
@@ -27978,8 +28104,8 @@ TEST(registry_receiver_chain_refuses_library_unique_name_issue1893) {
|
||||
* the final name and import distance picks the winner. */
|
||||
TEST(registry_receiver_chain_refuses_library_suffix_match_issue1893) {
|
||||
cbm_registry_t *reg = cbm_registry_new();
|
||||
cbm_registry_add(reg, "data", "HomeboxUI.PickedFile.data", "Variable");
|
||||
cbm_registry_add(reg, "data", "HomeboxUI.Payload.data", "Variable");
|
||||
cbm_registry_add(reg, "data", "HomeboxUI.PickedFile.data", "Variable", false);
|
||||
cbm_registry_add(reg, "data", "HomeboxUI.Payload.data", "Variable", false);
|
||||
|
||||
cbm_resolution_t r =
|
||||
cbm_registry_resolve(reg, "URLSession.shared.data", "HomeboxUI.Net", NULL, NULL, 0);
|
||||
@@ -27993,7 +28119,7 @@ TEST(registry_receiver_chain_refuses_library_suffix_match_issue1893) {
|
||||
* so Calendar really is in the receiver chain. */
|
||||
TEST(registry_receiver_chain_keeps_project_extension_issue1893) {
|
||||
cbm_registry_t *reg = cbm_registry_new();
|
||||
cbm_registry_add(reg, "startOfDayUTC", "AuthDTOs.Calendar.startOfDayUTC", "Method");
|
||||
cbm_registry_add(reg, "startOfDayUTC", "AuthDTOs.Calendar.startOfDayUTC", "Method", false);
|
||||
|
||||
cbm_resolution_t r = cbm_registry_resolve(reg, "Calendar.utcGregorian.startOfDayUTC",
|
||||
"HomeboxUI.Stats", NULL, NULL, 0);
|
||||
@@ -28013,7 +28139,7 @@ TEST(registry_receiver_chain_keeps_project_extension_issue1893) {
|
||||
TEST(registry_receiver_chain_admits_factory_chain_into_nested_type) {
|
||||
cbm_registry_t *reg = cbm_registry_new();
|
||||
cbm_registry_add(reg, "putList", "org.elasticsearch.common.settings.Settings.Builder.putList",
|
||||
"Method");
|
||||
"Method", false);
|
||||
|
||||
cbm_resolution_t r = cbm_registry_resolve(
|
||||
reg, "Settings.builder().putList", "org.elasticsearch.index.IndexSettings", NULL, NULL, 0);
|
||||
@@ -28024,7 +28150,7 @@ TEST(registry_receiver_chain_admits_factory_chain_into_nested_type) {
|
||||
* .toBuilder().setWarningsHandler -> RequestOptions.Builder.setWarningsHandler. */
|
||||
cbm_registry_add(reg, "setWarningsHandler",
|
||||
"org.elasticsearch.client.RequestOptions.Builder.setWarningsHandler",
|
||||
"Method");
|
||||
"Method", false);
|
||||
r = cbm_registry_resolve(reg, "RequestOptions.DEFAULT.toBuilder().setWarningsHandler",
|
||||
"org.elasticsearch.client.Rest", NULL, NULL, 0);
|
||||
ASSERT_STR_EQ(r.qualified_name,
|
||||
@@ -28040,7 +28166,7 @@ TEST(registry_receiver_chain_admits_factory_chain_into_nested_type) {
|
||||
TEST(registry_receiver_chain_still_refuses_foreign_root_with_ancestry_rule) {
|
||||
cbm_registry_t *reg = cbm_registry_new();
|
||||
cbm_registry_add(reg, "getEncoder", "org.elasticsearch.index.codec.DocOffsetsCodec.getEncoder",
|
||||
"Method");
|
||||
"Method", false);
|
||||
|
||||
cbm_resolution_t r = cbm_registry_resolve(
|
||||
reg, "Base64.getEncoder", "org.elasticsearch.index.IndexSettings", NULL, NULL, 0);
|
||||
@@ -28050,7 +28176,7 @@ TEST(registry_receiver_chain_still_refuses_foreign_root_with_ancestry_rule) {
|
||||
* org.elasticsearch.common.Math.toIntExact through "Math" appearing as an
|
||||
* ancestry segment ONLY IF that segment really is the type — here it is, and
|
||||
* that is the correct outcome (the project's own Math.toIntExact). */
|
||||
cbm_registry_add(reg, "toIntExact", "org.elasticsearch.common.Math.toIntExact", "Method");
|
||||
cbm_registry_add(reg, "toIntExact", "org.elasticsearch.common.Math.toIntExact", "Method", false);
|
||||
r = cbm_registry_resolve(reg, "Math.toIntExact", "org.elasticsearch.index.IndexSettings", NULL,
|
||||
NULL, 0);
|
||||
ASSERT_STR_EQ(r.qualified_name, "org.elasticsearch.common.Math.toIntExact");
|
||||
@@ -28063,7 +28189,7 @@ TEST(registry_receiver_chain_still_refuses_foreign_root_with_ancestry_rule) {
|
||||
* must not look at it, or every ordinary vm.load style call would be refused. */
|
||||
TEST(registry_receiver_chain_ignores_lowercase_root_issue1893) {
|
||||
cbm_registry_t *reg = cbm_registry_new();
|
||||
cbm_registry_add(reg, "load", "HomeboxUI.EntityListViewModel.load", "Method");
|
||||
cbm_registry_add(reg, "load", "HomeboxUI.EntityListViewModel.load", "Method", false);
|
||||
|
||||
cbm_resolution_t r = cbm_registry_resolve(reg, "vm.load", "HomeboxUI.Views", NULL, NULL, 0);
|
||||
ASSERT_STR_EQ(r.qualified_name, "HomeboxUI.EntityListViewModel.load");
|
||||
@@ -28079,7 +28205,7 @@ TEST(registry_receiver_chain_ignores_lowercase_root_issue1893) {
|
||||
* the CALLS entry point both resolvers share, for a C# file. */
|
||||
TEST(registry_receiver_chain_refuses_lowercase_type_keyword_issue2121) {
|
||||
cbm_registry_t *reg = cbm_registry_new();
|
||||
cbm_registry_add(reg, "TryParse", "tmp.Decoys.TsidNode.TryParse", "Method");
|
||||
cbm_registry_add(reg, "TryParse", "tmp.Decoys.TsidNode.TryParse", "Method", false);
|
||||
|
||||
cbm_resolution_t r = cbm_registry_resolve_call_lang(reg, "int.TryParse", "tmp.Demo", NULL, NULL,
|
||||
0, CBM_LANG_CSHARP);
|
||||
@@ -28100,7 +28226,7 @@ TEST(registry_receiver_chain_refuses_lowercase_type_keyword_issue2121) {
|
||||
* (which would pass through for the wrong reason). */
|
||||
TEST(registry_receiver_chain_keeps_type_keyword_when_chain_matches_issue2121) {
|
||||
cbm_registry_t *reg = cbm_registry_new();
|
||||
cbm_registry_add(reg, "TryParse", "tmp.int.TryParse", "Method");
|
||||
cbm_registry_add(reg, "TryParse", "tmp.int.TryParse", "Method", false);
|
||||
|
||||
cbm_resolution_t r = cbm_registry_resolve_call_lang(reg, "int.TryParse", "tmp.Demo", NULL, NULL,
|
||||
0, CBM_LANG_CSHARP);
|
||||
@@ -28119,7 +28245,7 @@ TEST(registry_receiver_chain_keeps_type_keyword_when_chain_matches_issue2121) {
|
||||
* pinning the other side of the gate. */
|
||||
TEST(registry_receiver_chain_keyword_list_gated_to_csharp_issue2121) {
|
||||
cbm_registry_t *reg = cbm_registry_new();
|
||||
cbm_registry_add(reg, "TryParse", "tmp.Decoys.TsidNode.TryParse", "Method");
|
||||
cbm_registry_add(reg, "TryParse", "tmp.Decoys.TsidNode.TryParse", "Method", false);
|
||||
|
||||
cbm_resolution_t r = cbm_registry_resolve_call_lang(reg, "object.TryParse", "tmp.Demo", NULL,
|
||||
NULL, 0, CBM_LANG_PYTHON);
|
||||
@@ -28129,7 +28255,7 @@ TEST(registry_receiver_chain_keyword_list_gated_to_csharp_issue2121) {
|
||||
/* A Go call and the language-blind cbm_registry_resolve read a second
|
||||
* keyword spelling as a value root as well. */
|
||||
cbm_registry_t *reg2 = cbm_registry_new();
|
||||
cbm_registry_add(reg2, "Join", "tmp.Decoys.Joiner.Join", "Method");
|
||||
cbm_registry_add(reg2, "Join", "tmp.Decoys.Joiner.Join", "Method", false);
|
||||
cbm_resolution_t r2 =
|
||||
cbm_registry_resolve_call_lang(reg2, "string.Join", "tmp.Demo", NULL, NULL, 0, CBM_LANG_GO);
|
||||
ASSERT_STR_EQ(r2.qualified_name, "tmp.Decoys.Joiner.Join");
|
||||
@@ -28151,7 +28277,7 @@ TEST(registry_receiver_chain_keyword_list_gated_to_csharp_issue2121) {
|
||||
* words that can only name a type. */
|
||||
TEST(registry_receiver_chain_csharp_dynamic_variable_still_binds_issue2121) {
|
||||
cbm_registry_t *reg = cbm_registry_new();
|
||||
cbm_registry_add(reg, "Invoke", "tmp.Handlers.Handler.Invoke", "Method");
|
||||
cbm_registry_add(reg, "Invoke", "tmp.Handlers.Handler.Invoke", "Method", false);
|
||||
|
||||
cbm_resolution_t r = cbm_registry_resolve_call_lang(reg, "dynamic.Invoke", "tmp.Demo", NULL,
|
||||
NULL, 0, CBM_LANG_CSHARP);
|
||||
@@ -28165,7 +28291,7 @@ TEST(registry_receiver_chain_csharp_dynamic_variable_still_binds_issue2121) {
|
||||
/* An unqualified callee has no chain at all and must pass through unchanged. */
|
||||
TEST(registry_receiver_chain_ignores_bare_name_issue1893) {
|
||||
cbm_registry_t *reg = cbm_registry_new();
|
||||
cbm_registry_add(reg, "helper", "proj.pkg.helper", "Function");
|
||||
cbm_registry_add(reg, "helper", "proj.pkg.helper", "Function", false);
|
||||
|
||||
cbm_resolution_t r = cbm_registry_resolve(reg, "helper", "proj.other", NULL, NULL, 0);
|
||||
ASSERT_STR_EQ(r.qualified_name, "proj.pkg.helper");
|
||||
@@ -28177,7 +28303,7 @@ TEST(registry_receiver_chain_ignores_bare_name_issue1893) {
|
||||
|
||||
TEST(registry_fuzzy_confidence_single) {
|
||||
cbm_registry_t *reg = cbm_registry_new();
|
||||
cbm_registry_add(reg, "Handler", "proj.svc.Handler", "Function");
|
||||
cbm_registry_add(reg, "Handler", "proj.svc.Handler", "Function", false);
|
||||
|
||||
cbm_fuzzy_result_t fr =
|
||||
cbm_registry_fuzzy_resolve(reg, "unknownPkg.Handler", "proj.caller", NULL, NULL, 0);
|
||||
@@ -28191,8 +28317,8 @@ TEST(registry_fuzzy_confidence_single) {
|
||||
|
||||
TEST(registry_fuzzy_confidence_distance) {
|
||||
cbm_registry_t *reg = cbm_registry_new();
|
||||
cbm_registry_add(reg, "Process", "proj.svcA.Process", "Function");
|
||||
cbm_registry_add(reg, "Process", "proj.svcB.Process", "Function");
|
||||
cbm_registry_add(reg, "Process", "proj.svcA.Process", "Function", false);
|
||||
cbm_registry_add(reg, "Process", "proj.svcB.Process", "Function", false);
|
||||
|
||||
cbm_fuzzy_result_t fr =
|
||||
cbm_registry_fuzzy_resolve(reg, "unknownPkg.Process", "proj.svcA.other", NULL, NULL, 0);
|
||||
@@ -28206,8 +28332,8 @@ TEST(registry_fuzzy_confidence_distance) {
|
||||
|
||||
TEST(registry_negative_import_rejects) {
|
||||
cbm_registry_t *reg = cbm_registry_new();
|
||||
cbm_registry_add(reg, "Process", "proj.billing.Process", "Function");
|
||||
cbm_registry_add(reg, "Process", "proj.handler.Process", "Function");
|
||||
cbm_registry_add(reg, "Process", "proj.billing.Process", "Function", false);
|
||||
cbm_registry_add(reg, "Process", "proj.handler.Process", "Function", false);
|
||||
|
||||
/* Import only handler's module → should prefer handler */
|
||||
const char *keys[] = {"handler"};
|
||||
@@ -28221,7 +28347,7 @@ TEST(registry_negative_import_rejects) {
|
||||
|
||||
TEST(registry_fuzzy_import_penalty) {
|
||||
cbm_registry_t *reg = cbm_registry_new();
|
||||
cbm_registry_add(reg, "Handler", "proj.billing.Handler", "Function");
|
||||
cbm_registry_add(reg, "Handler", "proj.billing.Handler", "Function", false);
|
||||
|
||||
/* Has imports but billing not imported → confidence halved */
|
||||
const char *keys[] = {"other"};
|
||||
@@ -28238,7 +28364,7 @@ TEST(registry_fuzzy_import_penalty) {
|
||||
|
||||
TEST(registry_fuzzy_no_import_map_passthrough) {
|
||||
cbm_registry_t *reg = cbm_registry_new();
|
||||
cbm_registry_add(reg, "Handler", "proj.billing.Handler", "Function");
|
||||
cbm_registry_add(reg, "Handler", "proj.billing.Handler", "Function", false);
|
||||
|
||||
/* NULL import map → no penalty, full fuzzy confidence */
|
||||
cbm_fuzzy_result_t fr =
|
||||
@@ -28252,10 +28378,10 @@ TEST(registry_fuzzy_no_import_map_passthrough) {
|
||||
|
||||
TEST(registry_find_by_name) {
|
||||
cbm_registry_t *reg = cbm_registry_new();
|
||||
cbm_registry_add(reg, "Foo", "proj.pkg.Foo", "Function");
|
||||
cbm_registry_add(reg, "Bar", "proj.pkg.Bar", "Function");
|
||||
cbm_registry_add(reg, "Foo", "proj.other.Foo", "Function");
|
||||
cbm_registry_add(reg, "transform", "proj.utils.DataProcessor.transform", "Method");
|
||||
cbm_registry_add(reg, "Foo", "proj.pkg.Foo", "Function", false);
|
||||
cbm_registry_add(reg, "Bar", "proj.pkg.Bar", "Function", false);
|
||||
cbm_registry_add(reg, "Foo", "proj.other.Foo", "Function", false);
|
||||
cbm_registry_add(reg, "transform", "proj.utils.DataProcessor.transform", "Method", false);
|
||||
|
||||
/* FindByName returns all entries for "Foo" */
|
||||
const char **foos = NULL;
|
||||
@@ -29215,10 +29341,10 @@ TEST(registry_is_import_reachable) {
|
||||
/* Port of FindEndingWith portion from Go TestFunctionRegistry in pipeline_test.go */
|
||||
TEST(registry_find_ending_with) {
|
||||
cbm_registry_t *reg = cbm_registry_new();
|
||||
cbm_registry_add(reg, "Foo", "proj.pkg.Foo", "Function");
|
||||
cbm_registry_add(reg, "Bar", "proj.pkg.Bar", "Function");
|
||||
cbm_registry_add(reg, "Foo", "proj.other.Foo", "Function");
|
||||
cbm_registry_add(reg, "transform", "proj.utils.DataProcessor.transform", "Method");
|
||||
cbm_registry_add(reg, "Foo", "proj.pkg.Foo", "Function", false);
|
||||
cbm_registry_add(reg, "Bar", "proj.pkg.Bar", "Function", false);
|
||||
cbm_registry_add(reg, "Foo", "proj.other.Foo", "Function", false);
|
||||
cbm_registry_add(reg, "transform", "proj.utils.DataProcessor.transform", "Method", false);
|
||||
|
||||
/* FindEndingWith "DataProcessor.transform" → 1 match */
|
||||
const char **matches = NULL;
|
||||
@@ -34486,6 +34612,8 @@ SUITE(pipeline) {
|
||||
RUN_TEST(pipeline_call_reference_sequential_parallel_edge_set_parity);
|
||||
RUN_TEST(pipeline_complexity_props_independent_of_worker_order);
|
||||
RUN_TEST(pipeline_incremental_cross_file_call_reference_matches_fresh_full);
|
||||
RUN_TEST(pipeline_registry_ranks_by_extractor_verdict_full_and_incremental);
|
||||
RUN_TEST(pipeline_registry_ranks_test_doubles_last_full_and_incremental);
|
||||
RUN_TEST(pipeline_incremental_changed_target_invalidates_stale_inbound_call_reference);
|
||||
RUN_TEST(pipeline_incremental_parallel_registry_nodes_advance_shared_ids);
|
||||
#if defined(CBM_INCREMENTAL_TEST_API) && CBM_INCREMENTAL_TEST_API
|
||||
|
||||
+271
-157
@@ -193,7 +193,7 @@ TEST(registry_free_null) {
|
||||
|
||||
TEST(registry_add_and_exists) {
|
||||
cbm_registry_t *r = cbm_registry_new();
|
||||
cbm_registry_add(r, "main", "proj.cmd.main", "Function");
|
||||
cbm_registry_add(r, "main", "proj.cmd.main", "Function", false);
|
||||
ASSERT_EQ(cbm_registry_size(r), 1);
|
||||
ASSERT_TRUE(cbm_registry_exists(r, "proj.cmd.main"));
|
||||
ASSERT_FALSE(cbm_registry_exists(r, "proj.cmd.other"));
|
||||
@@ -201,10 +201,114 @@ TEST(registry_add_and_exists) {
|
||||
PASS();
|
||||
}
|
||||
|
||||
/* REG_TEST_PENALTY had no direct coverage. Of the registry call sites in this
|
||||
* suite, none registered a symbol the scorer would treat as a test, so the
|
||||
* layer that keeps production code ahead of test code was never exercised.
|
||||
* These three pin it, and pin where the verdict comes from. */
|
||||
TEST(registry_prefers_non_test_over_test_candidate) {
|
||||
cbm_registry_t *r = cbm_registry_new();
|
||||
cbm_registry_add(r, "helper", "proj.app.helper", "Function", false);
|
||||
cbm_registry_add(r, "helper", "proj.harness.helper", "Function", true);
|
||||
|
||||
cbm_resolution_t res = cbm_registry_resolve(r, "helper", "proj.other", NULL, NULL, 0);
|
||||
ASSERT_STR_EQ(res.qualified_name, "proj.app.helper");
|
||||
cbm_registry_free(r);
|
||||
PASS();
|
||||
}
|
||||
|
||||
/* The verdict is the caller's, not the QN's spelling. "latest" contains "test",
|
||||
* so the scan this replaces read ordinary production code as a test and pushed
|
||||
* it below a real one; "harness" spells nothing, so the same scan read a test
|
||||
* as production. Both verdicts here are the extractor's, and both are right. */
|
||||
TEST(registry_test_verdict_comes_from_the_caller_not_the_qn) {
|
||||
cbm_registry_t *r = cbm_registry_new();
|
||||
cbm_registry_add(r, "parse", "proj.latest.parse", "Function", false);
|
||||
cbm_registry_add(r, "parse", "proj.harness.parse", "Function", true);
|
||||
|
||||
cbm_resolution_t res = cbm_registry_resolve(r, "parse", "proj.other", NULL, NULL, 0);
|
||||
ASSERT_STR_EQ(res.qualified_name, "proj.latest.parse");
|
||||
cbm_registry_free(r);
|
||||
PASS();
|
||||
}
|
||||
|
||||
/* A bucket of one is returned whatever its verdict: the penalty orders
|
||||
* candidates, it does not veto them. */
|
||||
TEST(registry_sole_test_candidate_still_resolves) {
|
||||
cbm_registry_t *r = cbm_registry_new();
|
||||
cbm_registry_add(r, "only", "proj.harness.only", "Function", true);
|
||||
|
||||
cbm_resolution_t res = cbm_registry_resolve(r, "only", "proj.other", NULL, NULL, 0);
|
||||
ASSERT_STR_EQ(res.qualified_name, "proj.harness.only");
|
||||
cbm_registry_free(r);
|
||||
PASS();
|
||||
}
|
||||
|
||||
/* The test-double path rule: whole directory segments named fake, mock, mocks,
|
||||
* testing or testutil, and Go files named mock_*.go or fake_*.go. Anchored, so
|
||||
* the substrings the old QN scan tripped on (inspect, latest, contest) and
|
||||
* near-miss names (fakeroot, mockery, testingutil) stay ordinary code. */
|
||||
TEST(registry_test_double_path_table) {
|
||||
static const char *const doubles[] = {
|
||||
"pkg/client/fake/clientset.go",
|
||||
"fake/x.go",
|
||||
"internal/mocks/store.go",
|
||||
"pkg/mock/store.go",
|
||||
"pkg/testing/fixtures.go",
|
||||
"internal/testutil/helpers.go",
|
||||
"pkg/mock_store.go",
|
||||
"pkg/fake_clock.go",
|
||||
"mock_store.go",
|
||||
"src/fake/deep/nested/util.py",
|
||||
};
|
||||
static const char *const ordinary[] = {
|
||||
"pkg/inspect/x.go", "pkg/latest/x.go", "pkg/contest/x.go", "pkg/fakeroot/x.go",
|
||||
"pkg/mockery/x.go", "pkg/testingutil/x.go", "pkg/mocking.go", "pkg/mock_store.py",
|
||||
"pkg/fake_clock.golang", "pkg/fake.go", "pkg/x/fake", "pkg/Fake/x.go",
|
||||
"pkg/myfake/x.go", "pkg/fake-client/x.go", "testing.go", "",
|
||||
};
|
||||
char why[256];
|
||||
for (size_t i = 0; i < sizeof(doubles) / sizeof(doubles[0]); i++) {
|
||||
if (!cbm_registry_test_double_path(doubles[i])) {
|
||||
snprintf(why, sizeof(why), "expected a test-double path: %s", doubles[i]);
|
||||
FAIL(why);
|
||||
}
|
||||
}
|
||||
for (size_t i = 0; i < sizeof(ordinary) / sizeof(ordinary[0]); i++) {
|
||||
if (cbm_registry_test_double_path(ordinary[i])) {
|
||||
snprintf(why, sizeof(why), "expected an ordinary path: %s", ordinary[i]);
|
||||
FAIL(why);
|
||||
}
|
||||
}
|
||||
ASSERT_FALSE(cbm_registry_test_double_path(NULL));
|
||||
PASS();
|
||||
}
|
||||
|
||||
/* The Swift overload choosers score with the verdicts stored at registration,
|
||||
* carried through their own filtered lists. Both overloads fit the call and tie
|
||||
* on proximity; without the verdicts the smaller QN (the test double) would win
|
||||
* the tie. */
|
||||
TEST(registry_swift_overload_choice_uses_stored_verdicts) {
|
||||
cbm_registry_t *r = cbm_registry_new();
|
||||
const char *double_qn = "proj.Fixtures.Store.load(path:String)";
|
||||
const char *real_qn = "proj.Zeta.Store.load(path:String)";
|
||||
cbm_registry_add(r, "load", double_qn, "Method", true);
|
||||
cbm_registry_add(r, "load", real_qn, "Method", false);
|
||||
cbm_registry_set_swift_signature(r, double_qn, 0, 1, NULL);
|
||||
cbm_registry_set_swift_signature(r, real_qn, 0, 1, NULL);
|
||||
|
||||
CBMCallArg arg = {.keyword = "path"};
|
||||
CBMCall call = {.callee_name = "load", .args = &arg, .arg_count = 1};
|
||||
const char *out[4] = {0};
|
||||
ASSERT_EQ(cbm_registry_swift_candidates(r, &call, "proj.Caller", NULL, 0, out, 4), 1);
|
||||
ASSERT_STR_EQ(out[0], real_qn);
|
||||
cbm_registry_free(r);
|
||||
PASS();
|
||||
}
|
||||
|
||||
TEST(registry_label_of) {
|
||||
cbm_registry_t *r = cbm_registry_new();
|
||||
cbm_registry_add(r, "Foo", "proj.pkg.Foo", "Class");
|
||||
cbm_registry_add(r, "bar", "proj.pkg.bar", "Function");
|
||||
cbm_registry_add(r, "Foo", "proj.pkg.Foo", "Class", false);
|
||||
cbm_registry_add(r, "bar", "proj.pkg.bar", "Function", false);
|
||||
|
||||
ASSERT_STR_EQ(cbm_registry_label_of(r, "proj.pkg.Foo"), "Class");
|
||||
ASSERT_STR_EQ(cbm_registry_label_of(r, "proj.pkg.bar"), "Function");
|
||||
@@ -218,10 +322,10 @@ TEST(registry_label_of) {
|
||||
* method's fenced `#field` twin when the class has one; nothing else moves. */
|
||||
TEST(registry_value_target_field_twin) {
|
||||
cbm_registry_t *r = cbm_registry_new();
|
||||
cbm_registry_add(r, "count", "p.C.count", "Method");
|
||||
cbm_registry_add(r, "count", "p.C.count#field", "Field");
|
||||
cbm_registry_add(r, "size", "p.C.size", "Method");
|
||||
cbm_registry_add(r, "name", "p.C.name", "Field");
|
||||
cbm_registry_add(r, "count", "p.C.count", "Method", false);
|
||||
cbm_registry_add(r, "count", "p.C.count#field", "Field", false);
|
||||
cbm_registry_add(r, "size", "p.C.size", "Method", false);
|
||||
cbm_registry_add(r, "name", "p.C.name", "Field", false);
|
||||
ASSERT_STR_EQ(cbm_registry_value_target(r, "p.C.count"), "p.C.count#field");
|
||||
ASSERT_STR_EQ(cbm_registry_value_target(r, "p.C.size"), "p.C.size");
|
||||
ASSERT_STR_EQ(cbm_registry_value_target(r, "p.C.name"), "p.C.name");
|
||||
@@ -237,9 +341,9 @@ TEST(registry_value_target_field_twin) {
|
||||
|
||||
TEST(registry_find_by_name) {
|
||||
cbm_registry_t *r = cbm_registry_new();
|
||||
cbm_registry_add(r, "main", "proj.cmd.main", "Function");
|
||||
cbm_registry_add(r, "main", "proj.srv.main", "Function");
|
||||
cbm_registry_add(r, "helper", "proj.util.helper", "Function");
|
||||
cbm_registry_add(r, "main", "proj.cmd.main", "Function", false);
|
||||
cbm_registry_add(r, "main", "proj.srv.main", "Function", false);
|
||||
cbm_registry_add(r, "helper", "proj.util.helper", "Function", false);
|
||||
|
||||
const char **out = NULL;
|
||||
int count = 0;
|
||||
@@ -258,8 +362,8 @@ TEST(registry_find_by_name) {
|
||||
|
||||
TEST(registry_no_duplicates) {
|
||||
cbm_registry_t *r = cbm_registry_new();
|
||||
cbm_registry_add(r, "main", "proj.cmd.main", "Function");
|
||||
cbm_registry_add(r, "main", "proj.cmd.main", "Function"); /* duplicate */
|
||||
cbm_registry_add(r, "main", "proj.cmd.main", "Function", false);
|
||||
cbm_registry_add(r, "main", "proj.cmd.main", "Function", false); /* duplicate */
|
||||
ASSERT_EQ(cbm_registry_size(r), 1);
|
||||
|
||||
const char **out = NULL;
|
||||
@@ -275,7 +379,7 @@ TEST(registry_no_duplicates) {
|
||||
|
||||
TEST(resolve_same_module) {
|
||||
cbm_registry_t *r = cbm_registry_new();
|
||||
cbm_registry_add(r, "helper", "proj.pkg.service.helper", "Function");
|
||||
cbm_registry_add(r, "helper", "proj.pkg.service.helper", "Function", false);
|
||||
|
||||
/* Call "helper" from the same module → should resolve */
|
||||
cbm_resolution_t res = cbm_registry_resolve(r, "helper", "proj.pkg.service", NULL, NULL, 0);
|
||||
@@ -297,7 +401,7 @@ TEST(resolve_same_module) {
|
||||
* (false only for a this/super receiver). */
|
||||
TEST(resolve_same_module_only_on_self_receiver) {
|
||||
cbm_registry_t *r = cbm_registry_new();
|
||||
cbm_registry_add(r, "get", "proj.pkg.service.get", "Function");
|
||||
cbm_registry_add(r, "get", "proj.pkg.service.get", "Function", false);
|
||||
const CBMLanguage js = CBM_LANG_JAVASCRIPT;
|
||||
const char *mod = "proj.pkg.service";
|
||||
CBMImport axios_import = {.local_name = "axios", .module_path = "axios", .is_default = true};
|
||||
@@ -457,11 +561,11 @@ TEST(resolve_qualified_receiver_awareness) {
|
||||
const CBMLanguage js = CBM_LANG_JAVASCRIPT;
|
||||
|
||||
/* 1. Register a Method candidate under Class Service: proj.pkg.Service.get */
|
||||
cbm_registry_add(r, "Service", "proj.pkg.Service", "Class");
|
||||
cbm_registry_add(r, "get", "proj.pkg.Service.get", "Method");
|
||||
cbm_registry_add(r, "Service", "proj.pkg.Service", "Class", false);
|
||||
cbm_registry_add(r, "get", "proj.pkg.Service.get", "Method", false);
|
||||
|
||||
/* 2. Register a Module axios */
|
||||
cbm_registry_add(r, "axios", "proj.axios", "Module");
|
||||
cbm_registry_add(r, "axios", "proj.axios", "Module", false);
|
||||
|
||||
/* Test Case A: Unrelated receiver that is a known module/type -> name-only
|
||||
* guess, dropped by the member guard */
|
||||
@@ -513,7 +617,7 @@ TEST(resolve_qualified_receiver_awareness) {
|
||||
/* Test Case F: Function candidate behind an unrelated receiver -> name-only
|
||||
* guess, dropped by the member guard */
|
||||
/* Register unique Function helper.compute */
|
||||
cbm_registry_add(r, "compute", "proj.pkg.helper.compute", "Function");
|
||||
cbm_registry_add(r, "compute", "proj.pkg.helper.compute", "Function", false);
|
||||
{
|
||||
cbm_resolution_t res =
|
||||
cbm_registry_resolve(r, "unrelated.compute", "proj.app", NULL, NULL, 0);
|
||||
@@ -530,9 +634,9 @@ TEST(resolve_qualified_receiver_awareness) {
|
||||
* Foo::Bar::sub()) where the same sub name exists in multiple packages. */
|
||||
TEST(resolve_qualified_disambiguates_same_name) {
|
||||
cbm_registry_t *r = cbm_registry_new();
|
||||
cbm_registry_add(r, "save", "proj.lib.App.Alpha.save", "Function");
|
||||
cbm_registry_add(r, "save", "proj.lib.App.Beta.save", "Function");
|
||||
cbm_registry_add(r, "save", "proj.lib.App.Gamma.save", "Function");
|
||||
cbm_registry_add(r, "save", "proj.lib.App.Alpha.save", "Function", false);
|
||||
cbm_registry_add(r, "save", "proj.lib.App.Beta.save", "Function", false);
|
||||
cbm_registry_add(r, "save", "proj.lib.App.Gamma.save", "Function", false);
|
||||
|
||||
/* Each fully-qualified call routes to its own package. */
|
||||
cbm_resolution_t a =
|
||||
@@ -573,8 +677,8 @@ TEST(resolve_qualified_disambiguates_same_name) {
|
||||
* pick arbitrarily under the high-confidence qualified_suffix strategy. */
|
||||
TEST(resolve_qualified_ambiguous_tail_falls_through) {
|
||||
cbm_registry_t *r = cbm_registry_new();
|
||||
cbm_registry_add(r, "run", "proj.svcA.Foo.Bar.run", "Function");
|
||||
cbm_registry_add(r, "run", "proj.svcB.Foo.Bar.run", "Function");
|
||||
cbm_registry_add(r, "run", "proj.svcA.Foo.Bar.run", "Function", false);
|
||||
cbm_registry_add(r, "run", "proj.svcB.Foo.Bar.run", "Function", false);
|
||||
|
||||
/* "Foo::Bar::run" tail matches BOTH candidates → not unique → fall through. */
|
||||
cbm_resolution_t res =
|
||||
@@ -592,7 +696,7 @@ TEST(resolve_qualified_ambiguous_tail_falls_through) {
|
||||
* `add` callee could ever reach it. */
|
||||
TEST(registry_indexes_by_passed_name_not_qn_tail) {
|
||||
cbm_registry_t *r = cbm_registry_new();
|
||||
cbm_registry_add(r, "add", "proj.lib.add#cfg(test)", "Function");
|
||||
cbm_registry_add(r, "add", "proj.lib.add#cfg(test)", "Function", false);
|
||||
|
||||
cbm_resolution_t res = cbm_registry_resolve(r, "add", "proj.lib.caller", NULL, NULL, 0);
|
||||
ASSERT_STR_EQ(res.qualified_name, "proj.lib.add#cfg(test)");
|
||||
@@ -617,9 +721,9 @@ TEST(registry_indexes_by_passed_name_not_qn_tail) {
|
||||
* lookup a bare module reference needs. */
|
||||
TEST(registry_indexes_a_dotted_name_under_its_tail_too) {
|
||||
cbm_registry_t *r = cbm_registry_new();
|
||||
cbm_registry_add(r, "resource.aws_instance.web", "proj.main.resource.aws_instance.web",
|
||||
"Class");
|
||||
cbm_registry_add(r, "helper.py", "proj.pkg.helper", "Module");
|
||||
cbm_registry_add(r, "resource.aws_instance.web", "proj.main.resource.aws_instance.web", "Class",
|
||||
false);
|
||||
cbm_registry_add(r, "helper.py", "proj.pkg.helper", "Module", false);
|
||||
|
||||
cbm_resolution_t tail = cbm_registry_resolve(r, "web", "proj.main", NULL, NULL, 0);
|
||||
ASSERT_STR_EQ(tail.qualified_name, "proj.main.resource.aws_instance.web");
|
||||
@@ -637,7 +741,7 @@ TEST(registry_indexes_a_dotted_name_under_its_tail_too) {
|
||||
|
||||
TEST(resolve_import_map) {
|
||||
cbm_registry_t *r = cbm_registry_new();
|
||||
cbm_registry_add(r, "Process", "proj.pkg.worker.Process", "Function");
|
||||
cbm_registry_add(r, "Process", "proj.pkg.worker.Process", "Function", false);
|
||||
|
||||
/* Import map: "worker" → "proj.pkg.worker" */
|
||||
const char *keys[] = {"worker"};
|
||||
@@ -660,9 +764,9 @@ TEST(resolve_import_map) {
|
||||
* file. Regression for the @/lib/auth-style import case. */
|
||||
TEST(resolve_import_map_bare_function) {
|
||||
cbm_registry_t *r = cbm_registry_new();
|
||||
cbm_registry_add(r, "requireAdmin", "proj.lib.authorization.requireAdmin", "Function");
|
||||
cbm_registry_add(r, "requireAdmin", "proj.lib.authorization.requireAdmin", "Function", false);
|
||||
/* Same name in another module — without the fix this is what gets picked. */
|
||||
cbm_registry_add(r, "requireAdmin", "proj.lib.users.requireAdmin", "Function");
|
||||
cbm_registry_add(r, "requireAdmin", "proj.lib.users.requireAdmin", "Function", false);
|
||||
|
||||
const char *keys[] = {"requireAdmin"};
|
||||
const char *vals[] = {"proj.lib.authorization"};
|
||||
@@ -683,7 +787,7 @@ TEST(resolve_import_map_bare_function) {
|
||||
* Regression for #875. */
|
||||
TEST(resolve_import_map_bare_alias) {
|
||||
cbm_registry_t *r = cbm_registry_new();
|
||||
cbm_registry_add(r, "scan_bash", "proj.security_scan.scan_bash", "Function");
|
||||
cbm_registry_add(r, "scan_bash", "proj.security_scan.scan_bash", "Function", false);
|
||||
/* Import map: alias "_scan_bash" → FULL SYMBOL QN (not the module). */
|
||||
const char *keys[] = {"_scan_bash"};
|
||||
const char *vals[] = {"proj.security_scan.scan_bash"};
|
||||
@@ -700,10 +804,10 @@ TEST(resolve_import_map_bare_alias) {
|
||||
* already on main via #875/#979; this locks the Yui G1 shape. */
|
||||
TEST(resolve_import_map_aliased_from_import) {
|
||||
cbm_registry_t *r = cbm_registry_new();
|
||||
cbm_registry_add(r, "execute", "proj.services.satori_bridge.gate.execute", "Function");
|
||||
cbm_registry_add(r, "execute", "proj.services.satori_bridge.gate.execute", "Function", false);
|
||||
/* Alias ghost must not win if somehow registered. */
|
||||
cbm_registry_add(r, "bridge_execute", "proj.services.satori_bridge.gate.bridge_execute",
|
||||
"Function");
|
||||
"Function", false);
|
||||
|
||||
const char *keys[] = {"bridge_execute"};
|
||||
const char *vals[] = {"proj.services.satori_bridge.gate.execute"};
|
||||
@@ -719,7 +823,7 @@ TEST(resolve_import_map_aliased_from_import) {
|
||||
|
||||
TEST(resolve_unique_name) {
|
||||
cbm_registry_t *r = cbm_registry_new();
|
||||
cbm_registry_add(r, "UniqueFunc", "proj.deep.path.UniqueFunc", "Function");
|
||||
cbm_registry_add(r, "UniqueFunc", "proj.deep.path.UniqueFunc", "Function", false);
|
||||
|
||||
/* Call "UniqueFunc" — only one candidate project-wide */
|
||||
cbm_resolution_t res =
|
||||
@@ -733,7 +837,7 @@ TEST(resolve_unique_name) {
|
||||
|
||||
TEST(resolve_unresolved) {
|
||||
cbm_registry_t *r = cbm_registry_new();
|
||||
cbm_registry_add(r, "foo", "proj.pkg.foo", "Function");
|
||||
cbm_registry_add(r, "foo", "proj.pkg.foo", "Function", false);
|
||||
|
||||
/* Call "nonexistent" — not in registry */
|
||||
cbm_resolution_t res = cbm_registry_resolve(r, "nonexistent", "proj.other", NULL, NULL, 0);
|
||||
@@ -750,7 +854,7 @@ TEST(resolve_many_nodes) {
|
||||
char name[32], qn[64];
|
||||
snprintf(name, sizeof(name), "func_%d", i);
|
||||
snprintf(qn, sizeof(qn), "proj.pkg.func_%d", i);
|
||||
cbm_registry_add(r, name, qn, "Function");
|
||||
cbm_registry_add(r, name, qn, "Function", false);
|
||||
}
|
||||
ASSERT_EQ(cbm_registry_size(r), 500);
|
||||
|
||||
@@ -788,8 +892,8 @@ TEST(confidence_band_speculative) {
|
||||
|
||||
TEST(resolve_suffix_match) {
|
||||
cbm_registry_t *r = cbm_registry_new();
|
||||
cbm_registry_add(r, "Process", "proj.svcA.Process", "Function");
|
||||
cbm_registry_add(r, "Process", "proj.svcB.Process", "Function");
|
||||
cbm_registry_add(r, "Process", "proj.svcA.Process", "Function", false);
|
||||
cbm_registry_add(r, "Process", "proj.svcB.Process", "Function", false);
|
||||
|
||||
/* Caller in svcA — should prefer svcA via import distance */
|
||||
cbm_resolution_t res = cbm_registry_resolve(r, "Process", "proj.svcA.caller", NULL, NULL, 0);
|
||||
@@ -812,7 +916,7 @@ TEST(resolve_caps_unresolvably_ambiguous_names) {
|
||||
for (int i = 0; i < 300; i++) {
|
||||
char qn[64];
|
||||
snprintf(qn, sizeof(qn), "proj.mod%d.flags", i);
|
||||
cbm_registry_add(r, "flags", qn, "Variable");
|
||||
cbm_registry_add(r, "flags", qn, "Variable", false);
|
||||
}
|
||||
cbm_resolution_t res = cbm_registry_resolve(r, "flags", "proj.other.caller", NULL, NULL, 0);
|
||||
ASSERT_TRUE(res.qualified_name == NULL || res.qualified_name[0] == '\0');
|
||||
@@ -830,7 +934,7 @@ TEST(resolve_caps_unresolvably_ambiguous_names) {
|
||||
|
||||
TEST(resolve_import_map_suffix) {
|
||||
cbm_registry_t *r = cbm_registry_new();
|
||||
cbm_registry_add(r, "Foo", "proj.other.sub.Foo", "Function");
|
||||
cbm_registry_add(r, "Foo", "proj.other.sub.Foo", "Function", false);
|
||||
|
||||
const char *keys[] = {"other"};
|
||||
const char *vals[] = {"proj.other"};
|
||||
@@ -852,7 +956,7 @@ TEST(resolve_is_import_reachable) {
|
||||
/* Test import reachability through unique_name confidence penalty.
|
||||
* is_import_reachable is static, so we test it indirectly. */
|
||||
cbm_registry_t *r = cbm_registry_new();
|
||||
cbm_registry_add(r, "Helper", "proj.shared.utils.Helper", "Function");
|
||||
cbm_registry_add(r, "Helper", "proj.shared.utils.Helper", "Function", false);
|
||||
|
||||
/* With import covering the module → full confidence */
|
||||
const char *keys1[] = {"utils"};
|
||||
@@ -875,7 +979,7 @@ TEST(resolve_is_import_reachable) {
|
||||
TEST(resolve_import_reachable_prefix) {
|
||||
/* "proj.handler.sub.Process" should be reachable via import "proj.handler" */
|
||||
cbm_registry_t *r = cbm_registry_new();
|
||||
cbm_registry_add(r, "Process", "proj.handler.sub.Process", "Function");
|
||||
cbm_registry_add(r, "Process", "proj.handler.sub.Process", "Function", false);
|
||||
|
||||
const char *keys[] = {"handler"};
|
||||
const char *vals[] = {"proj.handler"};
|
||||
@@ -929,8 +1033,8 @@ TEST(reach_cache_memo_matches_uncached_across_files) {
|
||||
|
||||
TEST(negative_import_rejects_unimported) {
|
||||
cbm_registry_t *r = cbm_registry_new();
|
||||
cbm_registry_add(r, "Process", "proj.billing.Process", "Function");
|
||||
cbm_registry_add(r, "Process", "proj.handler.Process", "Function");
|
||||
cbm_registry_add(r, "Process", "proj.billing.Process", "Function", false);
|
||||
cbm_registry_add(r, "Process", "proj.handler.Process", "Function", false);
|
||||
|
||||
/* Import only handler's module — suffix_match should prefer handler */
|
||||
const char *keys[] = {"handler"};
|
||||
@@ -946,8 +1050,8 @@ TEST(negative_import_rejects_unimported) {
|
||||
|
||||
TEST(fuzzy_resolve_single_candidate) {
|
||||
cbm_registry_t *r = cbm_registry_new();
|
||||
cbm_registry_add(r, "CreateOrder", "svcA.handlers.CreateOrder", "Function");
|
||||
cbm_registry_add(r, "ValidateOrder", "svcB.validators.ValidateOrder", "Function");
|
||||
cbm_registry_add(r, "CreateOrder", "svcA.handlers.CreateOrder", "Function", false);
|
||||
cbm_registry_add(r, "ValidateOrder", "svcB.validators.ValidateOrder", "Function", false);
|
||||
|
||||
/* FuzzyResolve should find by simple name even with unknown prefix */
|
||||
cbm_fuzzy_result_t fr =
|
||||
@@ -961,7 +1065,7 @@ TEST(fuzzy_resolve_single_candidate) {
|
||||
|
||||
TEST(fuzzy_resolve_nonexistent) {
|
||||
cbm_registry_t *r = cbm_registry_new();
|
||||
cbm_registry_add(r, "CreateOrder", "svcA.handlers.CreateOrder", "Function");
|
||||
cbm_registry_add(r, "CreateOrder", "svcA.handlers.CreateOrder", "Function", false);
|
||||
|
||||
cbm_fuzzy_result_t fr =
|
||||
cbm_registry_fuzzy_resolve(r, "NonExistent", "svcC.caller", NULL, NULL, 0);
|
||||
@@ -973,8 +1077,8 @@ TEST(fuzzy_resolve_nonexistent) {
|
||||
|
||||
TEST(fuzzy_resolve_multiple_best_by_distance) {
|
||||
cbm_registry_t *r = cbm_registry_new();
|
||||
cbm_registry_add(r, "Process", "svcA.handlers.Process", "Function");
|
||||
cbm_registry_add(r, "Process", "svcB.handlers.Process", "Function");
|
||||
cbm_registry_add(r, "Process", "svcA.handlers.Process", "Function", false);
|
||||
cbm_registry_add(r, "Process", "svcB.handlers.Process", "Function", false);
|
||||
|
||||
/* Caller in svcA — should prefer svcA */
|
||||
cbm_fuzzy_result_t fr =
|
||||
@@ -993,7 +1097,7 @@ TEST(fuzzy_resolve_multiple_best_by_distance) {
|
||||
|
||||
TEST(fuzzy_resolve_deep_name_extraction) {
|
||||
cbm_registry_t *r = cbm_registry_new();
|
||||
cbm_registry_add(r, "DoWork", "myproject.utils.DoWork", "Function");
|
||||
cbm_registry_add(r, "DoWork", "myproject.utils.DoWork", "Function", false);
|
||||
|
||||
/* Deeply qualified callee — should extract "DoWork" */
|
||||
cbm_fuzzy_result_t fr =
|
||||
@@ -1018,7 +1122,7 @@ TEST(fuzzy_resolve_empty_registry) {
|
||||
|
||||
TEST(fuzzy_resolve_confidence_single) {
|
||||
cbm_registry_t *r = cbm_registry_new();
|
||||
cbm_registry_add(r, "Handler", "proj.svc.Handler", "Function");
|
||||
cbm_registry_add(r, "Handler", "proj.svc.Handler", "Function", false);
|
||||
|
||||
cbm_fuzzy_result_t fr =
|
||||
cbm_registry_fuzzy_resolve(r, "unknownPkg.Handler", "proj.caller", NULL, NULL, 0);
|
||||
@@ -1032,8 +1136,8 @@ TEST(fuzzy_resolve_confidence_single) {
|
||||
|
||||
TEST(fuzzy_resolve_confidence_distance) {
|
||||
cbm_registry_t *r = cbm_registry_new();
|
||||
cbm_registry_add(r, "Process", "proj.svcA.Process", "Function");
|
||||
cbm_registry_add(r, "Process", "proj.svcB.Process", "Function");
|
||||
cbm_registry_add(r, "Process", "proj.svcA.Process", "Function", false);
|
||||
cbm_registry_add(r, "Process", "proj.svcB.Process", "Function", false);
|
||||
|
||||
cbm_fuzzy_result_t fr =
|
||||
cbm_registry_fuzzy_resolve(r, "unknownPkg.Process", "proj.svcA.other", NULL, NULL, 0);
|
||||
@@ -1047,7 +1151,7 @@ TEST(fuzzy_resolve_confidence_distance) {
|
||||
|
||||
TEST(fuzzy_penalty_unreachable_import) {
|
||||
cbm_registry_t *r = cbm_registry_new();
|
||||
cbm_registry_add(r, "Handler", "proj.billing.Handler", "Function");
|
||||
cbm_registry_add(r, "Handler", "proj.billing.Handler", "Function", false);
|
||||
|
||||
/* Import for different module → confidence halved */
|
||||
const char *keys[] = {"other"};
|
||||
@@ -1064,7 +1168,7 @@ TEST(fuzzy_penalty_unreachable_import) {
|
||||
|
||||
TEST(fuzzy_no_import_map_passthrough) {
|
||||
cbm_registry_t *r = cbm_registry_new();
|
||||
cbm_registry_add(r, "Handler", "proj.billing.Handler", "Function");
|
||||
cbm_registry_add(r, "Handler", "proj.billing.Handler", "Function", false);
|
||||
|
||||
/* nil import map → full confidence */
|
||||
cbm_fuzzy_result_t fr =
|
||||
@@ -1173,8 +1277,8 @@ TEST(registry_tie_break_is_independent_of_registration_order) {
|
||||
cbm_registry_t *forward = cbm_registry_new();
|
||||
cbm_registry_t *backward = cbm_registry_new();
|
||||
for (int i = 0; i < n; i++) {
|
||||
cbm_registry_add(forward, "dev_name", cands[i], labels[i]);
|
||||
cbm_registry_add(backward, "dev_name", cands[n - 1 - i], labels[n - 1 - i]);
|
||||
cbm_registry_add(forward, "dev_name", cands[i], labels[i], false);
|
||||
cbm_registry_add(backward, "dev_name", cands[n - 1 - i], labels[n - 1 - i], false);
|
||||
}
|
||||
|
||||
/* A far-away caller with no imports: every candidate scores the same. */
|
||||
@@ -1192,10 +1296,10 @@ TEST(registry_tie_break_is_independent_of_registration_order) {
|
||||
/* Equal kind: the smaller QN, from either order. */
|
||||
cbm_registry_t *lex_a = cbm_registry_new();
|
||||
cbm_registry_t *lex_b = cbm_registry_new();
|
||||
cbm_registry_add(lex_a, "sg_next", "proj.tools.virtio.scatterlist.sg_next", "Function");
|
||||
cbm_registry_add(lex_a, "sg_next", "proj.include.linux.scatterlist.sg_next", "Function");
|
||||
cbm_registry_add(lex_b, "sg_next", "proj.include.linux.scatterlist.sg_next", "Function");
|
||||
cbm_registry_add(lex_b, "sg_next", "proj.tools.virtio.scatterlist.sg_next", "Function");
|
||||
cbm_registry_add(lex_a, "sg_next", "proj.tools.virtio.scatterlist.sg_next", "Function", false);
|
||||
cbm_registry_add(lex_a, "sg_next", "proj.include.linux.scatterlist.sg_next", "Function", false);
|
||||
cbm_registry_add(lex_b, "sg_next", "proj.include.linux.scatterlist.sg_next", "Function", false);
|
||||
cbm_registry_add(lex_b, "sg_next", "proj.tools.virtio.scatterlist.sg_next", "Function", false);
|
||||
cbm_resolution_t la =
|
||||
cbm_registry_resolve(lex_a, "sg_next", "proj.drivers.scsi.arm_scsi", NULL, NULL, 0);
|
||||
cbm_resolution_t lb =
|
||||
@@ -1205,9 +1309,9 @@ TEST(registry_tie_break_is_independent_of_registration_order) {
|
||||
|
||||
/* Proximity outranks the tie order: the sibling wins over a shallower stranger. */
|
||||
cbm_registry_t *proximity = cbm_registry_new();
|
||||
cbm_registry_add(proximity, "vnic_rq_free", "proj.lib.vnic_rq_free", "Function");
|
||||
cbm_registry_add(proximity, "vnic_rq_free", "proj.lib.vnic_rq_free", "Function", false);
|
||||
cbm_registry_add(proximity, "vnic_rq_free", "proj.drivers.scsi.fnic.vnic_rq.vnic_rq_free",
|
||||
"Function");
|
||||
"Function", false);
|
||||
cbm_resolution_t nr = cbm_registry_resolve(proximity, "vnic_rq_free",
|
||||
"proj.drivers.scsi.fnic.fnic_res", NULL, NULL, 0);
|
||||
ASSERT_STR_EQ(nr.qualified_name, "proj.drivers.scsi.fnic.vnic_rq.vnic_rq_free");
|
||||
@@ -1231,13 +1335,14 @@ TEST(registry_tie_prefers_callable_over_property) {
|
||||
cbm_registry_add(
|
||||
r, "where",
|
||||
"proj.exposed-core.src.main.kotlin.org.jetbrains.exposed.v1.core.AbstractQuery.where",
|
||||
"Variable");
|
||||
"Variable", false);
|
||||
cbm_registry_add(r, "where",
|
||||
"proj.exposed-jdbc.src.main.kotlin.org.jetbrains.exposed.v1.jdbc.Query.where",
|
||||
"Method");
|
||||
"Method", false);
|
||||
/* A deeper method still beats a shallower field: kind decides before order. */
|
||||
cbm_registry_add(r, "limit", "proj.exposed-core.Table.limit", "Field");
|
||||
cbm_registry_add(r, "limit", "proj.exposed-jdbc.src.main.kotlin.jdbc.Query.limit", "Method");
|
||||
cbm_registry_add(r, "limit", "proj.exposed-core.Table.limit", "Field", false);
|
||||
cbm_registry_add(r, "limit", "proj.exposed-jdbc.src.main.kotlin.jdbc.Query.limit", "Method",
|
||||
false);
|
||||
|
||||
const char *caller =
|
||||
"proj.exposed-tests.src.test.kotlin.org.jetbrains.exposed.v1.tests.SelectTests";
|
||||
@@ -1258,8 +1363,8 @@ TEST(registry_tie_prefers_callable_over_property) {
|
||||
* order alone, which is still a pure function of the candidate set (O9). */
|
||||
TEST(registry_tie_ignores_nesting_depth) {
|
||||
cbm_registry_t *r = cbm_registry_new();
|
||||
cbm_registry_add(r, "filter", "proj.django.template.library.Library.filter", "Method");
|
||||
cbm_registry_add(r, "filter", "proj.django.db.models.query.QuerySet.filter", "Method");
|
||||
cbm_registry_add(r, "filter", "proj.django.template.library.Library.filter", "Method", false);
|
||||
cbm_registry_add(r, "filter", "proj.django.db.models.query.QuerySet.filter", "Method", false);
|
||||
cbm_resolution_t f =
|
||||
cbm_registry_resolve(r, "filter", "proj.tests.lookup.tests", NULL, NULL, 0);
|
||||
ASSERT_STR_EQ(f.qualified_name, "proj.django.db.models.query.QuerySet.filter");
|
||||
@@ -1281,23 +1386,25 @@ TEST(registry_tie_prefers_csharp_implementation_over_ref_stub) {
|
||||
cbm_registry_add_lang(
|
||||
r, "MemoryStream",
|
||||
"proj.src.libraries.System.Runtime.ref.System.Runtime.MemoryStream.MemoryStream", "Method",
|
||||
CBM_LANG_CSHARP);
|
||||
CBM_LANG_CSHARP, false);
|
||||
cbm_registry_add_lang(
|
||||
r, "MemoryStream",
|
||||
"proj.src.libraries.System.Private.CoreLib.src.System.IO.MemoryStream.MemoryStream",
|
||||
"Method", CBM_LANG_CSHARP);
|
||||
"Method", CBM_LANG_CSHARP, false);
|
||||
/* Equal depth, "ref" sorts before "src" (WithCancellation). */
|
||||
cbm_registry_add_lang(r, "WithCancellation",
|
||||
"proj.src.libraries.System.Linq.Parallel.ref.System.Linq."
|
||||
"ParallelEnumerable.WithCancellation",
|
||||
"Method", CBM_LANG_CSHARP);
|
||||
"Method", CBM_LANG_CSHARP, false);
|
||||
cbm_registry_add_lang(r, "WithCancellation",
|
||||
"proj.src.libraries.System.Linq.Parallel.src.System.Linq."
|
||||
"ParallelEnumerable.WithCancellation",
|
||||
"Method", CBM_LANG_CSHARP);
|
||||
"Method", CBM_LANG_CSHARP, false);
|
||||
/* Not C#: a `ref` directory gets no special treatment. */
|
||||
cbm_registry_add_lang(r, "resolve", "proj.lib.ref.Resolver.resolve", "Method", CBM_LANG_JAVA);
|
||||
cbm_registry_add_lang(r, "resolve", "proj.lib.src.Resolver.resolve", "Method", CBM_LANG_JAVA);
|
||||
cbm_registry_add_lang(r, "resolve", "proj.lib.ref.Resolver.resolve", "Method", CBM_LANG_JAVA,
|
||||
false);
|
||||
cbm_registry_add_lang(r, "resolve", "proj.lib.src.Resolver.resolve", "Method", CBM_LANG_JAVA,
|
||||
false);
|
||||
|
||||
const char *caller = "proj.src.libraries.System.Security.Cryptography.src.CapiHelper";
|
||||
cbm_resolution_t ms = cbm_registry_resolve(r, "MemoryStream", caller, NULL, NULL, 0);
|
||||
@@ -1353,10 +1460,10 @@ static cbm_resolution_t xl_resolve(const cbm_registry_t *r, CBMLanguage lang, co
|
||||
|
||||
TEST(name_guess_vetoed_across_languages) {
|
||||
cbm_registry_t *r = cbm_registry_new();
|
||||
cbm_registry_add_lang(r, "get", "builtins.dict.get", "Method", CBM_LANG_PYTHON);
|
||||
cbm_registry_add_lang(r, "patch", "proj.web.Panel.patch", "Function", CBM_LANG_TSX);
|
||||
cbm_registry_add_lang(r, "patch", "proj.api.client.patch", "Function", CBM_LANG_PYTHON);
|
||||
cbm_registry_add(r, "legacy", "proj.x.legacy", "Function"); /* unknown language */
|
||||
cbm_registry_add_lang(r, "get", "builtins.dict.get", "Method", CBM_LANG_PYTHON, false);
|
||||
cbm_registry_add_lang(r, "patch", "proj.web.Panel.patch", "Function", CBM_LANG_TSX, false);
|
||||
cbm_registry_add_lang(r, "patch", "proj.api.client.patch", "Function", CBM_LANG_PYTHON, false);
|
||||
cbm_registry_add(r, "legacy", "proj.x.legacy", "Function", false); /* unknown language */
|
||||
|
||||
/* JS `app.get`: the only "get" is Python's -> EMPTY, not unique_name. */
|
||||
cbm_resolution_t res = xl_resolve(r, CBM_LANG_JAVASCRIPT, "app.get", "proj.app");
|
||||
@@ -1402,10 +1509,11 @@ TEST(name_guess_vetoed_across_languages) {
|
||||
TEST(same_module_vetoed_across_languages) {
|
||||
cbm_registry_t *r = cbm_registry_new();
|
||||
cbm_registry_add_lang(r, "reducer_worker", "proj.tests.cov.reducer_worker", "Function",
|
||||
CBM_LANG_PYTHON);
|
||||
CBM_LANG_PYTHON, false);
|
||||
cbm_registry_add_lang(r, "reducer_worker", "proj.other.reducer_worker", "Function",
|
||||
CBM_LANG_PYTHON);
|
||||
cbm_registry_add_lang(r, "stat_child", "proj.tests.cov.stat_child", "Function", CBM_LANG_C);
|
||||
CBM_LANG_PYTHON, false);
|
||||
cbm_registry_add_lang(r, "stat_child", "proj.tests.cov.stat_child", "Function", CBM_LANG_C,
|
||||
false);
|
||||
|
||||
cbm_resolution_t res =
|
||||
cbm_registry_resolve(r, "reducer_worker", "proj.tests.cov", NULL, NULL, 0);
|
||||
@@ -1562,11 +1670,11 @@ TEST(c_bare_call_does_not_weigh_field_namesakes) {
|
||||
* A bare C call never names a member, so its Field namesakes do not
|
||||
* compete: the function is the unique name it was in v0.11.0. */
|
||||
cbm_registry_t *r = cbm_registry_new();
|
||||
cbm_registry_add_lang(r, "put_page", "proj.include.linux.mm.put_page", "Function",
|
||||
CBM_LANG_CPP);
|
||||
cbm_registry_add_lang(r, "put_page", "proj.include.linux.mm.put_page", "Function", CBM_LANG_CPP,
|
||||
false);
|
||||
cbm_registry_add_lang(r, "put_page",
|
||||
"proj.arch.arm64.include.asm.kvm_pgtable.kvm_pgtable_mm_ops.put_page",
|
||||
"Field", CBM_LANG_CPP);
|
||||
"Field", CBM_LANG_CPP, false);
|
||||
const char *mod = "proj.arch.arm64.kernel.elfcore";
|
||||
cbm_resolution_t res =
|
||||
cbm_registry_resolve_call_lang(r, "put_page", mod, NULL, NULL, 0, CBM_LANG_C);
|
||||
@@ -1581,13 +1689,14 @@ TEST(c_bare_call_does_not_weigh_field_namesakes) {
|
||||
/* A name only members carry is no bare C call target at all -- neither a
|
||||
* struct member nor a C++ method: redis's libc `read()` became the unique
|
||||
* name of a Qt adapter's `read` Method once only Fields were set aside. */
|
||||
cbm_registry_add_lang(r, "on_close", "proj.net.sock.sock_ops.on_close", "Field", CBM_LANG_C);
|
||||
cbm_registry_add_lang(r, "on_close", "proj.net.sock.sock_ops.on_close", "Field", CBM_LANG_C,
|
||||
false);
|
||||
res = cbm_registry_resolve_call_lang(r, "on_close", mod, NULL, NULL, 0, CBM_LANG_C);
|
||||
ASSERT_TRUE(res.qualified_name == NULL || res.qualified_name[0] == '\0');
|
||||
cbm_registry_add_lang(r, "read", "proj.deps.hiredis.adapters.qt.RedisQtAdapter.read", "Method",
|
||||
CBM_LANG_CPP);
|
||||
cbm_registry_add_lang(r, "read", "proj.src.connection.ConnectionType.read", "Field",
|
||||
CBM_LANG_C);
|
||||
CBM_LANG_CPP, false);
|
||||
cbm_registry_add_lang(r, "read", "proj.src.connection.ConnectionType.read", "Field", CBM_LANG_C,
|
||||
false);
|
||||
res = cbm_registry_resolve_call_lang(r, "read", mod, NULL, NULL, 0, CBM_LANG_C);
|
||||
ASSERT_TRUE(res.qualified_name == NULL || res.qualified_name[0] == '\0');
|
||||
/* C++ still reaches the method by name. */
|
||||
@@ -1652,12 +1761,12 @@ TEST(unique_field_qn_needs_exactly_one_field_of_that_name) {
|
||||
* the same name is not a candidate for a member at all. */
|
||||
cbm_registry_t *r = cbm_registry_new();
|
||||
ASSERT_NOT_NULL(r);
|
||||
cbm_registry_add(r, "only_here", "proj.a.Alpha.only_here", "Field");
|
||||
cbm_registry_add(r, "count", "proj.a.Alpha.count", "Field");
|
||||
cbm_registry_add(r, "count", "proj.b.Beta.count", "Field");
|
||||
cbm_registry_add(r, "close", "proj.cb.Callbacks.close", "Field");
|
||||
cbm_registry_add(r, "close", "proj.io.close", "Function");
|
||||
cbm_registry_add(r, "lonely_fn", "proj.io.lonely_fn", "Function");
|
||||
cbm_registry_add(r, "only_here", "proj.a.Alpha.only_here", "Field", false);
|
||||
cbm_registry_add(r, "count", "proj.a.Alpha.count", "Field", false);
|
||||
cbm_registry_add(r, "count", "proj.b.Beta.count", "Field", false);
|
||||
cbm_registry_add(r, "close", "proj.cb.Callbacks.close", "Field", false);
|
||||
cbm_registry_add(r, "close", "proj.io.close", "Function", false);
|
||||
cbm_registry_add(r, "lonely_fn", "proj.io.lonely_fn", "Function", false);
|
||||
|
||||
const char *only = cbm_registry_unique_field_qn(r, "only_here");
|
||||
ASSERT_NOT_NULL(only);
|
||||
@@ -1746,7 +1855,7 @@ TEST(generated_member_registers_by_qn_only) {
|
||||
ASSERT_EQ(n, 0);
|
||||
cbm_resolution_t res = cbm_registry_resolve(r, "v.y", "proj.app.Caller", NULL, NULL, 0);
|
||||
ASSERT_NULL(res.qualified_name);
|
||||
cbm_registry_add(r, "x", "proj.geo.Point.Point.x", "Method");
|
||||
cbm_registry_add(r, "x", "proj.geo.Point.Point.x", "Method", false);
|
||||
res = cbm_registry_resolve(r, "v.x", "proj.app.Caller", NULL, NULL, 0);
|
||||
ASSERT_STR_EQ(res.qualified_name, "proj.geo.Point.Point.x");
|
||||
/* Degenerate input registers nothing. */
|
||||
@@ -2403,8 +2512,8 @@ TEST(go_suppress_keeps_typed_and_import_aware_matches) {
|
||||
* Signal.send). Regression guard for #1000. */
|
||||
TEST(resolve_import_map_alias_with_suffix_hits_method) {
|
||||
cbm_registry_t *r = cbm_registry_new();
|
||||
cbm_registry_add(r, "user_logged_in", "proj.auth.signals.user_logged_in", "Variable");
|
||||
cbm_registry_add(r, "send", "proj.auth.signals.user_logged_in.send", "Method");
|
||||
cbm_registry_add(r, "user_logged_in", "proj.auth.signals.user_logged_in", "Variable", false);
|
||||
cbm_registry_add(r, "send", "proj.auth.signals.user_logged_in.send", "Method", false);
|
||||
const char *keys[] = {"user_logged_in"};
|
||||
const char *vals[] = {"proj.auth.signals.user_logged_in"};
|
||||
cbm_resolution_t res =
|
||||
@@ -2418,7 +2527,7 @@ TEST(resolve_import_map_alias_with_suffix_hits_method) {
|
||||
TEST(swift_overload_labels_defaults_and_trailing_closure) {
|
||||
cbm_registry_t *r = cbm_registry_new();
|
||||
ASSERT_NOT_NULL(r);
|
||||
cbm_registry_add(r, "Service", "proj.Service", "Class");
|
||||
cbm_registry_add(r, "Service", "proj.Service", "Class", false);
|
||||
const char *qns[] = {
|
||||
"proj.Service.work(flag:Bool)",
|
||||
"proj.Service.work(name:String)",
|
||||
@@ -2449,7 +2558,7 @@ TEST(swift_overload_labels_defaults_and_trailing_closure) {
|
||||
"combine", "repeated", "repeated", "unlabeled"};
|
||||
const uint8_t counts[] = {1, 1, 1, 1, 2, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 2, 2, 2, 2, 2};
|
||||
for (int i = 0; i < 22; i++) {
|
||||
cbm_registry_add(r, names[i], qns[i], "Method");
|
||||
cbm_registry_add(r, names[i], qns[i], "Method", false);
|
||||
uint64_t defaults = (i == 5 || i == 15) ? UINT64_C(1) << 1
|
||||
: i == 17 || i >= 19 ? UINT64_C(1)
|
||||
: 0;
|
||||
@@ -2460,7 +2569,7 @@ TEST(swift_overload_labels_defaults_and_trailing_closure) {
|
||||
const char *generic_qns[] = {"proj.Service.constrained<T;where T==()=>Void>(_:T)",
|
||||
"proj.Service.constrained<T>(_:T)async"};
|
||||
for (int i = 0; i < 2; i++) {
|
||||
cbm_registry_add(r, "constrained", generic_qns[i], "Method");
|
||||
cbm_registry_add(r, "constrained", generic_qns[i], "Method", false);
|
||||
cbm_registry_set_swift_signature(r, generic_qns[i], 0, 1, NULL);
|
||||
}
|
||||
CBMCallArg generic_arg = {0};
|
||||
@@ -2468,7 +2577,7 @@ TEST(swift_overload_labels_defaults_and_trailing_closure) {
|
||||
.callee_name = "Service.constrained", .args = &generic_arg, .arg_count = 1};
|
||||
ASSERT_EQ(cbm_registry_swift_candidates(r, &generic_call, "proj.Caller", NULL, 0, out, 8), 2);
|
||||
const char *nested = "proj.Service.nested<T,U;where T==Box<[Int]>,U==()=>Void>(_:T)=>Int";
|
||||
cbm_registry_add(r, "nested", nested, "Method");
|
||||
cbm_registry_add(r, "nested", nested, "Method", false);
|
||||
cbm_registry_set_swift_signature(r, nested, 0, 1, NULL);
|
||||
generic_call.callee_name = "Service.nested";
|
||||
ASSERT_EQ(cbm_registry_swift_candidates(r, &generic_call, "proj.Caller", NULL, 0, out, 8), 1);
|
||||
@@ -2476,7 +2585,7 @@ TEST(swift_overload_labels_defaults_and_trailing_closure) {
|
||||
const char *returns[] = {"proj.Service.pick()=>Int",
|
||||
"proj.Service.pick()async=>(A/Item)=>B/Item"};
|
||||
for (int i = 0; i < 2; i++) {
|
||||
cbm_registry_add(r, "pick", returns[i], "Method");
|
||||
cbm_registry_add(r, "pick", returns[i], "Method", false);
|
||||
cbm_registry_set_swift_signature(r, returns[i], 0, 0, NULL);
|
||||
}
|
||||
CBMCall returned = {.callee_name = "Service.pick"};
|
||||
@@ -2484,7 +2593,7 @@ TEST(swift_overload_labels_defaults_and_trailing_closure) {
|
||||
const char *malformed[] = {"proj.Service.bad()=>", "proj.Service.bad()=>[Int",
|
||||
"proj.Service.bad()=>Int)"};
|
||||
for (int i = 0; i < 3; i++) {
|
||||
cbm_registry_add(r, "bad", malformed[i], "Method");
|
||||
cbm_registry_add(r, "bad", malformed[i], "Method", false);
|
||||
cbm_registry_set_swift_signature(r, malformed[i], 0, 0, NULL);
|
||||
}
|
||||
returned.callee_name = "Service.bad";
|
||||
@@ -2493,7 +2602,7 @@ TEST(swift_overload_labels_defaults_and_trailing_closure) {
|
||||
for (int i = 0; i < 2; i++) {
|
||||
char qn[128];
|
||||
snprintf(qn, sizeof(qn), "proj.Service.%s(_:A,_:A)=>Bool", operators[i]);
|
||||
cbm_registry_add(r, operators[i], qn, "Method");
|
||||
cbm_registry_add(r, operators[i], qn, "Method", false);
|
||||
cbm_registry_set_swift_signature(r, qn, 0, 2, NULL);
|
||||
CBMCallArg operands[2] = {{0}};
|
||||
CBMCall operation = {.callee_name = operators[i], .args = operands, .arg_count = 2};
|
||||
@@ -2657,14 +2766,14 @@ TEST(swift_manifest_root_targets_respect_nested_package_boundaries) {
|
||||
const char *methods[] = {"proj.Root.Ext.Service.run()", "proj.Nested.Ext.Service.run()",
|
||||
"proj.Unknown.Ext.Service.run()"};
|
||||
for (int i = 0; i < 5; i++) {
|
||||
cbm_registry_add(r, "Service", qns[i], "Class");
|
||||
cbm_registry_add(r, "Service", qns[i], "Class", false);
|
||||
ASSERT_GT(cbm_gbuf_upsert_node(graph, "Class", "Service", qns[i], files[i], 1, 3,
|
||||
i == 0 || i == 2 ? "{\"swift_type_path\":\"Service\"}"
|
||||
: "{\"swift_extension\":\"Service\"}"),
|
||||
0);
|
||||
}
|
||||
for (int i = 0; i < 3; i++) {
|
||||
cbm_registry_add(r, "run", methods[i], "Method");
|
||||
cbm_registry_add(r, "run", methods[i], "Method", false);
|
||||
cbm_registry_set_swift_signature(r, methods[i], 0, 0, "()");
|
||||
}
|
||||
cbm_registry_build_swift_owners(r, graph, root, NULL, 0);
|
||||
@@ -2701,12 +2810,12 @@ TEST(swift_receiver_resolution_is_independent_of_registration_order) {
|
||||
for (int reverse = 0; reverse < 2; reverse++) {
|
||||
cbm_registry_t *r = cbm_registry_new();
|
||||
ASSERT_NOT_NULL(r);
|
||||
cbm_registry_add(r, "Service", "proj.alpha.Service", "Class");
|
||||
cbm_registry_add(r, "Service", "proj.beta.Service", "Class");
|
||||
cbm_registry_add(r, "Service", "proj.alpha.Service", "Class", false);
|
||||
cbm_registry_add(r, "Service", "proj.beta.Service", "Class", false);
|
||||
const char *first = reverse ? beta : alpha;
|
||||
const char *second = reverse ? alpha : beta;
|
||||
cbm_registry_add(r, "work", first, "Method");
|
||||
cbm_registry_add(r, "work", second, "Method");
|
||||
cbm_registry_add(r, "work", first, "Method", false);
|
||||
cbm_registry_add(r, "work", second, "Method", false);
|
||||
cbm_registry_set_swift_signature(r, first, 0, 1, NULL);
|
||||
cbm_registry_set_swift_signature(r, second, 0, 1, NULL);
|
||||
|
||||
@@ -2725,11 +2834,11 @@ TEST(swift_receiver_resolution_is_independent_of_registration_order) {
|
||||
TEST(swift_multiple_trailing_closures_match_labels) {
|
||||
cbm_registry_t *r = cbm_registry_new();
|
||||
ASSERT_NOT_NULL(r);
|
||||
cbm_registry_add(r, "Service", "proj.Service", "Class");
|
||||
cbm_registry_add(r, "Service", "proj.Service", "Class", false);
|
||||
const char *one_qn = "proj.Service.handle(completion:()=>Void)";
|
||||
const char *two_qn = "proj.Service.handle(completion:()=>Void,onError:()=>Void)";
|
||||
cbm_registry_add(r, "handle", one_qn, "Method");
|
||||
cbm_registry_add(r, "handle", two_qn, "Method");
|
||||
cbm_registry_add(r, "handle", one_qn, "Method", false);
|
||||
cbm_registry_add(r, "handle", two_qn, "Method", false);
|
||||
cbm_registry_set_swift_signature(r, one_qn, 0, 1, NULL);
|
||||
cbm_registry_set_swift_signature(r, two_qn, 0, 2, NULL);
|
||||
|
||||
@@ -2762,7 +2871,7 @@ TEST(swift_multiple_trailing_closures_match_labels) {
|
||||
* closure can skip an earlier defaulted closure and bind the required one. */
|
||||
const char *delayed_qn =
|
||||
"proj.Service.delayed(onDismiss:(()=>Void)?,content:()=>Void,onError:(()=>Void)?)";
|
||||
cbm_registry_add(r, "delayed", delayed_qn, "Method");
|
||||
cbm_registry_add(r, "delayed", delayed_qn, "Method", false);
|
||||
cbm_registry_set_swift_signature(r, delayed_qn, (UINT64_C(1) << 0) | (UINT64_C(1) << 2), 3,
|
||||
NULL);
|
||||
call.callee_name = "Service.delayed";
|
||||
@@ -2779,9 +2888,9 @@ TEST(swift_multiple_trailing_closures_match_labels) {
|
||||
TEST(swift_repeated_labels_skip_default_before_next_trailing_label) {
|
||||
cbm_registry_t *r = cbm_registry_new();
|
||||
ASSERT_NOT_NULL(r);
|
||||
cbm_registry_add(r, "Service", "proj.Service", "Class");
|
||||
cbm_registry_add(r, "Service", "proj.Service", "Class", false);
|
||||
const char *qn = "proj.Service.repeated(a:()=>Void,b:()=>Void,a:()=>Void,b:()=>Void)";
|
||||
cbm_registry_add(r, "repeated", qn, "Method");
|
||||
cbm_registry_add(r, "repeated", qn, "Method", false);
|
||||
cbm_registry_set_swift_signature(r, qn, (UINT64_C(1) << 0) | (UINT64_C(1) << 3), 4, NULL);
|
||||
const char *labels[3] = {NULL, "a", "b"};
|
||||
CBMCall call = {.callee_name = "Service.repeated",
|
||||
@@ -2800,9 +2909,9 @@ TEST(swift_repeated_labels_skip_default_before_next_trailing_label) {
|
||||
TEST(swift_repeated_labels_do_not_skip_default_past_next_trailing_label) {
|
||||
cbm_registry_t *r = cbm_registry_new();
|
||||
ASSERT_NOT_NULL(r);
|
||||
cbm_registry_add(r, "Service", "proj.Service", "Class");
|
||||
cbm_registry_add(r, "Service", "proj.Service", "Class", false);
|
||||
const char *qn = "proj.Service.repeated(a:()=>Void,b:()=>Void,a:()=>Void,b:()=>Void)";
|
||||
cbm_registry_add(r, "repeated", qn, "Method");
|
||||
cbm_registry_add(r, "repeated", qn, "Method", false);
|
||||
cbm_registry_set_swift_signature(r, qn, (UINT64_C(1) << 0) | (UINT64_C(1) << 1), 4, NULL);
|
||||
const char *labels[2] = {NULL, "b"};
|
||||
CBMCall call = {.callee_name = "Service.repeated",
|
||||
@@ -2822,9 +2931,9 @@ TEST(swift_repeated_labels_do_not_skip_default_past_next_trailing_label) {
|
||||
TEST(swift_variadic_trailing_closures_do_not_precede_parenthesized_argument) {
|
||||
cbm_registry_t *r = cbm_registry_new();
|
||||
ASSERT_NOT_NULL(r);
|
||||
cbm_registry_add(r, "Service", "proj.Service", "Class");
|
||||
cbm_registry_add(r, "Service", "proj.Service", "Class", false);
|
||||
const char *qn = "proj.Service.f(callbacks:(()=>Void)~,flag:Bool,done:()=>Void)";
|
||||
cbm_registry_add(r, "f", qn, "Method");
|
||||
cbm_registry_add(r, "f", qn, "Method", false);
|
||||
cbm_registry_set_swift_signature(r, qn, UINT64_C(1) << 1, 3, NULL);
|
||||
CBMCallArg arg = {.keyword = "flag"};
|
||||
const char *labels[2] = {NULL, "done"};
|
||||
@@ -2845,17 +2954,17 @@ TEST(swift_variadic_trailing_closures_do_not_precede_parenthesized_argument) {
|
||||
TEST(swift_overloaded_bucket_fails_closed) {
|
||||
cbm_registry_t *r = cbm_registry_new();
|
||||
ASSERT_NOT_NULL(r);
|
||||
cbm_registry_add(r, "Service", "proj.Service", "Class");
|
||||
cbm_registry_add(r, "Service", "proj.Service", "Class", false);
|
||||
const char *first_qn = "proj.Service.work(flag:Bool)";
|
||||
cbm_registry_add(r, "work", first_qn, "Method");
|
||||
cbm_registry_add(r, "work", first_qn, "Method", false);
|
||||
cbm_registry_set_swift_signature(r, first_qn, 0, 1, NULL);
|
||||
const char *other = "proj.Other.work(flag:Bool)";
|
||||
cbm_registry_add(r, "work", other, "Method");
|
||||
cbm_registry_add(r, "work", other, "Method", false);
|
||||
cbm_registry_set_swift_signature(r, other, 0, 1, NULL);
|
||||
char qns[256][64];
|
||||
for (int i = 1; i < 256; i++) {
|
||||
snprintf(qns[i], sizeof(qns[i]), "proj.Other%d.work(flag:Bool)", i);
|
||||
cbm_registry_add(r, "work", qns[i], "Method");
|
||||
cbm_registry_add(r, "work", qns[i], "Method", false);
|
||||
cbm_registry_set_swift_signature(r, qns[i], 0, 1, NULL);
|
||||
}
|
||||
CBMCall call = {.callee_name = "Service.work"};
|
||||
@@ -2921,11 +3030,11 @@ TEST(python_symbol_scope_len_preserves_file_identity) {
|
||||
TEST(registry_python_package_scope_is_explicit) {
|
||||
cbm_registry_t *r = cbm_registry_new();
|
||||
ASSERT_NOT_NULL(r);
|
||||
cbm_registry_add(r, "Field", "proj.forms.fields.Field", "Class");
|
||||
cbm_registry_add(r, "Field", "proj.db.fields.Field", "Class");
|
||||
cbm_registry_add(r, "Field", "proj.db.fields.__init__.Field", "Class");
|
||||
cbm_registry_add(r, "Field", "proj.db.plain.Field", "Class");
|
||||
cbm_registry_add(r, "Field", "proj.db.index.Field", "Class");
|
||||
cbm_registry_add(r, "Field", "proj.forms.fields.Field", "Class", false);
|
||||
cbm_registry_add(r, "Field", "proj.db.fields.Field", "Class", false);
|
||||
cbm_registry_add(r, "Field", "proj.db.fields.__init__.Field", "Class", false);
|
||||
cbm_registry_add(r, "Field", "proj.db.plain.Field", "Class", false);
|
||||
cbm_registry_add(r, "Field", "proj.db.index.Field", "Class", false);
|
||||
const char *keys[] = {"forms"};
|
||||
const char *vals[] = {"proj.forms"};
|
||||
const char *module = "proj.db.fields.__init__";
|
||||
@@ -2957,11 +3066,11 @@ TEST(registry_python_package_scope_is_explicit) {
|
||||
TEST(registry_python_scope_keeps_import_precedence_and_raw_scoring) {
|
||||
cbm_registry_t *r = cbm_registry_new();
|
||||
ASSERT_NOT_NULL(r);
|
||||
cbm_registry_add(r, "Field", "proj.vendor.Field", "Class");
|
||||
cbm_registry_add(r, "Field", "proj.pkg.Field", "Class");
|
||||
cbm_registry_add(r, "Field", "proj.pkg.__init__.Field", "Class");
|
||||
cbm_registry_add(r, "choose", "proj.pkg.__init__.child.choose", "Function");
|
||||
cbm_registry_add(r, "choose", "proj.pkg.peer.choose", "Function");
|
||||
cbm_registry_add(r, "Field", "proj.vendor.Field", "Class", false);
|
||||
cbm_registry_add(r, "Field", "proj.pkg.Field", "Class", false);
|
||||
cbm_registry_add(r, "Field", "proj.pkg.__init__.Field", "Class", false);
|
||||
cbm_registry_add(r, "choose", "proj.pkg.__init__.child.choose", "Function", false);
|
||||
cbm_registry_add(r, "choose", "proj.pkg.peer.choose", "Function", false);
|
||||
const char *keys[] = {"Field"};
|
||||
const char *vals[] = {"proj.vendor.Field"};
|
||||
bool seeded = cbm_registry_size(r) == 5;
|
||||
@@ -2985,14 +3094,14 @@ TEST(registry_python_scope_keeps_import_precedence_and_raw_scoring) {
|
||||
TEST(registry_python_scope_cache_modes_and_raw_lineage) {
|
||||
cbm_registry_t *r = cbm_registry_new();
|
||||
ASSERT_NOT_NULL(r);
|
||||
cbm_registry_add(r, "Entry", "proj.pkg.Entry", "Function");
|
||||
cbm_registry_add(r, "Entry", "proj.pkg.__init__.Entry", "Function");
|
||||
cbm_registry_add(r, "Reject", "proj.pkg.Reject", "Table");
|
||||
cbm_registry_add(r, "Reject", "proj.pkg.__init__.Reject", "Function");
|
||||
cbm_registry_add(r, "Accept", "proj.pkg.Accept", "Function");
|
||||
cbm_registry_add(r, "Accept", "proj.pkg.__init__.Accept", "Table");
|
||||
cbm_registry_add(r, "Relation", "proj.pkg.Relation", "View");
|
||||
cbm_registry_add(r, "Relation", "proj.pkg.__init__.Relation", "Table");
|
||||
cbm_registry_add(r, "Entry", "proj.pkg.Entry", "Function", false);
|
||||
cbm_registry_add(r, "Entry", "proj.pkg.__init__.Entry", "Function", false);
|
||||
cbm_registry_add(r, "Reject", "proj.pkg.Reject", "Table", false);
|
||||
cbm_registry_add(r, "Reject", "proj.pkg.__init__.Reject", "Function", false);
|
||||
cbm_registry_add(r, "Accept", "proj.pkg.Accept", "Function", false);
|
||||
cbm_registry_add(r, "Accept", "proj.pkg.__init__.Accept", "Table", false);
|
||||
cbm_registry_add(r, "Relation", "proj.pkg.Relation", "View", false);
|
||||
cbm_registry_add(r, "Relation", "proj.pkg.__init__.Relation", "Table", false);
|
||||
bool seeded = cbm_registry_size(r) == 8;
|
||||
bool correct = true;
|
||||
/* Each cache lifetime has one fixed module/import map. Alternate modes in
|
||||
@@ -3070,12 +3179,12 @@ TEST(registry_python_scope_complete_candidate_boundaries) {
|
||||
int tn = snprintf(target, sizeof(target), "%s.%s", scope, name);
|
||||
bool shape = mn > 0 && (size_t)mn < sizeof(module) && tn > 0 && (size_t)tn == lengths[i] &&
|
||||
(size_t)tn < sizeof(target);
|
||||
cbm_registry_add(r, name, target, "Function");
|
||||
cbm_registry_add(r, name, target, "Function", false);
|
||||
if (lengths[i] > 511) {
|
||||
char truncated[512];
|
||||
memcpy(truncated, target, sizeof(truncated) - 1);
|
||||
truncated[sizeof(truncated) - 1] = '\0';
|
||||
cbm_registry_add(r, "truncated_decoy", truncated, "Function");
|
||||
cbm_registry_add(r, "truncated_decoy", truncated, "Function", false);
|
||||
}
|
||||
const char *canonical = registry_scope_canonical(r, name, target);
|
||||
cbm_resolution_t python =
|
||||
@@ -3102,7 +3211,7 @@ TEST(registry_python_scope_qualified_terminal_bucket_uncapped) {
|
||||
char qn[80];
|
||||
int n = snprintf(qn, sizeof(qn), "proj.noise%d.perform", i);
|
||||
shape = n > 0 && (size_t)n < sizeof(qn) && shape;
|
||||
cbm_registry_add(r, "perform", qn, "Method");
|
||||
cbm_registry_add(r, "perform", qn, "Method", false);
|
||||
}
|
||||
char scope[1024];
|
||||
memcpy(scope, "proj.", 5);
|
||||
@@ -3120,7 +3229,7 @@ TEST(registry_python_scope_qualified_terminal_bucket_uncapped) {
|
||||
int pn = snprintf(prefixed, sizeof(prefixed), "receiver.%s", names[i]);
|
||||
shape = tn > 0 && (size_t)tn < sizeof(target) && pn > 0 && (size_t)pn < sizeof(prefixed) &&
|
||||
shape;
|
||||
cbm_registry_add(r, "perform", target, "Method");
|
||||
cbm_registry_add(r, "perform", target, "Method", false);
|
||||
const char *canonical = registry_scope_canonical(r, "perform", target);
|
||||
cbm_resolution_t direct =
|
||||
cbm_registry_resolve_lang(r, names[i], module, NULL, NULL, 0, CBM_LANG_PYTHON);
|
||||
@@ -3154,13 +3263,13 @@ TEST(registry_python_function_local_is_exact_only_issue1548) {
|
||||
{"Inner", "proj.web.Outer.Inner", "Class"},
|
||||
};
|
||||
for (size_t i = 0; i < sizeof(defs) / sizeof(defs[0]); i++) {
|
||||
cbm_registry_add_lang_in_module(r, defs[i][0], defs[i][1], defs[i][2], CBM_LANG_PYTHON,
|
||||
web);
|
||||
cbm_registry_add_lang_in_module(r, defs[i][0], defs[i][1], defs[i][2], CBM_LANG_PYTHON, web,
|
||||
false);
|
||||
}
|
||||
cbm_registry_add_lang_in_module(r, "f", "proj.ts.f", "Function", CBM_LANG_TYPESCRIPT,
|
||||
strlen("proj.ts"));
|
||||
strlen("proj.ts"), false);
|
||||
cbm_registry_add_lang_in_module(r, "g", "proj.ts.f.g", "Function", CBM_LANG_TYPESCRIPT,
|
||||
strlen("proj.ts"));
|
||||
strlen("proj.ts"), false);
|
||||
|
||||
static const char *const local[] = {"proj.web.create_app.index", "proj.web.create_app.Settings",
|
||||
"proj.web.create_app.Settings.value"};
|
||||
@@ -3212,7 +3321,7 @@ TEST(registry_resolve_lexical_walks_enclosing_defs_issue1548) {
|
||||
};
|
||||
for (size_t i = 0; i < sizeof(defs) / sizeof(defs[0]); i++) {
|
||||
cbm_registry_add_lang_in_module(r, defs[i][0], defs[i][1], defs[i][2], CBM_LANG_PYTHON,
|
||||
strlen("proj.m"));
|
||||
strlen("proj.m"), false);
|
||||
}
|
||||
|
||||
/* In method Tree.visit, `visit` is its nested def; so it is inside that
|
||||
@@ -3254,17 +3363,17 @@ TEST(registry_python_local_bound_at_own_file_issue1548) {
|
||||
cbm_registry_t *r = cbm_registry_new();
|
||||
if (order == 0) {
|
||||
cbm_registry_add_lang_in_module(r, "layout", "proj.pkg.layout", "Function",
|
||||
CBM_LANG_PYTHON, init_prefix);
|
||||
CBM_LANG_PYTHON, init_prefix, false);
|
||||
}
|
||||
cbm_registry_add_lang_in_module(r, "Layout", "proj.pkg.layout.Layout", "Class",
|
||||
CBM_LANG_PYTHON, mod_prefix);
|
||||
CBM_LANG_PYTHON, mod_prefix, false);
|
||||
cbm_registry_add_lang_in_module(r, "create", "proj.pkg.layout.Layout.create", "Method",
|
||||
CBM_LANG_PYTHON, mod_prefix);
|
||||
CBM_LANG_PYTHON, mod_prefix, false);
|
||||
cbm_registry_add_lang_in_module(r, "make", "proj.pkg.layout.make", "Function",
|
||||
CBM_LANG_PYTHON, mod_prefix);
|
||||
CBM_LANG_PYTHON, mod_prefix, false);
|
||||
if (order == 1) {
|
||||
cbm_registry_add_lang_in_module(r, "layout", "proj.pkg.layout", "Function",
|
||||
CBM_LANG_PYTHON, init_prefix);
|
||||
CBM_LANG_PYTHON, init_prefix, false);
|
||||
}
|
||||
static const char *const nameable[][2] = {{"Layout", "proj.pkg.layout.Layout"},
|
||||
{"create", "proj.pkg.layout.Layout.create"},
|
||||
@@ -3332,6 +3441,11 @@ SUITE(registry) {
|
||||
RUN_TEST(registry_create_free);
|
||||
RUN_TEST(registry_free_null);
|
||||
RUN_TEST(registry_add_and_exists);
|
||||
RUN_TEST(registry_prefers_non_test_over_test_candidate);
|
||||
RUN_TEST(registry_test_verdict_comes_from_the_caller_not_the_qn);
|
||||
RUN_TEST(registry_sole_test_candidate_still_resolves);
|
||||
RUN_TEST(registry_test_double_path_table);
|
||||
RUN_TEST(registry_swift_overload_choice_uses_stored_verdicts);
|
||||
RUN_TEST(registry_label_of);
|
||||
RUN_TEST(registry_value_target_field_twin);
|
||||
RUN_TEST(registry_find_by_name);
|
||||
|
||||
Reference in New Issue
Block a user