fix(python): skip annotation-only imports without a new raw free

The memory-core linter counts raw allocator calls per file and fails when a
file grows. The TYPE_CHECKING change freed the copied local name of an
annotation-only import with a new raw free in two import-map builders.
build_import_map now asks the predicate on the name's span in the edge
properties before copying it (cbm_imports_local_span_binds_runtime_name,
which cbm_imports_local_binds_runtime_name now wraps), and the cross-LSP
builder routes an annotation-only name through its existing no-value path.
Same behaviour, no new raw allocator site.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
This commit is contained in:
Martin Vogel committed 2026-10-19 02:11:45 +02:00
1 parent 2455e75578
commit 3ead775d0b
4 files changed
+36 -19

No files matched your search

+10 -3
View File
@@ -402,14 +402,16 @@ void cbm_imports_push(CBMImportArray *arr, CBMArena *a, CBMImport imp) {
arr->items[arr->count++] = imp;
}
bool cbm_imports_local_binds_runtime_name(const CBMImportArray *imports, const char *local_name) {
if (!imports || !local_name) {
bool cbm_imports_local_span_binds_runtime_name(const CBMImportArray *imports, const char *name,
size_t len) {
if (!imports || !name) {
return true;
}
bool seen = false;
for (int i = 0; i < imports->count; i++) {
const CBMImport *imp = &imports->items[i];
if (!imp->local_name || strcmp(imp->local_name, local_name) != 0) {
if (!imp->local_name || strncmp(imp->local_name, name, len) != 0 ||
imp->local_name[len] != '\0') {
continue;
}
if (cbm_import_binds_runtime_name(imp)) {
@@ -420,6 +422,11 @@ bool cbm_imports_local_binds_runtime_name(const CBMImportArray *imports, const c
return !seen;
}
bool cbm_imports_local_binds_runtime_name(const CBMImportArray *imports, const char *local_name) {
return !local_name ||
cbm_imports_local_span_binds_runtime_name(imports, local_name, strlen(local_name));
}
void cbm_usages_push(CBMUsageArray *arr, CBMArena *a, CBMUsage usage) {
GROW_ARRAY(arr, a);
arr->items[arr->count++] = usage;
+4
View File
@@ -1321,6 +1321,10 @@ void cbm_imports_push(CBMImportArray *arr, CBMArena *a, CBMImport imp);
* annotation-only (cbm_import_binds_runtime_name), so also true when no import
* names it: the rule for the builders that key IMPORTS edges by local name. */
bool cbm_imports_local_binds_runtime_name(const CBMImportArray *imports, const char *local_name);
/* The same for the `len` bytes at `name` (not NUL-terminated): lets a caller
* decide before it copies the name. */
bool cbm_imports_local_span_binds_runtime_name(const CBMImportArray *imports, const char *name,
size_t len);
void cbm_usages_push(CBMUsageArray *arr, CBMArena *a, CBMUsage usage);
void cbm_throws_push(CBMThrowArray *arr, CBMArena *a, CBMThrow thr);
void cbm_rw_push(CBMRWArray *arr, CBMArena *a, CBMReadWrite rw);
+18 -10
View File
@@ -95,21 +95,24 @@ static const char *itoa_log(int val) {
/* Build per-file import map from cached extraction result or graph buffer edges.
* Returns parallel arrays of (local_name, module_qn) pairs. Caller frees. */
/* Parse "local_name":"value" from JSON properties string. Returns strdup'd key or NULL. */
static char *extract_local_name_from_json(const char *props_json) {
/* The "local_name":"value" of a JSON properties string, as a span of it;
* false when absent or empty. */
static bool local_name_span_from_json(const char *props_json, const char **out, size_t *len) {
if (!props_json) {
return NULL;
return false;
}
const char *start = strstr(props_json, "\"local_name\":\"");
if (!start) {
return NULL;
return false;
}
start += strlen("\"local_name\":\"");
const char *end = strchr(start, '"');
if (!end || end <= start) {
return NULL;
return false;
}
return cbm_strndup(start, end - start);
*out = start;
*len = (size_t)(end - start);
return true;
}
/* The graph node whose QN is cbm_pipeline_fqn_compute(project, rel_path, name),
@@ -193,11 +196,16 @@ static int build_import_map(cbm_pipeline_ctx_t *ctx, const char *rel_path,
if (!target) {
continue;
}
char *key = extract_local_name_from_json(e->properties_json);
if (key && !cbm_imports_local_binds_runtime_name(result ? &result->imports : NULL, key)) {
free(key); /* annotation-only import (#1277): no CALLS binding */
key = NULL;
const char *name = NULL;
size_t name_len = 0;
/* An annotation-only import (#1277) binds no CALLS name: skipped before
* the key is copied. */
if (!local_name_span_from_json(e->properties_json, &name, &name_len) ||
!cbm_imports_local_span_binds_runtime_name(result ? &result->imports : NULL, name,
name_len)) {
continue;
}
char *key = cbm_strndup(name, name_len);
if (key) {
keys[count] = key;
vals[count] = target->qualified_name;
+4 -6
View File
@@ -1147,12 +1147,10 @@ int cbm_pxc_build_import_map_ex(const cbm_gbuf_t *gbuf, const char *project_name
continue;
memcpy(local, start, n);
local[n] = '\0';
if (!include_type_only &&
!cbm_imports_local_binds_runtime_name(result ? &result->imports : NULL, local)) {
free(local); /* annotation-only import (#1277): no CALLS binding */
continue;
}
char *value = pxc_import_value_qn(lang, result, local, target);
/* An annotation-only import (#1277) binds no CALLS name: it gets no value. */
bool binds = include_type_only ||
cbm_imports_local_binds_runtime_name(result ? &result->imports : NULL, local);
char *value = binds ? pxc_import_value_qn(lang, result, local, target) : NULL;
if (!value) {
free(local);
continue;