Files
codebase-memory-mcp/tests/test_call_reference_contract.c
Martin Vogel 896d7611fb test: flip Python pins that true rules now satisfy
- fastapi_call_quality pinned found 4, missing 3 with three known gaps. The check
  now reports found 7, missing 0, wrong 0 and PROMOTE_TO_REQUIRED on all three,
  so they are required. This is the rest of incr_full_edge_types.
- python_unknown_attribute_effects: reading an attribute of something unknown says
  nothing about which declaration a typed receiver selects, and nearly every body
  does it, so as a barrier it abstained on everything. Renamed to say so. A
  property read stays a read, never a reference or a call.
- decorated_member_barriers: an instance store does not bar a property read, since a
  property is a data descriptor and the read runs the getter. The rebound receiver,
  the class-level rebinding and __getattribute__ stay barriers.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-11-01 17:34:30 +01:00

873 lines
42 KiB
C

/* Production-graph contracts for callable values. These tests intentionally
* exercise the full MCP indexing path rather than only raw extraction/LSP rows. */
#include "test_framework.h"
#include "repro_harness.h"
#include <store/store.h>
#include <string.h>
static int crc_edge_count(cbm_store_t *store, const char *project, const char *type,
const char *source_name, const char *target_label,
const char *target_name) {
cbm_edge_t *edges = NULL;
int edge_count = 0;
if (cbm_store_find_edges_by_type(store, project, type, &edges, &edge_count) != CBM_STORE_OK) {
return -1;
}
int matches = 0;
for (int i = 0; i < edge_count; i++) {
cbm_node_t source = {0};
cbm_node_t target = {0};
bool source_ok =
cbm_store_find_node_by_id(store, edges[i].source_id, &source) == CBM_STORE_OK;
bool target_ok =
cbm_store_find_node_by_id(store, edges[i].target_id, &target) == CBM_STORE_OK;
if (source_ok && target_ok && source.name && target.name &&
strcmp(source.name, source_name) == 0 && strcmp(target.name, target_name) == 0 &&
(!target_label || (target.label && strcmp(target.label, target_label) == 0))) {
matches++;
}
cbm_node_free_fields(&source);
cbm_node_free_fields(&target);
}
cbm_store_free_edges(edges, edge_count);
return matches;
}
/* A source binding must name this declaration, not another same-name node. */
static int crc_exact_edge_count(cbm_store_t *store, const char *project, const char *type,
const char *source_suffix, const char *target_suffix,
const char *target_label, const char *target_path, int start_line,
int end_line) {
char source_qn[512], target_qn[512];
int source_length = snprintf(source_qn, sizeof(source_qn), "%s.%s", project, source_suffix);
int target_length = snprintf(target_qn, sizeof(target_qn), "%s.%s", project, target_suffix);
if (source_length < 0 || (size_t)source_length >= sizeof(source_qn) || target_length < 0 ||
(size_t)target_length >= sizeof(target_qn))
return -1;
cbm_edge_t *edges = NULL;
int edge_count = 0, matches = 0;
if (cbm_store_find_edges_by_type(store, project, type, &edges, &edge_count) != CBM_STORE_OK)
return -1;
for (int i = 0; i < edge_count; i++) {
cbm_node_t source = {0}, target = {0};
bool source_ok =
cbm_store_find_node_by_id(store, edges[i].source_id, &source) == CBM_STORE_OK;
bool target_ok =
cbm_store_find_node_by_id(store, edges[i].target_id, &target) == CBM_STORE_OK;
if (source_ok && target_ok && source.qualified_name && target.qualified_name &&
target.label && target.file_path && !strcmp(source.qualified_name, source_qn) &&
!strcmp(target.qualified_name, target_qn) && !strcmp(target.label, target_label) &&
!strcmp(target.file_path, target_path) && target.start_line == start_line &&
target.end_line == end_line)
matches++;
cbm_node_free_fields(&source);
cbm_node_free_fields(&target);
}
cbm_store_free_edges(edges, edge_count);
return matches;
}
/* Edges of `type` out of `source_name` whose target's qualified name ends with
* `target_qn_suffix`. Two methods in one project can share a short name, and
* then only the qualified name tells them apart. */
static int crc_edge_count_to_qn(cbm_store_t *store, const char *project, const char *type,
const char *source_name, const char *target_qn_suffix) {
cbm_edge_t *edges = NULL;
int edge_count = 0;
if (cbm_store_find_edges_by_type(store, project, type, &edges, &edge_count) != CBM_STORE_OK) {
return -1;
}
int matches = 0;
size_t suffix_len = strlen(target_qn_suffix);
for (int i = 0; i < edge_count; i++) {
cbm_node_t source = {0};
cbm_node_t target = {0};
bool source_ok =
cbm_store_find_node_by_id(store, edges[i].source_id, &source) == CBM_STORE_OK;
bool target_ok =
cbm_store_find_node_by_id(store, edges[i].target_id, &target) == CBM_STORE_OK;
if (source_ok && target_ok && source.name && target.qualified_name &&
strcmp(source.name, source_name) == 0) {
size_t qn_len = strlen(target.qualified_name);
if (qn_len >= suffix_len &&
strcmp(target.qualified_name + qn_len - suffix_len, target_qn_suffix) == 0) {
matches++;
}
}
cbm_node_free_fields(&source);
cbm_node_free_fields(&target);
}
cbm_store_free_edges(edges, edge_count);
return matches;
}
static int crc_global_handler_edge_count(cbm_store_t *store, const char *project,
const char *caller) {
int references =
crc_edge_count(store, project, "CALL_REFERENCE", caller, "Function", "handler");
int calls = crc_edge_count(store, project, "CALLS", caller, "Function", "handler");
int usages = crc_edge_count(store, project, "USAGE", caller, "Function", "handler");
return references < 0 || calls < 0 || usages < 0 ? -1 : references + calls + usages;
}
static int crc_function_count(cbm_store_t *store, const char *project, const char *name) {
cbm_node_t *nodes = NULL;
int node_count = 0;
if (cbm_store_find_nodes_by_name(store, project, name, &nodes, &node_count) != CBM_STORE_OK) {
return -1;
}
int functions = 0;
for (int i = 0; i < node_count; i++) {
if (nodes[i].label && strcmp(nodes[i].label, "Function") == 0) {
functions++;
}
}
cbm_store_free_nodes(nodes, node_count);
return functions;
}
TEST(call_reference_typescript_direct_argument_is_exact) {
/* Explicit module scope keeps this callable-value contract independent of
* unsupported script loading/global namespace order. */
static const char source[] = "export {};\n"
"function handler(): void {}\n"
"function register(callback: () => void): void {}\n"
"function bootstrap(): void { register(handler); }\n";
RProj project;
cbm_store_t *store = rh_index(&project, "main.ts", source);
ASSERT_NOT_NULL(store);
int registrar =
crc_edge_count(store, project.project, "CALLS", "bootstrap", "Function", "register");
int reference = crc_edge_count(store, project.project, "CALL_REFERENCE", "bootstrap",
"Function", "handler");
int usage = crc_edge_count(store, project.project, "USAGE", "bootstrap", "Function", "handler");
int fabricated_call =
crc_edge_count(store, project.project, "CALLS", "bootstrap", "Function", "handler");
rh_cleanup(&project, store);
ASSERT_EQ(registrar, 1);
ASSERT_EQ(reference, 1);
ASSERT_EQ(usage, 0);
ASSERT_EQ(fabricated_call, 0);
PASS();
}
TEST(call_reference_kotlin_alias_argument_is_exact) {
static const char source[] = "fun handler(): Unit {}\n"
"fun accept(callback: () -> Unit): Unit {}\n"
"val callback: () -> Unit = ::handler\n"
"fun argument(): Unit {\n"
" accept(callback)\n"
"}\n";
RProj project;
cbm_store_t *store = rh_index(&project, "AliasArgument.kt", source);
ASSERT_NOT_NULL(store);
int reference =
crc_edge_count(store, project.project, "CALL_REFERENCE", "argument", "Function", "handler");
int usage = crc_edge_count(store, project.project, "USAGE", "argument", "Function", "handler");
int fabricated_call =
crc_edge_count(store, project.project, "CALLS", "argument", "Function", "handler");
rh_cleanup(&project, store);
ASSERT_EQ(reference, 1);
ASSERT_EQ(usage, 0);
ASSERT_EQ(fabricated_call, 0);
PASS();
}
TEST(call_reference_python_use_before_local_shadow_is_not_global) {
static const char source[] = "def handler():\n"
" pass\n"
"def accept(callback):\n"
" pass\n"
"def caller():\n"
" accept(handler)\n"
" handler = lambda: None\n";
RProj project;
cbm_store_t *store = rh_index(&project, "shadow.py", source);
ASSERT_NOT_NULL(store);
int registrar = crc_edge_count(store, project.project, "CALLS", "caller", "Function", "accept");
int handler_nodes = crc_function_count(store, project.project, "handler");
int global_edges = crc_global_handler_edge_count(store, project.project, "caller");
rh_cleanup(&project, store);
ASSERT_EQ(registrar, 1);
ASSERT_GTE(handler_nodes, 1);
ASSERT_EQ(global_edges, 0);
PASS();
}
TEST(call_reference_javascript_use_before_local_shadow_is_not_global) {
/* Explicit module scope keeps this callable-value contract independent of
* unsupported script loading/global namespace order. */
static const char source[] = "export {};\n"
"function handler() {}\n"
"function accept(callback) {}\n"
"function caller() {\n"
" accept(handler);\n"
" const handler = () => {};\n"
"}\n";
RProj project;
cbm_store_t *store = rh_index(&project, "shadow.js", source);
ASSERT_NOT_NULL(store);
int registrar = crc_edge_count(store, project.project, "CALLS", "caller", "Function", "accept");
int handler_nodes = crc_function_count(store, project.project, "handler");
int global_edges = crc_global_handler_edge_count(store, project.project, "caller");
rh_cleanup(&project, store);
ASSERT_EQ(registrar, 1);
ASSERT_GTE(handler_nodes, 1);
ASSERT_EQ(global_edges, 0);
PASS();
}
TEST(call_reference_typescript_use_before_local_shadow_is_not_global) {
/* Explicit module scope keeps this callable-value contract independent of
* unsupported script loading/global namespace order. */
static const char source[] = "export {};\n"
"function handler(): void {}\n"
"function accept(callback: () => void): void {}\n"
"function caller(): void {\n"
" accept(handler);\n"
" const handler = (): void => {};\n"
"}\n";
RProj project;
cbm_store_t *store = rh_index(&project, "shadow.ts", source);
ASSERT_NOT_NULL(store);
int registrar = crc_edge_count(store, project.project, "CALLS", "caller", "Function", "accept");
int handler_nodes = crc_function_count(store, project.project, "handler");
int global_edges = crc_global_handler_edge_count(store, project.project, "caller");
rh_cleanup(&project, store);
ASSERT_EQ(registrar, 1);
ASSERT_GTE(handler_nodes, 1);
ASSERT_EQ(global_edges, 0);
PASS();
}
TEST(call_reference_python_keyword_argument_is_exact) {
static const char source[] = "def handler():\n"
" pass\n"
"def accept(callback=None):\n"
" pass\n"
"def argument():\n"
" accept(callback=handler)\n";
RProj project;
cbm_store_t *store = rh_index(&project, "keyword.py", source);
ASSERT_NOT_NULL(store);
int reference =
crc_edge_count(store, project.project, "CALL_REFERENCE", "argument", "Function", "handler");
int usage = crc_edge_count(store, project.project, "USAGE", "argument", "Function", "handler");
int fabricated_call =
crc_edge_count(store, project.project, "CALLS", "argument", "Function", "handler");
rh_cleanup(&project, store);
ASSERT_EQ(reference, 1);
ASSERT_EQ(usage, 0);
ASSERT_EQ(fabricated_call, 0);
PASS();
}
TEST(call_reference_python_bound_method_argument_is_exact) {
static const char source[] = "class Service:\n"
" def handler(self):\n"
" pass\n"
"def accept(callback):\n"
" pass\n"
"def argument(service: Service):\n"
" accept(service.handler)\n";
RProj project;
cbm_store_t *store = rh_index(&project, "bound.py", source);
ASSERT_NOT_NULL(store);
int reference =
crc_edge_count(store, project.project, "CALL_REFERENCE", "argument", "Method", "handler");
int usage = crc_edge_count(store, project.project, "USAGE", "argument", "Method", "handler");
int fabricated_call =
crc_edge_count(store, project.project, "CALLS", "argument", "Method", "handler");
rh_cleanup(&project, store);
ASSERT_EQ(reference, 1);
ASSERT_EQ(usage, 0);
ASSERT_EQ(fabricated_call, 0);
PASS();
}
TEST(call_reference_python_parenthesized_bound_method_is_exact) {
static const char source[] = "class Service:\n"
" def handler(self):\n"
" pass\n"
"def accept(callback):\n"
" pass\n"
"def argument(service: Service):\n"
" accept((service.handler))\n";
RProj project;
cbm_store_t *store = rh_index(&project, "parenthesized.py", source);
ASSERT_NOT_NULL(store);
int reference =
crc_edge_count(store, project.project, "CALL_REFERENCE", "argument", "Method", "handler");
int usage = crc_edge_count(store, project.project, "USAGE", "argument", "Method", "handler");
int fabricated_call =
crc_edge_count(store, project.project, "CALLS", "argument", "Method", "handler");
rh_cleanup(&project, store);
ASSERT_EQ(reference, 1);
ASSERT_EQ(usage, 0);
ASSERT_EQ(fabricated_call, 0);
PASS();
}
TEST(call_reference_python_instance_field_shadow_is_not_method) {
static const char source[] = "class Service:\n"
" def __init__(self):\n"
" self.handler = 42\n"
" def handler(self):\n"
" pass\n"
"def accept(callback):\n"
" pass\n"
"def argument(service: Service):\n"
" accept(service.handler)\n";
RProj project;
cbm_store_t *store = rh_index(&project, "field_shadow.py", source);
ASSERT_NOT_NULL(store);
int method_reference =
crc_edge_count(store, project.project, "CALL_REFERENCE", "argument", "Method", "handler");
int any_reference =
crc_edge_count(store, project.project, "CALL_REFERENCE", "argument", NULL, "handler");
int usage = crc_edge_count(store, project.project, "USAGE", "argument", NULL, "handler");
int fabricated_call =
crc_edge_count(store, project.project, "CALLS", "argument", NULL, "handler");
/* WHY: instance field has no distinct materialized declaration; the Method namesake is not its
* target. TRIED: exact source declaration identity, without relaxing the collision guard.
* resolver_quality_python_materialization_gaps_are_counted retains this genuine reference in
* the independent quality denominator. */
int exact_usage =
crc_exact_edge_count(store, project.project, "USAGE", "field_shadow.argument",
"field_shadow.Service.handler", "Field", "field_shadow.py", 3, 3);
rh_cleanup(&project, store);
ASSERT_EQ(method_reference, 0);
ASSERT_EQ(any_reference, 0);
ASSERT_EQ(usage, exact_usage);
ASSERT(usage >= 0 && usage <= 1);
ASSERT_EQ(fabricated_call, 0);
PASS();
}
TEST(call_reference_python_property_value_stays_usage) {
static const char source[] = "class Service:\n"
" @property\n"
" def handler(self) -> int:\n"
" return 42\n"
"def accept(callback):\n"
" pass\n"
"def argument(service: Service):\n"
" accept(service.handler)\n";
RProj project;
cbm_store_t *store = rh_index(&project, "property.py", source);
ASSERT_NOT_NULL(store);
int accept_call =
crc_edge_count(store, project.project, "CALLS", "argument", "Function", "accept");
int reference =
crc_edge_count(store, project.project, "CALL_REFERENCE", "argument", NULL, "handler");
int usage = crc_edge_count(store, project.project, "USAGE", "argument", "Method", "handler");
int fabricated_call =
crc_edge_count(store, project.project, "CALLS", "argument", NULL, "handler");
int exact_usage =
crc_exact_edge_count(store, project.project, "USAGE", "property.argument",
"property.Service.handler", "Method", "property.py", 3, 4);
rh_cleanup(&project, store);
ASSERT_EQ(accept_call, 1);
ASSERT_EQ(reference, 0);
ASSERT_EQ(usage, 1);
ASSERT_EQ(exact_usage, 1);
ASSERT_EQ(fabricated_call, 0);
PASS();
}
TEST(call_reference_python_unknown_decorated_value_stays_usage) {
static const char source[] = "def replace_with_value(function):\n"
" return 42\n"
"class Service:\n"
" @replace_with_value\n"
" def handler(self):\n"
" pass\n"
"def accept(callback):\n"
" pass\n"
"def argument(service: Service):\n"
" accept(service.handler)\n";
RProj project;
cbm_store_t *store = rh_index(&project, "unknown_decorator.py", source);
ASSERT_NOT_NULL(store);
int accept_call =
crc_edge_count(store, project.project, "CALLS", "argument", "Function", "accept");
int reference =
crc_edge_count(store, project.project, "CALL_REFERENCE", "argument", NULL, "handler");
int usage = crc_edge_count(store, project.project, "USAGE", "argument", "Method", "handler");
int fabricated_call =
crc_edge_count(store, project.project, "CALLS", "argument", NULL, "handler");
int exact_usage = crc_exact_edge_count(
store, project.project, "USAGE", "unknown_decorator.argument",
"unknown_decorator.Service.handler", "Method", "unknown_decorator.py", 5, 6);
rh_cleanup(&project, store);
ASSERT_EQ(accept_call, 1);
ASSERT_EQ(reference, 0);
ASSERT_EQ(usage, 1);
ASSERT_EQ(exact_usage, 1);
ASSERT_EQ(fabricated_call, 0);
PASS();
}
TEST(call_reference_python_shadowed_property_name_is_only_a_value_binding) {
static const char source[] = "def property(function):\n"
" return 42\n"
"class Service:\n"
" @property\n"
" def handler(self):\n"
" pass\n"
"def accept(callback):\n"
" pass\n"
"def argument(service: Service):\n"
" accept(service.handler)\n";
RProj project;
cbm_store_t *store = rh_index(&project, "shadowed_property.py", source);
ASSERT_NOT_NULL(store);
int usage = crc_exact_edge_count(store, project.project, "USAGE", "shadowed_property.argument",
"shadowed_property.Service.handler", "Method",
"shadowed_property.py", 5, 6);
int reference =
crc_edge_count(store, project.project, "CALL_REFERENCE", "argument", NULL, "handler");
int call = crc_edge_count(store, project.project, "CALLS", "argument", NULL, "handler");
rh_cleanup(&project, store);
ASSERT_EQ(usage, 1);
ASSERT_EQ(reference, 0);
ASSERT_EQ(call, 0);
PASS();
}
TEST(call_reference_python_decorated_member_barriers_reject_value_edges) {
static const char *const classes[] = {
"class Service:\n @property\n def handler(self):\n return 42\n",
"class Service:\n @property\n def handler(self):\n return 42\n"
" handler = 42\n",
"class Service:\n @property\n def handler(self):\n return 42\n"
" def __init__(self):\n self.handler = 42\n",
"class Service:\n @property\n def handler(self):\n return 42\n"
" def __getattribute__(self, name):\n return 42\n"};
for (size_t i = 0; i < sizeof(classes) / sizeof(classes[0]); i++) {
char source[1024];
snprintf(source, sizeof(source),
"%sdef accept(callback):\n pass\ndef argument(service: Service):\n%s"
" accept(service.handler)\n",
classes[i], i == 0 ? " service = 42\n" : "");
RProj project;
cbm_store_t *store = rh_index(&project, "value_barrier.py", source);
ASSERT_NOT_NULL(store);
int usage = crc_edge_count(store, project.project, "USAGE", "argument", NULL, "handler");
int reference =
crc_edge_count(store, project.project, "CALL_REFERENCE", "argument", NULL, "handler");
int call = crc_edge_count(store, project.project, "CALLS", "argument", NULL, "handler");
rh_cleanup(&project, store);
/* An instance store does not bar the read: a property is a data descriptor, so the
* read runs the getter whatever stores there. The other barriers do. */
ASSERT_EQ(usage, i == 2 ? 1 : 0);
ASSERT_EQ(reference, 0);
ASSERT_EQ(call, 0);
}
PASS();
}
/* Reading an attribute of something unknown says nothing about which declaration a typed
* receiver selects. Nearly every function body does it, so as a barrier it abstained on
* everything. A property read stays a read: never a reference and never a call. */
TEST(call_reference_python_unknown_attribute_effects_keep_the_receiver_fact) {
static const char *const statements[] = {" unknown.attribute\n accept(service.handler)\n",
" unknown.method(service.handler)\n"};
for (size_t i = 0; i < sizeof(statements) / sizeof(statements[0]); i++) {
char source[1024];
snprintf(source, sizeof(source),
"class Service:\n @property\n def handler(self):\n return 42\n"
"def accept(callback):\n pass\ndef argument(service: Service):\n%s",
statements[i]);
RProj project;
cbm_store_t *store = rh_index(&project, "descriptor_effect.py", source);
ASSERT_NOT_NULL(store);
int usage = crc_edge_count(store, project.project, "USAGE", "argument", NULL, "handler");
int reference =
crc_edge_count(store, project.project, "CALL_REFERENCE", "argument", NULL, "handler");
int call = crc_edge_count(store, project.project, "CALLS", "argument", NULL, "handler");
rh_cleanup(&project, store);
ASSERT_EQ(usage, 1);
ASSERT_EQ(reference, 0);
ASSERT_EQ(call, 0);
}
PASS();
}
TEST(call_reference_python_cross_file_decorated_value_identity) {
static const char library[] = "class Service:\n"
" @property\n"
" def handler(self):\n"
" return 42\n";
static const char source[] = "from lib import Service\n"
"def accept(callback):\n"
" pass\n"
"def argument(service: Service):\n"
" accept(((service.handler)))\n"
"def invocation(service: Service):\n"
" service.handler()\n";
RFile files[] = {{"lib.py", library}, {"main.py", source}};
RProj project;
cbm_store_t *store = rh_index_files(&project, files, 2);
ASSERT_NOT_NULL(store);
int usage = crc_exact_edge_count(store, project.project, "USAGE", "main.argument",
"lib.Service.handler", "Method", "lib.py", 3, 4);
int reference =
crc_edge_count(store, project.project, "CALL_REFERENCE", "argument", NULL, "handler");
int call = crc_edge_count(store, project.project, "CALLS", "invocation", NULL, "handler");
rh_cleanup(&project, store);
ASSERT_EQ(usage, 1);
ASSERT_EQ(reference, 0);
ASSERT_EQ(call, 0);
PASS();
}
TEST(call_reference_python_later_decorated_function_rebinding_stays_usage) {
static const char source[] = "def handler():\n"
" pass\n"
"def replace_with_value(function):\n"
" return 42\n"
"@replace_with_value\n"
"def handler():\n"
" pass\n"
"def accept(callback):\n"
" pass\n"
"def argument():\n"
" accept(handler)\n";
RProj project;
cbm_store_t *store = rh_index(&project, "function_rebinding.py", source);
ASSERT_NOT_NULL(store);
int accept_call =
crc_edge_count(store, project.project, "CALLS", "argument", "Function", "accept");
int reference =
crc_edge_count(store, project.project, "CALL_REFERENCE", "argument", NULL, "handler");
int usage = crc_edge_count(store, project.project, "USAGE", "argument", "Function", "handler");
int fabricated_call =
crc_edge_count(store, project.project, "CALLS", "argument", NULL, "handler");
/* WHY: multiple source declarations collapse to one graph QN; the earlier declaration is not
* the later value binding. TRIED: exact source declaration identity, without relaxing the
* collision guard. resolver_quality_python_materialization_gaps_are_counted retains this
* genuine reference in the independent quality denominator. */
int exact_usage = crc_exact_edge_count(
store, project.project, "USAGE", "function_rebinding.argument",
"function_rebinding.handler", "Function", "function_rebinding.py", 6, 7);
rh_cleanup(&project, store);
ASSERT_EQ(accept_call, 1);
ASSERT_EQ(reference, 0);
ASSERT_EQ(usage, exact_usage);
ASSERT(usage >= 0 && usage <= 1);
ASSERT_EQ(fabricated_call, 0);
PASS();
}
TEST(call_reference_python_later_decorated_method_rebinding_stays_usage) {
static const char source[] = "def replace_with_value(function):\n"
" return 42\n"
"class Service:\n"
" def handler(self):\n"
" pass\n"
" @replace_with_value\n"
" def handler(self):\n"
" pass\n"
"def accept(callback):\n"
" pass\n"
"def argument(service: Service):\n"
" accept(service.handler)\n";
RProj project;
cbm_store_t *store = rh_index(&project, "method_rebinding.py", source);
ASSERT_NOT_NULL(store);
int accept_call =
crc_edge_count(store, project.project, "CALLS", "argument", "Function", "accept");
int reference =
crc_edge_count(store, project.project, "CALL_REFERENCE", "argument", NULL, "handler");
int usage = crc_edge_count(store, project.project, "USAGE", "argument", "Method", "handler");
int fabricated_call =
crc_edge_count(store, project.project, "CALLS", "argument", NULL, "handler");
/* WHY: multiple source methods collapse to one graph QN; the earlier declaration is not the
* later value binding. TRIED: exact source declaration identity, without relaxing the collision
* guard. resolver_quality_python_materialization_gaps_are_counted retains
* this genuine reference in the independent quality denominator. */
int exact_usage = crc_exact_edge_count(
store, project.project, "USAGE", "method_rebinding.argument",
"method_rebinding.Service.handler", "Method", "method_rebinding.py", 7, 8);
rh_cleanup(&project, store);
ASSERT_EQ(accept_call, 1);
ASSERT_EQ(reference, 0);
ASSERT_EQ(usage, exact_usage);
ASSERT(usage >= 0 && usage <= 1);
ASSERT_EQ(fabricated_call, 0);
PASS();
}
TEST(call_reference_go_bound_method_argument_is_exact) {
static const char source[] = "package refs\n"
"type Service struct{}\n"
"func (Service) handler() {}\n"
"func accept(callback func()) {}\n"
"func argument(service Service) { accept(service.handler) }\n";
RProj project;
cbm_store_t *store = rh_index(&project, "bound.go", source);
ASSERT_NOT_NULL(store);
int reference =
crc_edge_count(store, project.project, "CALL_REFERENCE", "argument", "Method", "handler");
int usage = crc_edge_count(store, project.project, "USAGE", "argument", "Method", "handler");
int fabricated_call =
crc_edge_count(store, project.project, "CALLS", "argument", "Method", "handler");
rh_cleanup(&project, store);
ASSERT_EQ(reference, 1);
ASSERT_EQ(usage, 0);
ASSERT_EQ(fabricated_call, 0);
PASS();
}
TEST(call_reference_go_ambiguous_promoted_method_has_no_target) {
static const char source[] = "package refs\n"
"type Alpha struct{}\n"
"func (Alpha) handler() {}\n"
"type Beta struct{}\n"
"func (Beta) handler() {}\n"
"type Service struct { Alpha; Beta }\n"
"func accept(callback func()) {}\n"
"func argument(service Service) { accept(service.handler) }\n";
RProj project;
cbm_store_t *store = rh_index(&project, "ambiguous.go", source);
ASSERT_NOT_NULL(store);
int reference =
crc_edge_count(store, project.project, "CALL_REFERENCE", "argument", "Method", "handler");
int usage = crc_edge_count(store, project.project, "USAGE", "argument", "Method", "handler");
int fabricated_call =
crc_edge_count(store, project.project, "CALLS", "argument", "Method", "handler");
/* Both methods occur at the same embedding depth. Go has no unique
* selector binding here; USAGE cannot select an arbitrary owner either. */
int alpha_usage = crc_exact_edge_count(store, project.project, "USAGE", "argument",
"Alpha.handler", "Method", "ambiguous.go", 3, 3);
int beta_usage = crc_exact_edge_count(store, project.project, "USAGE", "argument",
"Beta.handler", "Method", "ambiguous.go", 5, 5);
rh_cleanup(&project, store);
ASSERT_EQ(reference, 0);
ASSERT_EQ(usage, 0);
ASSERT_EQ(alpha_usage, 0);
ASSERT_EQ(beta_usage, 0);
ASSERT_EQ(fabricated_call, 0);
PASS();
}
TEST(call_java_record_accessor_binds_the_record) {
/* A record's accessors are implicit, so `record Point(double x, ...)`
* declares no `x()` in its source. Extraction emits them, and `other.x()`
* binds to the record's own accessor.
*
* Two things must not happen instead. The call must not reach the
* project-wide short-name registry and land on the first `x` in the tree —
* here an unrelated class's private field. And the guard that refuses such
* data binds must not swallow a real method that merely shares the
* spelling: the builder's `x(int)`, declared beside its own `x` field,
* keeps its edge. */
static const RFile files[] = {
{"Point.java", "package com.example;\n"
"public record Point(double x, double y, double z) {\n"
" public double first(Point other) {\n"
" return Maths.abs(other.x()) + new Builder().x(5).made();\n"
" }\n"
"}\n"},
{"Spring.java", "package com.example;\n"
"public final class Spring {\n"
" private double x;\n"
" public void put(double v) { this.x = v; }\n"
"}\n"},
{"Builder.java", "package com.example;\n"
"public final class Builder {\n"
" private int x;\n"
" public Builder x(int v) { this.x = v; return this; }\n"
" public int made() { return x; }\n"
"}\n"},
{"Maths.java", "package com.example;\n"
"public final class Maths {\n"
" public static double abs(double v) { return v < 0 ? -v : v; }\n"
"}\n"}};
RProj project;
cbm_store_t *store = rh_index_files(&project, files, 4);
ASSERT_NOT_NULL(store);
int fabricated_field = crc_edge_count(store, project.project, "CALLS", "first", "Field", "x");
int fabricated_var = crc_edge_count(store, project.project, "CALLS", "first", "Variable", "x");
int genuine = crc_edge_count(store, project.project, "CALLS", "first", "Method", "abs");
int accessor = crc_edge_count_to_qn(store, project.project, "CALLS", "first", ".Point.x");
int builder = crc_edge_count_to_qn(store, project.project, "CALLS", "first", ".Builder.x");
rh_cleanup(&project, store);
ASSERT_EQ(fabricated_field, 0);
ASSERT_EQ(fabricated_var, 0);
ASSERT_EQ(genuine, 1);
ASSERT_EQ(accessor, 1);
ASSERT_EQ(builder, 1);
PASS();
}
/* Static imports can name both an inherited method and a field. Thread's
* activeCount() is external to this project, whereas Owner.activeCount is
* local data; an unresolved import must not turn the field into a callee. */
static int crc_java_static_import_data_call_count(bool parallel) {
RFile files[52] = {{"example/Owner.java", "package example;\n"
"public class Owner extends Thread {\n"
" public static int activeCount;\n"
"}\n"},
{"example/Caller.java", "package example;\n"
"import static example.Owner.activeCount;\n"
"class Caller {\n"
" int caller() { return activeCount(); }\n"
"}\n"}};
char names[50][64];
char sources[50][64];
int count = parallel ? 52 : 2;
for (int i = 2; i < count; i++) {
snprintf(names[i - 2], sizeof(names[i - 2]), "example/Pad%d.java", i);
snprintf(sources[i - 2], sizeof(sources[i - 2]), "package example; class Pad%d {}\n", i);
files[i].name = names[i - 2];
files[i].content = sources[i - 2];
}
RProj project;
cbm_store_t *store = rh_index_files(&project, files, count);
if (!store) {
return -1;
}
int fields = crc_edge_count(store, project.project, "CALLS", "caller", "Field", "activeCount");
int vars = crc_edge_count(store, project.project, "CALLS", "caller", "Variable", "activeCount");
rh_cleanup(&project, store);
return fields < 0 || vars < 0 ? -1 : fields + vars;
}
TEST(call_java_static_import_does_not_call_field_sequential) {
ASSERT_EQ(crc_java_static_import_data_call_count(false), 0);
PASS();
}
TEST(call_java_static_import_does_not_call_field_parallel) {
ASSERT_EQ(crc_java_static_import_data_call_count(true), 0);
PASS();
}
TEST(call_java_record_varargs_accessor_has_array_return) {
static const char *sources[] = {
"record Values(String... xs) {}\n", "record Values(String[]... xs) {}\n",
"record Values(int... xs) {}\n", "record Values(@Deprecated String... xs) {}\n",
"record Values(String... xs) { public String[] xs() { return xs; } }\n"};
static const char *return_types[] = {"String[]", "String[][]", "int[]", "String[]", "String[]"};
for (int fixture = 0; fixture < 5; fixture++) {
CBMFileResult *result =
cbm_extract_file(sources[fixture], (int)strlen(sources[fixture]), CBM_LANG_JAVA,
"repro", "Values.java", 0, NULL, NULL);
ASSERT_NOT_NULL(result);
int accessors = 0;
bool array_return = false;
for (int i = 0; i < result->defs.count; i++) {
CBMDefinition *def = &result->defs.items[i];
if (strcmp(def->name, "xs") == 0 && strcmp(def->label, "Method") == 0) {
accessors++;
array_return =
def->return_type && strcmp(def->return_type, return_types[fixture]) == 0;
}
}
cbm_free_result(result);
ASSERT_EQ(accessors, 1);
ASSERT_TRUE(array_return);
}
PASS();
}
/* Comment extras in an otherwise empty parameter list must not create a
* second, implicit accessor at the record component's source location. */
TEST(call_java_record_commented_accessor_has_only_explicit_span) {
static const char *sources[] = {"record Value(int x) {\n"
" public int x(/* explanation */) {\n"
" return x;\n"
" }\n"
"}\n",
"record Value(int x) {\n"
" public int x(// explanation\n"
" ) {\n"
" return x;\n"
" }\n"
"}\n"};
static const int end_lines[] = {4, 5};
for (int fixture = 0; fixture < 2; fixture++) {
CBMFileResult *result =
cbm_extract_file(sources[fixture], (int)strlen(sources[fixture]), CBM_LANG_JAVA,
"repro", "Value.java", 0, NULL, NULL);
ASSERT_NOT_NULL(result);
int accessors = 0;
int explicit_spans = 0;
for (int i = 0; i < result->defs.count; i++) {
CBMDefinition *def = &result->defs.items[i];
if (strcmp(def->name, "x") == 0 && strcmp(def->label, "Method") == 0) {
accessors++;
if (def->start_line == 2 && def->end_line == end_lines[fixture]) {
explicit_spans++;
}
}
}
cbm_free_result(result);
ASSERT_EQ(accessors, 1);
ASSERT_EQ(explicit_spans, 1);
}
PASS();
}
TEST(call_java_record_commented_overload_keeps_implicit_accessor) {
static const char source[] =
"record Value(int x) {\n"
" public int x(/* explanation */ int scale) { return x * scale; }\n"
"}\n";
CBMFileResult *result = cbm_extract_file(source, (int)strlen(source), CBM_LANG_JAVA, "repro",
"Value.java", 0, NULL, NULL);
ASSERT_NOT_NULL(result);
int methods = 0;
int implicit_spans = 0;
int overload_spans = 0;
for (int i = 0; i < result->defs.count; i++) {
CBMDefinition *def = &result->defs.items[i];
if (strcmp(def->name, "x") == 0 && strcmp(def->label, "Method") == 0) {
methods++;
if (def->start_line == 1 && def->end_line == 1) {
implicit_spans++;
}
if (def->start_line == 2 && def->end_line == 2) {
overload_spans++;
}
}
}
cbm_free_result(result);
ASSERT_EQ(methods, 2);
ASSERT_EQ(implicit_spans, 1);
ASSERT_EQ(overload_spans, 1);
PASS();
}
SUITE(call_reference_contract) {
RUN_TEST(call_reference_typescript_direct_argument_is_exact);
RUN_TEST(call_reference_kotlin_alias_argument_is_exact);
RUN_TEST(call_reference_python_use_before_local_shadow_is_not_global);
RUN_TEST(call_reference_javascript_use_before_local_shadow_is_not_global);
RUN_TEST(call_reference_typescript_use_before_local_shadow_is_not_global);
RUN_TEST(call_reference_python_keyword_argument_is_exact);
RUN_TEST(call_reference_python_bound_method_argument_is_exact);
RUN_TEST(call_reference_python_parenthesized_bound_method_is_exact);
RUN_TEST(call_reference_python_instance_field_shadow_is_not_method);
RUN_TEST(call_reference_python_property_value_stays_usage);
RUN_TEST(call_reference_python_unknown_attribute_effects_keep_the_receiver_fact);
RUN_TEST(call_reference_python_cross_file_decorated_value_identity);
RUN_TEST(call_reference_python_shadowed_property_name_is_only_a_value_binding);
RUN_TEST(call_reference_python_decorated_member_barriers_reject_value_edges);
RUN_TEST(call_reference_python_unknown_decorated_value_stays_usage);
RUN_TEST(call_reference_python_later_decorated_function_rebinding_stays_usage);
RUN_TEST(call_reference_python_later_decorated_method_rebinding_stays_usage);
RUN_TEST(call_reference_go_bound_method_argument_is_exact);
RUN_TEST(call_reference_go_ambiguous_promoted_method_has_no_target);
RUN_TEST(call_java_record_accessor_binds_the_record);
RUN_TEST(call_java_record_varargs_accessor_has_array_return);
RUN_TEST(call_java_record_commented_accessor_has_only_explicit_span);
RUN_TEST(call_java_record_commented_overload_keeps_implicit_accessor);
RUN_TEST(call_java_static_import_does_not_call_field_sequential);
RUN_TEST(call_java_static_import_does_not_call_field_parallel);
}