mirror of
https://github.com/DeusData/codebase-memory-mcp.git
synced 2026-11-04 10:48:20 +00:00
Merge origin/main into fix/swift-init-extraction
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
This commit is contained in:
commit
ebe2e6fa30
8 files changed
+171
-32
No files matched your search
@@ -1040,6 +1040,7 @@ static const char *solidity_field_types[] = {"state_variable_declaration", "stru
|
||||
static const char *solidity_call_types[] = {"call_expression", "call", "new_expression", NULL};
|
||||
static const char *solidity_import_types[] = {"import_directive", "import", "using_directive",
|
||||
NULL};
|
||||
static const char *solidity_decorator_types[] = {"modifier_invocation", NULL};
|
||||
static const char *solidity_branch_types[] = {"if_statement", "for_statement",
|
||||
"while_statement", "do_while_statement",
|
||||
"try_statement", NULL};
|
||||
@@ -2117,7 +2118,7 @@ static const CBMLangSpec lang_specs[CBM_LANG_COUNT] = {
|
||||
solidity_field_types, solidity_module_types, solidity_call_types,
|
||||
solidity_import_types, empty_types, solidity_branch_types,
|
||||
solidity_var_types, solidity_assign_types, solidity_throw_types, NULL,
|
||||
empty_types, NULL, NULL, tree_sitter_solidity, NULL},
|
||||
solidity_decorator_types, NULL, NULL, tree_sitter_solidity, NULL},
|
||||
|
||||
// CBM_LANG_TYPST
|
||||
[CBM_LANG_TYPST] = {CBM_LANG_TYPST, typst_func_types, empty_types, empty_types,
|
||||
|
||||
@@ -3791,10 +3791,9 @@ static void resolve_file_usages(resolve_ctx_t *rc, resolve_worker_state_t *ws,
|
||||
if (tgt && cbm_suppress_cross_language_ref(lang, tgt->file_path)) {
|
||||
continue;
|
||||
}
|
||||
/* #1942/#1962: a bare Go reference can never denote a struct
|
||||
* field; the member half of a selector may. */
|
||||
if (tgt && cbm_go_suppress_bare_field_ref(lang == CBM_LANG_GO, usage->is_member_access,
|
||||
tgt->label)) {
|
||||
/* #1942/#1962/#2666: a bare Go or C reference can never denote
|
||||
* a struct field; the member half of a selector may. */
|
||||
if (tgt && cbm_suppress_bare_field_ref(lang, usage->is_member_access, tgt->label)) {
|
||||
continue;
|
||||
}
|
||||
if (usage->semantic_reference_blocked && (usage->semantic_reference_local_shadow ||
|
||||
@@ -3920,9 +3919,9 @@ static void resolve_file_rw(resolve_ctx_t *rc, resolve_worker_state_t *ws, CBMFi
|
||||
if (cbm_suppress_cross_language_ref(lang, tgt->file_path)) {
|
||||
continue;
|
||||
}
|
||||
/* #1942/#1962: a bare Go reference can never denote a struct field;
|
||||
* a selector-LHS write (`t.err = x`) may bind it. */
|
||||
if (cbm_go_suppress_bare_field_ref(lang == CBM_LANG_GO, rw->is_member_access, tgt->label)) {
|
||||
/* #1942/#1962/#2666: a bare Go or C reference can never denote a
|
||||
* struct field; a selector-LHS write (`t.err = x`) may bind it. */
|
||||
if (cbm_suppress_bare_field_ref(lang, rw->is_member_access, tgt->label)) {
|
||||
continue;
|
||||
}
|
||||
const char *etype = rw->is_write ? "WRITES" : "READS";
|
||||
|
||||
@@ -283,10 +283,9 @@ static int resolve_usage_edges(cbm_pipeline_ctx_t *ctx, const CBMFileResult *res
|
||||
if (tgt && cbm_suppress_cross_language_ref(lang, tgt->file_path)) {
|
||||
continue;
|
||||
}
|
||||
/* #1942/#1962: a bare Go reference can never denote a struct
|
||||
* field; the member half of a selector may. */
|
||||
if (tgt && cbm_go_suppress_bare_field_ref(lang == CBM_LANG_GO, usage->is_member_access,
|
||||
tgt->label)) {
|
||||
/* #1942/#1962/#2666: a bare Go or C reference can never denote
|
||||
* a struct field; the member half of a selector may. */
|
||||
if (tgt && cbm_suppress_bare_field_ref(lang, usage->is_member_access, tgt->label)) {
|
||||
continue;
|
||||
}
|
||||
if (usage->semantic_reference_blocked && (usage->semantic_reference_local_shadow ||
|
||||
@@ -416,9 +415,9 @@ static int resolve_rw_edges(cbm_pipeline_ctx_t *ctx, const CBMFileResult *result
|
||||
if (cbm_suppress_cross_language_ref(lang, tgt->file_path)) {
|
||||
continue;
|
||||
}
|
||||
/* #1942/#1962: a bare Go reference can never denote a struct field;
|
||||
* a selector-LHS write (`t.err = x`) may bind it. */
|
||||
if (cbm_go_suppress_bare_field_ref(lang == CBM_LANG_GO, rw->is_member_access, tgt->label)) {
|
||||
/* #1942/#1962/#2666: a bare Go or C reference can never denote a
|
||||
* struct field; a selector-LHS write (`t.err = x`) may bind it. */
|
||||
if (cbm_suppress_bare_field_ref(lang, rw->is_member_access, tgt->label)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
@@ -467,13 +467,14 @@ bool cbm_suppress_cross_language_suffix_match(CBMLanguage caller_lang, const cha
|
||||
* unit-tested in test_registry.c. */
|
||||
bool cbm_suppress_cross_language_ref(CBMLanguage caller_lang, const char *target_file_path);
|
||||
|
||||
/* #1942: a bare (dot-less) Go reference can never denote a struct field —
|
||||
* field access is always a selector expression, and selector references
|
||||
* resolve on the LSP path. Drops a READS/WRITES/USAGE bind whose target is a
|
||||
* Field when the reference text carries no '.'. Go only: other OO languages
|
||||
* legitimately reference their own members bare inside method bodies. Pure;
|
||||
* unit-tested in test_registry.c. */
|
||||
bool cbm_go_suppress_bare_field_ref(bool is_go, bool is_member_access, const char *target_label);
|
||||
/* #1942/#2666: a bare Go or C reference can never denote a struct field —
|
||||
* field access is always a selector expression (or, in C, a designator), and
|
||||
* the member half of one is recorded as is_member_access. Drops a
|
||||
* READS/WRITES/USAGE bind whose target is a Field when the reference was not
|
||||
* the member half of a selector. Go and C only: other OO languages (and C++,
|
||||
* which every `.h` is parsed as) legitimately reference their own members
|
||||
* bare inside method bodies. Pure; unit-tested in test_registry.c. */
|
||||
bool cbm_suppress_bare_field_ref(CBMLanguage lang, bool is_member_access, const char *target_label);
|
||||
|
||||
/* What to do with a call whose registry resolution landed on a Field node.
|
||||
* Pure; unit-tested in test_registry.c. */
|
||||
|
||||
+15
-4
@@ -1276,7 +1276,8 @@ bool cbm_suppress_cross_language_ref(CBMLanguage caller_lang, const char *target
|
||||
return true;
|
||||
}
|
||||
|
||||
bool cbm_go_suppress_bare_field_ref(bool is_go, bool is_member_access, const char *target_label) {
|
||||
bool cbm_suppress_bare_field_ref(CBMLanguage lang, bool is_member_access,
|
||||
const char *target_label) {
|
||||
/* #1942/#1962: a bare Go identifier can never denote a struct field —
|
||||
* field access is always a selector expression (x.f). The extractor
|
||||
* strips the receiver before the resolver runs (resolve_lhs_write_name
|
||||
@@ -1284,10 +1285,20 @@ bool cbm_go_suppress_bare_field_ref(bool is_go, bool is_member_access, const cha
|
||||
* field_identifier), so the reference TEXT is always dot-less and cannot
|
||||
* carry the distinction — the recorded is_member_access shape can. Only a
|
||||
* reference that was never the member half of a selector is refused a
|
||||
* Field bind. Go-gated: a C#/Java/C++/Python method body legitimately
|
||||
* Field bind.
|
||||
*
|
||||
* #2666: C has the same rule. A struct member is named only as the member
|
||||
* half of `a.b`, `a->b` or a designator `.b`, all recorded as
|
||||
* field_identifier; a bare C identifier (a macro argument such as
|
||||
* RUN_SELECTED_SUITE(mcp), a stray name in an unexpanded macro body) is
|
||||
* never one. The call path already drops a bare C call onto a Field
|
||||
* (cbm_call_onto_field_policy).
|
||||
*
|
||||
* Go- and C-gated: a C#/Java/C++/Python method body legitimately
|
||||
* references its own members bare (cp_reads_writes_cs_static_field pins
|
||||
* that shape as required), so a global veto would break those languages. */
|
||||
if (!is_go || is_member_access || !target_label) {
|
||||
* that shape as required), so a global veto would break those languages.
|
||||
* A `.h` is parsed as C++ and keeps that behavior. */
|
||||
if ((lang != CBM_LANG_GO && lang != CBM_LANG_C) || is_member_access || !target_label) {
|
||||
return false;
|
||||
}
|
||||
return strcmp(target_label, "Field") == 0;
|
||||
|
||||
@@ -5807,6 +5807,24 @@ TEST(extract_python_bare_decorator_route_rules) {
|
||||
PASS();
|
||||
}
|
||||
|
||||
/* Issue #2090: Solidity modifier_invocation tracked as decorator. */
|
||||
TEST(solidity_modifier_decorator) {
|
||||
CBMFileResult *r = extract("pragma solidity ^0.8.0;\n"
|
||||
"contract Vault {\n"
|
||||
" modifier onlyOwner() { _; }\n"
|
||||
" function withdraw() public onlyOwner {\n"
|
||||
" }\n"
|
||||
"}\n",
|
||||
CBM_LANG_SOLIDITY, "t", "Vault.sol");
|
||||
ASSERT_NOT_NULL(r);
|
||||
ASSERT_FALSE(r->has_error);
|
||||
const CBMDefinition *m = find_def_by_name(r, "withdraw");
|
||||
ASSERT_NOT_NULL(m);
|
||||
ASSERT(decorators_contain(m, "onlyOwner"));
|
||||
cbm_free_result(r);
|
||||
PASS();
|
||||
}
|
||||
|
||||
/* ── ArkTS (HarmonyOS .ets) ─────────────────────────────────────── */
|
||||
|
||||
TEST(arkts_component_struct) {
|
||||
@@ -14093,6 +14111,7 @@ SUITE(extraction) {
|
||||
RUN_TEST(extract_java_method_annotations_issue382);
|
||||
RUN_TEST(extract_python_mock_patch_is_not_route);
|
||||
RUN_TEST(extract_python_bare_decorator_route_rules);
|
||||
RUN_TEST(solidity_modifier_decorator);
|
||||
RUN_TEST(arkts_component_struct);
|
||||
RUN_TEST(arkts_exported_struct_decorators);
|
||||
RUN_TEST(arkts_member_decorators);
|
||||
|
||||
@@ -11516,6 +11516,93 @@ TEST(pipeline_go_bare_ref_never_binds_field_parallel) {
|
||||
PASS();
|
||||
}
|
||||
|
||||
/* #2666: tests/test_main.c runs every suite through a macro,
|
||||
* RUN_SELECTED_SUITE(mcp); the argument is a bare C identifier that names no
|
||||
* declaration, and the registry fallback bound it to whichever struct member
|
||||
* was called `mcp`. toggle() writes a bare name the extractor cannot see
|
||||
* declared (a macro-made global, say) that a struct member also carries.
|
||||
* state.c holds the genuine member uses that must keep their edges.
|
||||
* pad_files pushes the run past MIN_FILES_FOR_PARALLEL for the parallel twin. */
|
||||
static void write_c_bare_field_fixture(const char *tmp, int pad_files) {
|
||||
write_temp_file(tmp, "include/suite.h",
|
||||
"typedef struct suite_state {\n"
|
||||
" char mcp[64];\n"
|
||||
" int count;\n"
|
||||
" int enabled;\n"
|
||||
"} suite_state_t;\n");
|
||||
write_temp_file(tmp, "tests/main.c",
|
||||
"#include \"suite.h\"\n"
|
||||
"#define RUN_SELECTED_SUITE(name) run_suite(#name)\n"
|
||||
"int run_suite(const char *name);\n"
|
||||
"int main(void) {\n"
|
||||
" RUN_SELECTED_SUITE(mcp);\n"
|
||||
" RUN_SELECTED_SUITE(count);\n"
|
||||
" return 0;\n"
|
||||
"}\n"
|
||||
"void toggle(void) {\n"
|
||||
" enabled = 1;\n"
|
||||
"}\n");
|
||||
write_temp_file(tmp, "src/state.c",
|
||||
"#include \"suite.h\"\n"
|
||||
"int state_count(const suite_state_t *s) { return s->count; }\n"
|
||||
"void state_reset(suite_state_t *s) { s->enabled = 0; }\n");
|
||||
for (int i = 0; i < pad_files; i++) {
|
||||
char name[64];
|
||||
char body[128];
|
||||
snprintf(name, sizeof(name), "pad/filler%d.c", i);
|
||||
snprintf(body, sizeof(body), "int filler%d(void) { return %d; }\n", i, i);
|
||||
write_temp_file(tmp, name, body);
|
||||
}
|
||||
}
|
||||
|
||||
static int run_c_bare_field_case(const char *tmpl, int pad_files) {
|
||||
char tmp[256];
|
||||
snprintf(tmp, sizeof(tmp), "%s", tmpl);
|
||||
if (!cbm_mkdtemp(tmp)) {
|
||||
FAIL("tmpdir");
|
||||
}
|
||||
write_c_bare_field_fixture(tmp, pad_files);
|
||||
|
||||
char db_path[512];
|
||||
snprintf(db_path, sizeof(db_path), "%s/c_bare.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);
|
||||
|
||||
/* The fields must exist for the probes to mean anything. */
|
||||
ASSERT_GTE(fixture_node_count(s, project, "include/suite.h", "mcp", "Field"), 1);
|
||||
ASSERT_GTE(fixture_node_count(s, project, "include/suite.h", "count", "Field"), 1);
|
||||
ASSERT_GTE(fixture_node_count(s, project, "include/suite.h", "enabled", "Field"), 1);
|
||||
/* Reproduce-first: RED before the fix — the macro arguments bind the
|
||||
* same-named struct members, and the bare write binds `enabled`. */
|
||||
ASSERT_FALSE(cross_file_edge_exists(s, project, "main", "mcp", "USAGE"));
|
||||
ASSERT_FALSE(cross_file_edge_exists(s, project, "main", "count", "USAGE"));
|
||||
ASSERT_FALSE(cross_file_edge_exists(s, project, "toggle", "enabled", "WRITES"));
|
||||
/* The member half of `s->count` / `s->enabled = 0` still binds the field. */
|
||||
ASSERT_TRUE(cross_file_edge_exists(s, project, "state_count", "count", "USAGE"));
|
||||
ASSERT_TRUE(cross_file_edge_exists(s, project, "state_reset", "enabled", "WRITES"));
|
||||
|
||||
cbm_store_close(s);
|
||||
cbm_pipeline_free(p);
|
||||
th_rmtree(tmp);
|
||||
PASS();
|
||||
}
|
||||
|
||||
TEST(pipeline_c_bare_ref_never_binds_field) {
|
||||
/* Sequential path: resolve_usage_edges / resolve_rw_edges. */
|
||||
return run_c_bare_field_case("/tmp/cbm_c_bare_XXXXXX", 0);
|
||||
}
|
||||
|
||||
TEST(pipeline_c_bare_ref_never_binds_field_parallel) {
|
||||
/* Parallel twin: resolve_file_usages / resolve_file_rw must consult the
|
||||
* same predicate (#1928's lesson). */
|
||||
return run_c_bare_field_case("/tmp/cbm_c_barep_XXXXXX", 52);
|
||||
}
|
||||
|
||||
/* A C caller of a header function that shares its name with a struct member
|
||||
* elsewhere (kernel: include/linux/mm.h put_page beside the arm64
|
||||
* kvm_pgtable_mm_ops.put_page member). pad_files pushes the run past
|
||||
@@ -28933,6 +29020,8 @@ SUITE(pipeline) {
|
||||
RUN_TEST(pipeline_rust_std_receiver_never_binds_project_method_parallel);
|
||||
RUN_TEST(pipeline_go_bare_ref_never_binds_field);
|
||||
RUN_TEST(pipeline_go_bare_ref_never_binds_field_parallel);
|
||||
RUN_TEST(pipeline_c_bare_ref_never_binds_field);
|
||||
RUN_TEST(pipeline_c_bare_ref_never_binds_field_parallel);
|
||||
RUN_TEST(pipeline_c_call_reaches_header_function_beside_field);
|
||||
RUN_TEST(pipeline_c_call_reaches_header_function_beside_field_parallel);
|
||||
RUN_TEST(pipeline_c_member_access_binds_field_by_object_type);
|
||||
|
||||
+26
-6
@@ -1245,17 +1245,36 @@ TEST(go_bare_ref_never_binds_field) {
|
||||
* trailing name; is_reference_node records the inner field_identifier),
|
||||
* so the selector-vs-bare distinction arrives as the recorded
|
||||
* is_member_access signal, never as a dot in the reference text. */
|
||||
ASSERT_TRUE(cbm_go_suppress_bare_field_ref(true, false, "Field"));
|
||||
ASSERT_TRUE(cbm_suppress_bare_field_ref(CBM_LANG_GO, false, "Field"));
|
||||
/* The member half of a selector may bind a field. */
|
||||
ASSERT_FALSE(cbm_go_suppress_bare_field_ref(true, true, "Field"));
|
||||
ASSERT_FALSE(cbm_suppress_bare_field_ref(CBM_LANG_GO, true, "Field"));
|
||||
/* Bare references to non-fields are untouched. */
|
||||
ASSERT_FALSE(cbm_go_suppress_bare_field_ref(true, false, "Variable"));
|
||||
ASSERT_FALSE(cbm_go_suppress_bare_field_ref(true, false, "Function"));
|
||||
ASSERT_FALSE(cbm_suppress_bare_field_ref(CBM_LANG_GO, false, "Variable"));
|
||||
ASSERT_FALSE(cbm_suppress_bare_field_ref(CBM_LANG_GO, false, "Function"));
|
||||
/* Other languages reference their own members bare inside methods —
|
||||
* never suppressed (cp_reads_writes_cs_static_field pins the C# shape). */
|
||||
ASSERT_FALSE(cbm_go_suppress_bare_field_ref(false, false, "Field"));
|
||||
ASSERT_FALSE(cbm_suppress_bare_field_ref(CBM_LANG_CSHARP, false, "Field"));
|
||||
ASSERT_FALSE(cbm_suppress_bare_field_ref(CBM_LANG_JAVA, false, "Field"));
|
||||
/* Degenerate input → nothing to judge. */
|
||||
ASSERT_FALSE(cbm_go_suppress_bare_field_ref(true, false, NULL));
|
||||
ASSERT_FALSE(cbm_suppress_bare_field_ref(CBM_LANG_GO, false, 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
|
||||
* bare C identifier — a macro argument like RUN_SELECTED_SUITE(mcp) — is
|
||||
* never one, so it must not bind a same-named Field. */
|
||||
ASSERT_TRUE(cbm_suppress_bare_field_ref(CBM_LANG_C, false, "Field"));
|
||||
/* The member half still binds (through the C owner rows upstream). */
|
||||
ASSERT_FALSE(cbm_suppress_bare_field_ref(CBM_LANG_C, true, "Field"));
|
||||
/* A bare C name keeps binding globals, functions and types. */
|
||||
ASSERT_FALSE(cbm_suppress_bare_field_ref(CBM_LANG_C, false, "Variable"));
|
||||
ASSERT_FALSE(cbm_suppress_bare_field_ref(CBM_LANG_C, false, "Function"));
|
||||
ASSERT_FALSE(cbm_suppress_bare_field_ref(CBM_LANG_C, false, "Type"));
|
||||
/* C++ (and every `.h`, parsed as C++): a method body names its own
|
||||
* members bare, so the bind is kept. */
|
||||
ASSERT_FALSE(cbm_suppress_bare_field_ref(CBM_LANG_CPP, false, "Field"));
|
||||
PASS();
|
||||
}
|
||||
|
||||
@@ -2571,6 +2590,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(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);
|
||||
RUN_TEST(c_arrow_member_call_is_c_family_and_arrow_only);
|
||||
|
||||
Reference in new issue
Block a user