mirror of
https://github.com/DeusData/codebase-memory-mcp.git
synced 2026-11-04 10:48:20 +00:00
Merge pull request #2862 from DeusData/fix/go-bare-init-never-binds
fix(go): a bare init never binds to a package init or an init method
This commit is contained in:
7 files changed
+327
No files matched your search
@@ -1361,6 +1361,14 @@ static int resolve_single_call(cbm_pipeline_ctx_t *ctx, CBMCall *call, const CBM
|
||||
if (cbm_java_suppress_call_to_data_member(lang == CBM_LANG_JAVA, target_node->label)) {
|
||||
return 0;
|
||||
}
|
||||
/* A bare Go `init(...)` calls a local closure or parameter: never a
|
||||
* package init (unreferable in Go) and never a same-named method found
|
||||
* by name alone — see cbm_go_suppress_bare_init_bind. MUST match
|
||||
* pass_parallel.c exactly. */
|
||||
if (cbm_go_suppress_bare_init_bind(lang == CBM_LANG_GO, call->is_method, call->callee_name,
|
||||
res.strategy, target_node->label)) {
|
||||
return 0;
|
||||
}
|
||||
evidence->candidate_qn = target_node->qualified_name;
|
||||
evidence->calls_emitted |=
|
||||
emit_classified_edge(ctx, call, source_node, target_node, &res, module_qn, imp_keys,
|
||||
|
||||
@@ -3659,6 +3659,13 @@ static void resolve_file_calls(resolve_ctx_t *rc, resolve_worker_state_t *ws, CB
|
||||
* Variable or a Field. */
|
||||
continue;
|
||||
}
|
||||
if (target_node && source_node->id != target_node->id &&
|
||||
cbm_go_suppress_bare_init_bind(lang == CBM_LANG_GO, call->is_method, call->callee_name,
|
||||
res.strategy, target_node->label)) {
|
||||
/* Same guard as pass_calls.c — a bare Go `init` is a local name,
|
||||
* never a package init or a method found by name alone. */
|
||||
continue;
|
||||
}
|
||||
if (!target_node || source_node->id == target_node->id) {
|
||||
/* HTTP/ASYNC calls to an EXTERNAL client library (`requests.get(url)`)
|
||||
* resolve to an unindexed QN (target_node == NULL), but their edge
|
||||
@@ -3835,6 +3842,12 @@ static void resolve_file_usages(resolve_ctx_t *rc, resolve_worker_state_t *ws,
|
||||
if (tgt && cbm_suppress_bare_field_ref(lang, usage->is_member_access, tgt->label)) {
|
||||
continue;
|
||||
}
|
||||
/* A bare Go `init` names a local (cbm_go_suppress_bare_init_bind).
|
||||
* Mirrors the sequential twin (pass_usages.c). */
|
||||
if (tgt && cbm_go_suppress_bare_init_bind(lang == CBM_LANG_GO, usage->is_member_access,
|
||||
usage->ref_name, res.strategy, tgt->label)) {
|
||||
continue;
|
||||
}
|
||||
if (usage->semantic_reference_blocked && (usage->semantic_reference_local_shadow ||
|
||||
cbm_pipeline_node_is_callable_target(tgt))) {
|
||||
continue;
|
||||
@@ -3963,6 +3976,12 @@ static void resolve_file_rw(resolve_ctx_t *rc, resolve_worker_state_t *ws, CBMFi
|
||||
if (cbm_suppress_bare_field_ref(lang, rw->is_member_access, tgt->label)) {
|
||||
continue;
|
||||
}
|
||||
/* A bare Go `init` names a local (cbm_go_suppress_bare_init_bind).
|
||||
* Mirrors the sequential twin (pass_usages.c). */
|
||||
if (cbm_go_suppress_bare_init_bind(lang == CBM_LANG_GO, rw->is_member_access, rw->var_name,
|
||||
res.strategy, tgt->label)) {
|
||||
continue;
|
||||
}
|
||||
const char *etype = rw->is_write ? "WRITES" : "READS";
|
||||
cbm_gbuf_insert_edge(ws->local_edge_buf, src->id, tgt->id, etype, "{}");
|
||||
}
|
||||
|
||||
@@ -288,6 +288,12 @@ static int resolve_usage_edges(cbm_pipeline_ctx_t *ctx, const CBMFileResult *res
|
||||
if (tgt && cbm_suppress_bare_field_ref(lang, usage->is_member_access, tgt->label)) {
|
||||
continue;
|
||||
}
|
||||
/* A bare Go `init` names a local, never a package init or an init
|
||||
* method (cbm_go_suppress_bare_init_bind). Mirrors pass_parallel.c. */
|
||||
if (tgt && cbm_go_suppress_bare_init_bind(lang == CBM_LANG_GO, usage->is_member_access,
|
||||
usage->ref_name, res.strategy, tgt->label)) {
|
||||
continue;
|
||||
}
|
||||
if (usage->semantic_reference_blocked && (usage->semantic_reference_local_shadow ||
|
||||
cbm_pipeline_node_is_callable_target(tgt))) {
|
||||
continue;
|
||||
@@ -420,6 +426,13 @@ static int resolve_rw_edges(cbm_pipeline_ctx_t *ctx, const CBMFileResult *result
|
||||
if (cbm_suppress_bare_field_ref(lang, rw->is_member_access, tgt->label)) {
|
||||
continue;
|
||||
}
|
||||
/* `init := func(...)`: a bare Go `init` names a local, never a package
|
||||
* init or an init method (cbm_go_suppress_bare_init_bind). Mirrors
|
||||
* pass_parallel.c. */
|
||||
if (cbm_go_suppress_bare_init_bind(lang == CBM_LANG_GO, rw->is_member_access, rw->var_name,
|
||||
res.strategy, tgt->label)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const char *edge_type = rw->is_write ? "WRITES" : "READS";
|
||||
cbm_gbuf_insert_edge(ctx->gbuf, src->id, tgt->id, edge_type, "{}");
|
||||
|
||||
@@ -459,6 +459,16 @@ bool cbm_suppress_external_import_shadow(const char *callee_name, const char *st
|
||||
bool cbm_go_suppress_weak_method_match(bool is_go, bool is_method, const char *strategy,
|
||||
double confidence);
|
||||
|
||||
/* A bare Go `init` (a call or reference that is not the member half of a
|
||||
* selector) is always a local name: Go forbids referring to a package init
|
||||
* function, and a method or field needs a selector. True when such a name
|
||||
* resolved onto a Function (every Go Function named init is a package init;
|
||||
* any registry strategy) or onto a Method or Field found by a weak short-name
|
||||
* strategy. lsp_* answers are kept. Go only. Pure; unit-tested in
|
||||
* test_registry.c; wired in pass_calls.c, pass_usages.c and pass_parallel.c. */
|
||||
bool cbm_go_suppress_bare_init_bind(bool is_go, bool is_member, const char *name,
|
||||
const char *strategy, const char *target_label);
|
||||
|
||||
/* Import-binding counterpart (#2127). True when the callee's root identifier
|
||||
* is bound by an import of this file whose module chain contradicts the
|
||||
* resolved target (`from unittest.mock import patch; patch()` must not bind a
|
||||
|
||||
@@ -1015,6 +1015,46 @@ bool cbm_go_suppress_weak_method_match(bool is_go, bool is_method, const char *s
|
||||
return confidence < CONF_UNIQUE_NAME;
|
||||
}
|
||||
|
||||
bool cbm_go_suppress_bare_init_bind(bool is_go, bool is_member, const char *name,
|
||||
const char *strategy, const char *target_label) {
|
||||
/* The Go spec gives `init` no binding in the package block: the
|
||||
* identifier declares init functions and nothing else, and "init
|
||||
* functions cannot be referred to from anywhere in a program". A method
|
||||
* or a field named init is reached only through a selector (`x.init()`,
|
||||
* `e.init`), which arrives here as is_member and is left alone. So a
|
||||
* BARE `init` is always a local name: a closure (`init :=
|
||||
* func(ctx) {...}; init(ctx)`, 22 kubernetes e2e storage suites), a
|
||||
* parameter (`func sum(init func() ref.Val)`), or the `package init`
|
||||
* clause of a directory named init. None of them is a project node.
|
||||
*
|
||||
* Every Go Function named init is a package init (no other package-level
|
||||
* `init` can be declared), so that target is refused for every registry
|
||||
* strategy. Before #1915 the flat QN made same_module bind the bare name
|
||||
* to its own package's init; after it, the per-file QN misses there and
|
||||
* the name fallback binds unrelated init methods instead. A Method or
|
||||
* Field namesake is refused when only the name found it (the shared weak
|
||||
* drop-list), which is how every such edge was minted. lsp_* answers are
|
||||
* untouched: the sequential resolver emits them before its registry
|
||||
* guards run, so refusing one here would split the two resolvers.
|
||||
*
|
||||
* Go-gated: elsewhere `init` is an ordinary name. A bare `init()` in a
|
||||
* Java, C#, C++, Kotlin or Ruby method calls the class's own method, and
|
||||
* a C, Python or JS `init` function is called by its bare name. */
|
||||
if (!is_go || is_member || !name || strcmp(name, "init") != 0 || !target_label) {
|
||||
return false;
|
||||
}
|
||||
if (strategy && strncmp(strategy, "lsp_", strlen("lsp_")) == 0) {
|
||||
return false;
|
||||
}
|
||||
if (strcmp(target_label, "Function") == 0) {
|
||||
return true;
|
||||
}
|
||||
if (strcmp(target_label, "Method") == 0 || strcmp(target_label, "Field") == 0) {
|
||||
return weak_short_name_strategy(strategy);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Import-binding counterpart of the parameter guard above (#2127). An import
|
||||
* binds the identifier for the whole module; when that import is external it
|
||||
* never materializes, and the chain falls through to project-wide short-name
|
||||
|
||||
@@ -11071,6 +11071,201 @@ TEST(pipeline_go_init_lsp_edges_per_file_parallel) {
|
||||
PASS();
|
||||
}
|
||||
|
||||
/* Fixture for the bare-`init` probes (sequential and parallel twins), the
|
||||
* shapes the kubernetes census found after #1915: a local closure `init :=
|
||||
* func(ctx)` that is called, written and passed by value (the e2e storage
|
||||
* suites), a parameter named init (`func sum(init func() ref.Val)`), and a
|
||||
* `package init` directory. The decoys are a package init in the caller's
|
||||
* own package (what same_module bound before #1915) and an unrelated
|
||||
* Watcher.init method (what the name fallback binds after it). Watcher.Start
|
||||
* and Boot call that method through a selector, the control that must keep
|
||||
* resolving. pad_files > 0 adds filler Go files past MIN_FILES_FOR_PARALLEL. */
|
||||
static void write_go_bare_init_fixture(const char *tmp, int pad_files) {
|
||||
write_temp_file(tmp, "go.mod", "module example.com/fxbareinit\n\ngo 1.22\n");
|
||||
write_temp_file(tmp, "suites/suite.go",
|
||||
"package suites\n"
|
||||
"\n"
|
||||
"import \"context\"\n"
|
||||
"\n"
|
||||
"var registered []string\n"
|
||||
"\n"
|
||||
"func init() { registered = append(registered, \"volumes\") }\n"
|
||||
"\n"
|
||||
"func wrap(fn func(context.Context)) { _ = fn }\n"
|
||||
"\n"
|
||||
"func DefineTests(ctx context.Context) {\n"
|
||||
"\tinit := func(ctx context.Context) {\n"
|
||||
"\t\t_ = ctx\n"
|
||||
"\t}\n"
|
||||
"\tinit(ctx)\n"
|
||||
"\twrap(init)\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"func Sum(init func() int) int {\n"
|
||||
"\treturn init()\n"
|
||||
"}\n");
|
||||
write_temp_file(tmp, "watcher/watcher.go",
|
||||
"package watcher\n"
|
||||
"\n"
|
||||
"type Watcher struct{ ready bool }\n"
|
||||
"\n"
|
||||
"func NewWatcher() *Watcher { return &Watcher{} }\n"
|
||||
"\n"
|
||||
"func (w *Watcher) init() { w.ready = true }\n"
|
||||
"\n"
|
||||
"func (w *Watcher) Start() {\n"
|
||||
"\tw.init()\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"func Boot() {\n"
|
||||
"\tw := NewWatcher()\n"
|
||||
"\tw.init()\n"
|
||||
"}\n");
|
||||
write_temp_file(tmp, "boot/init/init.go",
|
||||
"// Package init installs defaults for its importers.\n"
|
||||
"package init\n"
|
||||
"\n"
|
||||
"func init() { setup() }\n"
|
||||
"\n"
|
||||
"func setup() {}\n");
|
||||
for (int i = 0; i < pad_files; i++) {
|
||||
char name[64];
|
||||
char body[128];
|
||||
snprintf(name, sizeof(name), "pad/filler%d.go", i);
|
||||
snprintf(body, sizeof(body), "package pad\n\nfunc filler%d() int { return %d }\n", i, i);
|
||||
write_temp_file(tmp, name, body);
|
||||
}
|
||||
}
|
||||
|
||||
static bool bare_init_qn_ends_with(const char *qn, const char *suffix) {
|
||||
size_t ql = strlen(qn);
|
||||
size_t sl = strlen(suffix);
|
||||
return ql >= sl && strcmp(qn + ql - sl, suffix) == 0;
|
||||
}
|
||||
|
||||
/* Resolver edges into any node named `init` whose source is not one of the
|
||||
* two selector controls. Every such edge in the fixture binds a bare `init`,
|
||||
* which in Go is always a local name. */
|
||||
static int go_bare_init_false_edges(cbm_store_t *s, const char *project) {
|
||||
static const char *const types[] = {"CALLS", "USAGE", "WRITES", "READS", "CALL_REFERENCE"};
|
||||
int hits = 0;
|
||||
for (size_t t = 0; t < sizeof(types) / sizeof(types[0]); t++) {
|
||||
cbm_edge_t *edges = NULL;
|
||||
int edge_count = 0;
|
||||
cbm_store_find_edges_by_type(s, project, types[t], &edges, &edge_count);
|
||||
for (int i = 0; i < edge_count; i++) {
|
||||
cbm_node_t src = {0};
|
||||
cbm_node_t tgt = {0};
|
||||
if (cbm_store_find_node_by_id(s, edges[i].source_id, &src) == CBM_STORE_OK &&
|
||||
cbm_store_find_node_by_id(s, edges[i].target_id, &tgt) == CBM_STORE_OK &&
|
||||
src.qualified_name && tgt.name && strcmp(tgt.name, "init") == 0 &&
|
||||
!bare_init_qn_ends_with(src.qualified_name, ".watcher.Watcher.Start") &&
|
||||
!bare_init_qn_ends_with(src.qualified_name, ".watcher.Boot")) {
|
||||
printf(" bare init bound: %s %s -> %s\n", types[t], src.qualified_name,
|
||||
tgt.qualified_name ? tgt.qualified_name : "?");
|
||||
hits++;
|
||||
}
|
||||
cbm_node_free_fields(&src);
|
||||
cbm_node_free_fields(&tgt);
|
||||
}
|
||||
if (edges) {
|
||||
cbm_store_free_edges(edges, edge_count);
|
||||
}
|
||||
}
|
||||
return hits;
|
||||
}
|
||||
|
||||
static int assert_go_bare_init_edges(cbm_store_t *s, const char *project) {
|
||||
/* The decoys exist, so a zero below is a refused binding and not an
|
||||
* absent target: two package inits and the Watcher method. */
|
||||
cbm_node_t *inits = NULL;
|
||||
int ic = 0;
|
||||
cbm_store_find_nodes_by_name(s, project, "init", &inits, &ic);
|
||||
int functions = 0;
|
||||
int methods = 0;
|
||||
for (int i = 0; i < ic; i++) {
|
||||
functions += inits[i].label && strcmp(inits[i].label, "Function") == 0;
|
||||
methods += inits[i].label && strcmp(inits[i].label, "Method") == 0;
|
||||
}
|
||||
cbm_store_free_nodes(inits, ic);
|
||||
ASSERT_EQ(functions, 2);
|
||||
ASSERT_EQ(methods, 1);
|
||||
/* NEGATIVE: no bare `init` (closure call, closure write, closure passed
|
||||
* by value, parameter call, `package init`) binds a package init or the
|
||||
* unrelated method. */
|
||||
ASSERT_EQ(go_bare_init_false_edges(s, project), 0);
|
||||
/* CONTROL: a selector call `w.init()` is not bare and still resolves to
|
||||
* the method, from a method body and from a free function. */
|
||||
ASSERT_GTE(
|
||||
go_calls_between(s, project, ".watcher.Watcher.Start", ".watcher.Watcher.init", NULL), 1);
|
||||
ASSERT_GTE(go_calls_between(s, project, ".watcher.Boot", ".watcher.Watcher.init", NULL), 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* R45 (follow-up to #1915): a bare Go `init` never binds a package init or,
|
||||
* by name alone, an init method (cbm_go_suppress_bare_init_bind). */
|
||||
TEST(pipeline_go_bare_init_binds_nothing) {
|
||||
char tmp[256];
|
||||
snprintf(tmp, sizeof(tmp), "/tmp/cbm_go_bare_init_XXXXXX");
|
||||
if (!cbm_mkdtemp(tmp)) {
|
||||
FAIL("tmpdir");
|
||||
}
|
||||
write_go_bare_init_fixture(tmp, 0);
|
||||
|
||||
char db_path[512];
|
||||
snprintf(db_path, sizeof(db_path), "%s/go_bare_init.db", tmp);
|
||||
cbm_pipeline_t *p = cbm_pipeline_new(tmp, db_path, CBM_MODE_FULL);
|
||||
ASSERT_NOT_NULL(p);
|
||||
ASSERT_EQ(cbm_pipeline_run(p), 0);
|
||||
const char *project = cbm_pipeline_project_name(p);
|
||||
|
||||
cbm_store_t *s = cbm_store_open_path(db_path);
|
||||
ASSERT_NOT_NULL(s);
|
||||
ASSERT_EQ(assert_go_bare_init_edges(s, project), 0);
|
||||
|
||||
cbm_store_close(s);
|
||||
cbm_pipeline_free(p);
|
||||
th_rmtree(tmp);
|
||||
PASS();
|
||||
}
|
||||
|
||||
/* Parallel twin: >= MIN_FILES_FOR_PARALLEL (50) files plus CBM_WORKERS=4
|
||||
* take the parallel resolver, which carries its own copy of each guard. */
|
||||
TEST(pipeline_go_bare_init_binds_nothing_parallel) {
|
||||
char tmp[256];
|
||||
snprintf(tmp, sizeof(tmp), "/tmp/cbm_go_bare_init_par_XXXXXX");
|
||||
if (!cbm_mkdtemp(tmp)) {
|
||||
FAIL("tmpdir");
|
||||
}
|
||||
write_go_bare_init_fixture(tmp, 52);
|
||||
|
||||
char *old_workers = getenv("CBM_WORKERS");
|
||||
char *saved = old_workers ? strdup(old_workers) : NULL;
|
||||
cbm_setenv("CBM_WORKERS", "4", 1);
|
||||
|
||||
char db_path[512];
|
||||
snprintf(db_path, sizeof(db_path), "%s/go_bare_init_par.db", tmp);
|
||||
cbm_pipeline_t *p = cbm_pipeline_new(tmp, db_path, CBM_MODE_FULL);
|
||||
ASSERT_NOT_NULL(p);
|
||||
ASSERT_EQ(cbm_pipeline_run(p), 0);
|
||||
const char *project = cbm_pipeline_project_name(p);
|
||||
|
||||
cbm_store_t *s = cbm_store_open_path(db_path);
|
||||
ASSERT_NOT_NULL(s);
|
||||
ASSERT_EQ(assert_go_bare_init_edges(s, project), 0);
|
||||
|
||||
cbm_store_close(s);
|
||||
cbm_pipeline_free(p);
|
||||
if (saved) {
|
||||
cbm_setenv("CBM_WORKERS", saved, 1);
|
||||
free(saved);
|
||||
} else {
|
||||
cbm_unsetenv("CBM_WORKERS");
|
||||
}
|
||||
th_rmtree(tmp);
|
||||
PASS();
|
||||
}
|
||||
|
||||
/* Fixture for the #1928 cross-language reference-guard probes (sequential and
|
||||
* parallel twins). pad_files > 0 adds filler files to push the index over the
|
||||
* parallel-pipeline threshold, since USAGE/WRITES/READS have one resolver per
|
||||
@@ -29823,6 +30018,8 @@ SUITE(pipeline) {
|
||||
RUN_TEST(pipeline_go_multi_init_nodes_survive);
|
||||
RUN_TEST(pipeline_go_init_lsp_edges_per_file);
|
||||
RUN_TEST(pipeline_go_init_lsp_edges_per_file_parallel);
|
||||
RUN_TEST(pipeline_go_bare_init_binds_nothing);
|
||||
RUN_TEST(pipeline_go_bare_init_binds_nothing_parallel);
|
||||
RUN_TEST(pipeline_go_crossfile_type_alias_method);
|
||||
RUN_TEST(pipeline_go_crossfile_type_alias_method_parallel);
|
||||
RUN_TEST(pipeline_go_rw_usage_never_cross_into_c);
|
||||
|
||||
@@ -1260,6 +1260,45 @@ TEST(go_bare_ref_never_binds_field) {
|
||||
PASS();
|
||||
}
|
||||
|
||||
TEST(go_bare_init_never_binds_init_namesake) {
|
||||
/* Go cannot refer to a package init function, and a method or field
|
||||
* named init needs a selector, so a bare `init` (a closure, a parameter,
|
||||
* a `package init` clause) is always a local name. */
|
||||
/* Every Go Function named init is a package init: refused for every
|
||||
* registry strategy, same_module included (the pre-#1915 binding). */
|
||||
ASSERT_TRUE(cbm_go_suppress_bare_init_bind(true, false, "init", "same_module", "Function"));
|
||||
ASSERT_TRUE(cbm_go_suppress_bare_init_bind(true, false, "init", "suffix_match", "Function"));
|
||||
ASSERT_TRUE(cbm_go_suppress_bare_init_bind(true, false, "init", "import_map", "Function"));
|
||||
ASSERT_TRUE(cbm_go_suppress_bare_init_bind(true, false, "init", NULL, "Function"));
|
||||
/* A Method or Field namesake found by the name alone is refused. */
|
||||
ASSERT_TRUE(cbm_go_suppress_bare_init_bind(true, false, "init", "suffix_match", "Method"));
|
||||
ASSERT_TRUE(cbm_go_suppress_bare_init_bind(true, false, "init", "unique_name", "Method"));
|
||||
ASSERT_TRUE(cbm_go_suppress_bare_init_bind(true, false, "init", "suffix_match", "Field"));
|
||||
/* ... but not one that an import- or module-aware strategy produced. */
|
||||
ASSERT_FALSE(cbm_go_suppress_bare_init_bind(true, false, "init", "same_module", "Method"));
|
||||
ASSERT_FALSE(cbm_go_suppress_bare_init_bind(true, false, "init", "import_map", "Method"));
|
||||
/* lsp_* answers are kept: the sequential resolver emits them before its
|
||||
* registry guards run, so refusing one here would split the resolvers. */
|
||||
ASSERT_FALSE(cbm_go_suppress_bare_init_bind(true, false, "init", "lsp_direct", "Function"));
|
||||
ASSERT_FALSE(cbm_go_suppress_bare_init_bind(true, false, "init", "lsp_direct", "Method"));
|
||||
/* The member half of a selector (`w.init()`, `e.init`) is not bare. */
|
||||
ASSERT_FALSE(cbm_go_suppress_bare_init_bind(true, true, "init", "suffix_match", "Method"));
|
||||
ASSERT_FALSE(cbm_go_suppress_bare_init_bind(true, true, "init", "suffix_match", "Field"));
|
||||
/* Other names and other target kinds are untouched. */
|
||||
ASSERT_FALSE(cbm_go_suppress_bare_init_bind(true, false, "initAll", "suffix_match", "Method"));
|
||||
ASSERT_FALSE(cbm_go_suppress_bare_init_bind(true, false, "Init", "suffix_match", "Function"));
|
||||
ASSERT_FALSE(cbm_go_suppress_bare_init_bind(true, false, "init", "suffix_match", "Variable"));
|
||||
ASSERT_FALSE(cbm_go_suppress_bare_init_bind(true, false, "init", "suffix_match", "Module"));
|
||||
/* Go only: elsewhere a bare `init()` is an ordinary call (a Java or C++
|
||||
* method body calls its own init, a C or Python init is a function). */
|
||||
ASSERT_FALSE(cbm_go_suppress_bare_init_bind(false, false, "init", "suffix_match", "Method"));
|
||||
ASSERT_FALSE(cbm_go_suppress_bare_init_bind(false, false, "init", "same_module", "Function"));
|
||||
/* Degenerate input → nothing to judge. */
|
||||
ASSERT_FALSE(cbm_go_suppress_bare_init_bind(true, false, NULL, "suffix_match", "Function"));
|
||||
ASSERT_FALSE(cbm_go_suppress_bare_init_bind(true, false, "init", "suffix_match", NULL));
|
||||
PASS();
|
||||
}
|
||||
|
||||
TEST(c_bare_ref_never_binds_field) {
|
||||
/* #2666: a C struct member is named only as the member half of `a.b`,
|
||||
* `a->b` or a designator `.b` (field_identifier → is_member_access). A
|
||||
@@ -2656,6 +2695,7 @@ SUITE(registry) {
|
||||
RUN_TEST(registry_tie_prefers_csharp_implementation_over_ref_stub);
|
||||
RUN_TEST(cross_language_ref_drops_go_vs_c);
|
||||
RUN_TEST(go_bare_ref_never_binds_field);
|
||||
RUN_TEST(go_bare_init_never_binds_init_namesake);
|
||||
RUN_TEST(c_bare_ref_never_binds_field);
|
||||
RUN_TEST(call_onto_field_follows_language_and_shape);
|
||||
RUN_TEST(c_bare_call_does_not_weigh_field_namesakes);
|
||||
|
||||
Reference in new issue
Block a user