Files
codebase-memory-mcp/tests/test_complexity.c
T
Martin Vogel eccd026782 perf(resolver): read a Dart barrel's exports as a walk reaches them
Every file that imports a barrel read the barrel's whole export list again, though one name's walk stops at the chain's bound of 128 libraries. With the barrel listing the project, that is files x exports: 16,000 libraries took 39.7 s in resolve.

Exports are now read when a walk first reaches them, and a walk stops once the bound is passed, since no further library can then be visited or change its answer. So `dart_export_graph_not_walked` is still what a name past the bound gets.

Left as it was: an export that a `show` or `hide` rules out costs a filter check per name and does not count toward the bound.

complexity_dart_barrel_exports_are_near_linear: 3.71 on the old read, 1.94 now.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-11-03 01:27:36 +01:00

3532 lines
161 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* test_complexity.c — Complexity guard: superlinearity detection at tiny input
* sizes, gated on DETERMINISTIC work counters — never time.
*
* Why this suite exists: the v0.9.0→v0.10.x indexing regression was a
* files×corpus_defs coupling in cross-file LSP (#1669). Finding it took an
* 11-corpus A/B across two release binaries, because nothing in CI could see
* an O(n^2) forming. This suite makes that class of bug fail a unit test.
*
* Method — replicated independent modules:
* Build a synthetic corpus of k INDEPENDENT module copies per language
* (module i never references module j). Then every extensive quantity —
* nodes, edges, and Σ per-file work — MUST grow linearly in k. Run the full
* in-process pipeline at k and 2k and assert counter RATIOS:
*
* linear pipeline ratio ≈ 2 (gate: within [lo, hi])
* files×corpus bug ratio ≈ 4 (per-file work itself grows with k)
*
* Ratios expose the exponent at TINY sizes (dozens of files, seconds of
* runtime): a counter that counts the quadratic term directly doubles its
* growth per doubling regardless of absolute scale. No large corpora needed.
*
* Determinism doctrine (O9): a verdict must be a pure function of
* (code, input). Work counters are sums over per-file work and do not depend
* on scheduling; wall time does. Therefore ONLY counters gate. Throughput
* (nodes/s, edges/s) is measured and written to a LOCAL report under private/
* as information for trend comparison — it never gates, and the report step is
* skipped entirely under CBM_SKIP_PERF (starved fidelity legs would record
* meaningless rates).
*
* Dynamic coverage: languages come from two providers, iterated over the full
* CBM_LANG_COUNT enum —
* 1. embedded templates below (the LSP-hybrid languages, where cross-file
* machinery — and therefore files×corpus coupling risk — lives);
* 2. auto-discovered fixture dirs tests/fixtures/complexity/<lang-name>/
* (drop files there when adding a language; no test edit needed).
* Languages with neither provider are recorded in the report as skipped with
* the reason "no complexity template": their extractors are per-file by
* construction (grammar-only, no cross-file resolution), so the coupling this
* suite hunts cannot arise from them; the shared passes they feed (registry,
* similarity, semantic) are exercised by the template corpus.
*/
#include "lsp/type_registry.h"
#include "test_framework.h"
#include "test_helpers.h"
#include "../src/foundation/compat.h"
#include "../src/foundation/compat_fs.h"
#include "../src/foundation/compat_thread.h"
#include "../src/foundation/log.h"
#include "../src/foundation/profile.h"
#include "cbm.h"
#include "discover/discover.h"
#include "lang_specs.h"
#include "lsp/c_lsp.h"
#include "lsp/kotlin_lsp.h"
#include "lsp/py_lsp.h"
#include "lsp/rust_proof.h"
#include "tree_sitter/api.h"
#include "pipeline/pass_lsp_cross.h"
#include "pipeline/pipeline.h"
#include "pipeline/pipeline_internal.h"
#include "store/store.h"
#include "resolve/stats.h"
#include "foundation/sha256.h"
#include <sqlite3.h>
#include "graph_buffer/graph_buffer.h"
#include "resolve/binding.h"
#include "resolve/rust_names.h"
#include "resolve/source_adapter.h"
#include "lsp/lsp_work.h"
#include "lsp/lsp_name_rows.h"
#define CBM_SYNTAX_INDEX_IMPLEMENTATION /* the seam only: this file asks tree-sitter directly */
#include "resolve/syntax_index.h"
#include "yyjson/yyjson.h"
#include <stdatomic.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
/* Tail-match scan counters (defined in pass_parallel.c, declared in
* lsp_resolve.h — re-declared here to avoid pulling that header's statics). */
extern _Atomic uint64_t g_lsp_tail_lookups;
extern _Atomic uint64_t g_lsp_tail_candidates;
/* Infra resolver candidate counters (pass_k8s.c, pass_operator.c,
* pass_shell.c, pass_terraform.c). */
extern _Atomic uint64_t g_k8s_candidates;
extern _Atomic uint64_t g_op_candidates;
extern _Atomic uint64_t g_sh_candidates;
extern _Atomic uint64_t g_tf_candidates;
extern _Atomic uint64_t g_dk_hub_edges;
/* ── Corpus scale ──────────────────────────────────────────────────────
* Small on purpose: the gate reads exponents from ratios, not magnitudes.
* K_BASE modules vs 2*K_BASE modules, CX_FILES_PER_MOD files each, per
* provider language. Total runtime target for the whole suite: seconds. */
enum {
CX_K_BASE = 3,
CX_FILES_PER_MOD = 5,
/* Gate only when the base run produced enough work for the ratio to be
* meaningful — a near-zero denominator would make the gate noise. The
* false-guard audit rule applies: a separate assertion below proves the
* counter is genuinely nonzero so this gate can never pass vacuously. */
CX_MIN_BASE_WORK = 40,
/* The pipeline resolves sequentially below MIN_FILES_FOR_PARALLEL (50
* files, pipeline.c) and that path builds no shared registries — so both
* legs of every corpus pair must exceed it or the gates measure the wrong
* code path. The sequential path's own per-file registry work is bounded
* by the 50-file ceiling and is deliberately out of scope here. */
CX_BIGPKG_FILES_PER_K = 20,
CX_PYEXT_FILES_PER_MOD = 20,
CX_PYEXT_DEFS_PER_FILE = 120,
CX_PYEXT_PROBES_PER_FILE = 3,
};
/* Linear growth bounds for a 2x input doubling. Fixed structural overhead
* (Project/root nodes) pulls node ratios slightly under 2; template boundary
* effects push edge ratios slightly around 2. A files×corpus coupling lands
* at ~4 — far outside. */
#define CX_RATIO_LO 1.45
#define CX_RATIO_HI 2.75
/* ── Language templates ────────────────────────────────────────────────
* Each emitter writes file j of a module. Files reference file j-1 of the
* SAME module (cross-file resolution work); modules never reference each
* other (independence — the property the linearity gate rests on). */
static void cx_emit_java(FILE *f, int m, int j) {
fprintf(f, "package mod%d;\n\npublic class C%d {\n", m, j);
fprintf(f, " public int val%d(int x) {\n return x + %d;\n }\n", j, j);
if (j > 0) {
fprintf(f, " public int chain() {\n");
fprintf(f, " C%d prev = new C%d();\n", j - 1, j - 1);
fprintf(f, " return prev.val%d(1) + val%d(2);\n }\n", j - 1, j);
} else {
fprintf(f, " public int chain() {\n return val0(2);\n }\n");
}
fprintf(f, "}\n");
}
static void cx_emit_python(FILE *f, int m, int j) {
(void)m;
if (j > 0) {
fprintf(f, "from .f%d import val%d\n\n", j - 1, j - 1);
}
fprintf(f, "def val%d(x):\n return x + %d\n\n", j, j);
if (j > 0) {
fprintf(f, "def chain%d():\n return val%d(1) + val%d(2)\n", j, j - 1, j);
} else {
fprintf(f, "def chain0():\n return val0(2)\n");
}
/* Unknown call effects must invalidate a real receiver binding. The two
* member occurrences exercise both positive proof and stale-type refusal. */
fprintf(f,
"\nclass Box%d:\n def value(self):\n return %d\n\n"
"def mutate%d(effect):\n obj = Box%d()\n obj.value()\n"
" effect()\n obj.value()\n",
j, j, j, j);
}
static void cx_emit_go(FILE *f, int m, int j) {
fprintf(f, "package mod%d\n\n", m);
fprintf(f, "func Val%d(x int) int {\n\treturn x + %d\n}\n\n", j, j);
if (j > 0) {
fprintf(f, "func Chain%d() int {\n\treturn Val%d(1) + Val%d(2)\n}\n", j, j - 1, j);
} else {
fprintf(f, "func Chain0() int {\n\treturn Val0(2)\n}\n");
}
}
static void cx_emit_ts(FILE *f, int m, int j) {
(void)m;
if (j > 0) {
fprintf(f, "import { val%d } from \"./f%d\";\n\n", j - 1, j - 1);
}
fprintf(f, "export function val%d(x: number): number {\n return x + %d;\n}\n\n", j, j);
if (j > 0) {
fprintf(f, "export function chain%d(): number {\n return val%d(1) + val%d(2);\n}\n", j,
j - 1, j);
} else {
fprintf(f, "export function chain0(): number {\n return val0(2);\n}\n");
}
}
typedef struct {
CBMLanguage lang;
const char *dirname; /* corpus subdir, doubles as the per-lang scope */
const char *file_prefix;
const char *ext;
void (*emit)(FILE *f, int m, int j);
} CxTemplate;
static const CxTemplate CX_TEMPLATES[] = {
{CBM_LANG_JAVA, "javasrc", "C", ".java", cx_emit_java},
{CBM_LANG_PYTHON, "pysrc", "f", ".py", cx_emit_python},
{CBM_LANG_GO, "gosrc", "f", ".go", cx_emit_go},
{CBM_LANG_TYPESCRIPT, "tssrc", "f", ".ts", cx_emit_ts},
};
enum { CX_TEMPLATE_COUNT = sizeof(CX_TEMPLATES) / sizeof(CX_TEMPLATES[0]) };
/* Fixture-dir provider: tests/fixtures/complexity/<lang-name>/ — every file
* and directory in it (dot-names aside) is copied verbatim into each module
* dir. Lets a new language join the
* guard by dropping fixtures, with no edit to this suite. */
static bool cx_fixture_dir_for(CBMLanguage lang, char *out, size_t cap) {
const char *name = cbm_language_name(lang);
if (!name || !name[0]) {
return false;
}
snprintf(out, cap, "tests/fixtures/complexity/%s", name);
cbm_dir_t *d = cbm_opendir(out);
if (!d) {
return false;
}
cbm_closedir(d);
return true;
}
static int cx_copy_file(const char *src, const char *dst) {
FILE *in = fopen(src, "rb");
if (!in) {
return -1;
}
FILE *out = fopen(dst, "wb");
if (!out) {
fclose(in);
return -1;
}
char buf[4096];
size_t n;
while ((n = fread(buf, 1, sizeof(buf), in)) > 0) {
fwrite(buf, 1, n, out);
}
fclose(in);
fclose(out);
return 0;
}
/* A fixture entry into `dst`: a file, or a directory with its contents. */
static int cx_copy_entry(const char *src, const char *dst, bool is_dir) {
if (!is_dir) {
return cx_copy_file(src, dst);
}
if (th_mkdir_p(dst) != 0) {
return -1;
}
cbm_dir_t *d = cbm_opendir(src);
if (!d) {
return -1;
}
int rc = 0;
cbm_dirent_t *entry;
while ((entry = cbm_readdir(d)) != NULL) {
if (entry->name[0] == '.') {
continue;
}
char from[1024];
char to[1024];
snprintf(from, sizeof(from), "%s/%s", src, entry->name);
snprintf(to, sizeof(to), "%s/%s", dst, entry->name);
if (cx_copy_entry(from, to, entry->is_dir) != 0) {
rc = -1;
}
}
cbm_closedir(d);
return rc;
}
/* ── Corpus builder ──────────────────────────────────────────────────── */
static int cx_build_corpus(const char *root, int k_modules) {
char dir[1024];
char path[1200];
for (int t = 0; t < CX_TEMPLATE_COUNT; t++) {
const CxTemplate *tp = &CX_TEMPLATES[t];
for (int m = 0; m < k_modules; m++) {
snprintf(dir, sizeof(dir), "%s/%s/mod%d", root, tp->dirname, m);
if (th_mkdir_p(dir) != 0) {
return -1;
}
for (int j = 0; j < CX_FILES_PER_MOD; j++) {
snprintf(path, sizeof(path), "%s/%s%d%s", dir, tp->file_prefix, j, tp->ext);
FILE *f = fopen(path, "w");
if (!f) {
return -1;
}
tp->emit(f, m, j);
fclose(f);
}
}
}
/* Fixture-dir providers: replicate each discovered language dir into
* k module copies. Distinct paths make distinct modules/QNs, which is all
* the independence argument needs. */
for (int lang = 0; lang < CBM_LANG_COUNT; lang++) {
/* a templated language may add fixtures too (Go: the operator
* corpus); they land in their own fx_<lang> root */
char fixdir[512];
if (!cx_fixture_dir_for((CBMLanguage)lang, fixdir, sizeof(fixdir))) {
continue;
}
cbm_dir_t *d = cbm_opendir(fixdir);
if (!d) {
continue;
}
cbm_dirent_t *entry;
while ((entry = cbm_readdir(d)) != NULL) {
if (entry->name[0] == '.') {
continue;
}
for (int m = 0; m < k_modules; m++) {
snprintf(dir, sizeof(dir), "%s/fx_%s/mod%d", root,
cbm_language_name((CBMLanguage)lang), m);
if (th_mkdir_p(dir) != 0) {
continue;
}
char src[1024];
snprintf(src, sizeof(src), "%s/%s", fixdir, entry->name);
snprintf(path, sizeof(path), "%s/%s", dir, entry->name);
(void)cx_copy_entry(src, path, entry->is_dir);
}
}
cbm_closedir(d);
}
return 0;
}
/* Corpus shape #2 — the growing shared package. Real monorepos concentrate
* files in a few large packages (org.<org>.common, …), and the JVM filter
* branch includes every def sharing the file's namespace — so per-file work
* tracks PACKAGE size. A package whose file count scales with the corpus is
* therefore the honest reproducer for the #1669 growth pattern that fully
* independent modules cannot show: here every extensive quantity must STILL
* be linear in k, while a namespace/module-scoped per-file registry build
* goes quadratic. */
static int cx_build_bigpkg(const char *root, int k) {
char dir[1024];
char path[1200];
snprintf(dir, sizeof(dir), "%s/bigsrc/bigpkg", root);
if (th_mkdir_p(dir) != 0) {
return -1;
}
int files = k * CX_BIGPKG_FILES_PER_K;
for (int j = 0; j < files; j++) {
snprintf(path, sizeof(path), "%s/B%d.java", dir, j);
FILE *f = fopen(path, "w");
if (!f) {
return -1;
}
fprintf(f, "package bigpkg;\n\npublic class B%d {\n", j);
fprintf(f, " public int val%d(int x) {\n return x + %d;\n }\n", j, j);
if (j > 0) {
fprintf(f, " public int chain() {\n");
fprintf(f, " B%d prev = new B%d();\n", j - 1, j - 1);
fprintf(f, " return prev.val%d(1) + val%d(2);\n }\n", j - 1, j);
} else {
fprintf(f, " public int chain() {\n return val0(2);\n }\n");
}
fprintf(f, "}\n");
fclose(f);
}
/* One growing Python package exercises export/type propagation and
* invalidation alongside the JVM namespace registry. Each import has a
* bounded direct predecessor, so adding files must not scan the package. */
snprintf(dir, sizeof(dir), "%s/resolverpkg", root);
if (th_mkdir_p(dir) != 0)
return -1;
snprintf(path, sizeof(path), "%s/__init__.py", dir);
FILE *package = cbm_fopen(path, "wb");
if (!package || fclose(package) != 0)
return -1;
for (int j = 0; j < files; j++) {
snprintf(path, sizeof(path), "%s/f%d.py", dir, j);
FILE *f = cbm_fopen(path, "wb");
if (!f)
return -1;
cx_emit_python(f, 0, j);
bool failed = ferror(f) != 0;
if (fclose(f) != 0 || failed)
return -1;
}
return 0;
}
/* Every file probes absent external submodules and retains a real local-call
* control. Independent module copies grow the shared registry without adding
* cross-copy dependencies. Both sizes exceed the parallel-driver threshold. */
static int cx_build_pyext(const char *root, int k) {
char dir[1024];
char path[1200];
for (int m = 0; m < k; m++) {
int n = snprintf(dir, sizeof(dir), "%s/pyext/mod%d", root, m);
if (n < 0 || (size_t)n >= sizeof(dir) || th_mkdir_p(dir) != 0)
return -1;
for (int j = 0; j < CX_PYEXT_FILES_PER_MOD; j++) {
n = snprintf(path, sizeof(path), "%s/p%d.py", dir, j);
if (n < 0 || (size_t)n >= sizeof(path))
return -1;
FILE *f = fopen(path, "w");
if (!f)
return -1;
bool written = fprintf(f, "import extlib\n\n") >= 0;
for (int d = 0; written && d < CX_PYEXT_DEFS_PER_FILE; d++) {
written =
fprintf(f, "def cx_pyext_definition_%d(x):\n return x + %d\n\n", d, d) >= 0;
}
if (written)
written =
fprintf(f, "def cx_pyext_use():\n return (cx_pyext_definition_0(0)") >= 0;
for (int probe = 0; written && probe < CX_PYEXT_PROBES_PER_FILE; probe++) {
written = fprintf(f, " + extlib.sub%d.helper(%d)", probe, probe) >= 0;
}
if (written)
written = fprintf(f, ")\n") >= 0;
int closed = fclose(f);
if (!written || closed != 0)
return -1;
}
}
return 0;
}
/* ── Metrics ─────────────────────────────────────────────────────────── */
/* Per-pass timing capture: a TEE log sink parses `pass.timing` lines during a
* run. Information for the report only — pass timings are wall-clock and never
* gate (O9). */
enum { CX_MAX_PASSES = 48 };
typedef struct {
char name[64];
long ms;
} CxPassMs;
static CxPassMs g_cx_passes[CX_MAX_PASSES];
static _Atomic int g_cx_pass_count = 0;
static void cx_pass_sink(const char *line) {
if (!line || !strstr(line, "pass.timing")) {
return;
}
const char *pp = strstr(line, "pass=");
const char *ee = strstr(line, "elapsed_ms=");
if (!pp || !ee) {
return;
}
int n = atomic_fetch_add_explicit(&g_cx_pass_count, 1, memory_order_relaxed);
if (n >= CX_MAX_PASSES) {
return;
}
size_t i = 0;
pp += 5;
while (pp[i] && pp[i] != ' ' && i < sizeof(g_cx_passes[n].name) - 1) {
g_cx_passes[n].name[i] = pp[i];
i++;
}
g_cx_passes[n].name[i] = '\0';
g_cx_passes[n].ms = atol(ee + 11);
}
/* Fixture-provided infra corpora (tests/fixtures/complexity/<name>/), scoped
* by their copy root: Kubernetes manifests with a kustomization (and a Flux
* HelmRelease whose chartRef names an OCIRepository), a Go operator
* (controller, API types with +groupName, an embedded CRD, a chart loader), a
* shell deploy chain (composite CI action -> make -> scripts -> kubectl / helm
* of the copy's own manifests and chart), and Terraform helm_releases (one of
* a chart dir of the module, one of an external chart). An API
* group/kind is one identity across a repository: copies declaring the same
* one ARE joined (a CRD links to every package marking its group), so the
* fixtures' CRDs use groups no copy's Go code declares -- the guard measures
* accidental growth, not that by-design join. The container layer (Docker)
* has Dockerfiles with stage ENV, an ENTRYPOINT script and a compose project
* building two components of one repository; an image repository is one
* identity too, so every copy's builds of it join one Image -- that join
* must stay linear (the component split examines its edges). */
static const char *const CX_INFRA_DIRS[] = {"fx_Kubernetes", "fx_Go", "fx_Bash", "fx_HCL",
"fx_Dockerfile"};
enum { CX_INFRA_COUNT = sizeof(CX_INFRA_DIRS) / sizeof(CX_INFRA_DIRS[0]) };
typedef struct {
int nodes;
int edges;
int lang_nodes[CX_TEMPLATE_COUNT];
int lang_edges[CX_TEMPLATE_COUNT];
CxPassMs passes[CX_MAX_PASSES];
int pass_count;
uint64_t perfile_defs; /* Σ defs registered by per-file/overlay registry builds */
uint64_t build_files;
uint64_t filter_failed;
uint64_t tail_lookups;
uint64_t tail_candidates;
uint64_t fallback_rows;
uint64_t imp_nodes; /* symbols scored by the importance pass */
uint64_t imp_name_visits; /* same-name-group members visited by that pass */
uint64_t k8s_candidates; /* objects the K8s resolvers examined */
uint64_t op_candidates; /* Go types / dirs the operator pass examined */
uint64_t sh_candidates; /* paths / make rules the shell layer examined */
uint64_t tf_candidates; /* blocks the Terraform layer examined */
uint64_t dk_hub_edges; /* Image edges the component split examined */
int infra_nodes[CX_INFRA_COUNT];
int infra_edges[CX_INFRA_COUNT];
uint64_t py_probes;
uint64_t py_probe_visits;
int pyext_files;
int pyext_defs;
int pyext_controls;
uint64_t py_class_binds;
uint64_t py_class_visits;
uint64_t py_wildcards;
uint64_t py_wildcard_visits;
int pyrange_files;
int pyrange_defs;
int pyrange_classes;
int pyrange_peer_classes;
int pyrange_controls;
int errors;
double wall_s;
cbm_resolve_stats_snapshot_t resolver;
} CxMetrics;
/* Count the intended corpus, then prove each use() kept its exact local LSP
* call. Query errors are failures, never indistinguishable from zero rows. */
static bool cx_pyext_control(cbm_store_t *store, const char *project, const cbm_node_t *caller) {
if (!caller->qualified_name || !caller->file_path)
return false;
const char *dot = strrchr(caller->qualified_name, '.');
if (!dot || dot - caller->qualified_name > 1000)
return false;
char target_qn[1200];
int n = snprintf(target_qn, sizeof(target_qn), "%.*s.cx_pyext_definition_0",
(int)(dot - caller->qualified_name), caller->qualified_name);
if (n < 0 || (size_t)n >= sizeof(target_qn))
return false;
cbm_node_t target = {0};
cbm_edge_t *edges = NULL;
int count = 0;
bool found = false;
if (cbm_store_find_node_by_qn(store, project, target_qn, &target) == CBM_STORE_OK &&
target.label && strcmp(target.label, "Function") == 0 && target.file_path &&
strcmp(target.file_path, caller->file_path) == 0 &&
cbm_store_find_edges_by_source_type(store, caller->id, "CALLS", &edges, &count) ==
CBM_STORE_OK) {
for (int i = 0; i < count; i++) {
if (edges[i].target_id != target.id || !edges[i].properties_json)
continue;
/* Every admitted semantic edge carries the shared resolver proof rule. */
yyjson_doc *doc =
yyjson_read(edges[i].properties_json, strlen(edges[i].properties_json), 0);
const char *strategy =
doc ? yyjson_get_str(yyjson_obj_get(yyjson_doc_get_root(doc), "strategy")) : NULL;
found = found ||
(strategy && !strcmp(strategy, "shared_resolver") &&
yyjson_get_int(yyjson_obj_get(yyjson_doc_get_root(doc), "resolver_rule")) > 0);
yyjson_doc_free(doc);
}
}
cbm_store_free_edges(edges, count);
cbm_node_free_fields(&target);
return found;
}
static bool cx_pyext_population(cbm_store_t *store, const char *project, CxMetrics *out) {
cbm_node_t *nodes = NULL;
int count = 0;
bool ok = cbm_store_find_nodes_by_label(store, project, "File", &nodes, &count) == CBM_STORE_OK;
for (int i = 0; ok && i < count; i++) {
if (nodes[i].file_path && strncmp(nodes[i].file_path, "pyext/", 6) == 0)
out->pyext_files++;
}
cbm_store_free_nodes(nodes, count);
nodes = NULL;
count = 0;
ok = ok &&
cbm_store_find_nodes_by_label(store, project, "Function", &nodes, &count) == CBM_STORE_OK;
for (int i = 0; ok && i < count; i++) {
const cbm_node_t *node = &nodes[i];
if (!node->file_path || strncmp(node->file_path, "pyext/", 6) != 0 || !node->name)
continue;
static const char definition_prefix[] = "cx_pyext_definition_";
if (strncmp(node->name, definition_prefix, sizeof(definition_prefix) - 1) == 0)
out->pyext_defs++;
if (strcmp(node->name, "cx_pyext_use") == 0 && cx_pyext_control(store, project, node))
out->pyext_controls++;
}
cbm_store_free_nodes(nodes, count);
return ok;
}
/* Each package has many registered direct members; other packages are decoys.
* Wildcards precede definitions so the fixture retains explicit LSP positives.
* Exact invalidation-order negatives live in test_py_lsp.c. */
enum { CX_PYRANGE_FILES_PER_K = 20, CX_PYRANGE_DEFS = 40, CX_PYRANGE_WILDCARDS = 4 };
static int cx_build_pyrange(const char *root, int k) {
for (int m = 0; m < k; m++) {
for (int j = 0; j < CX_PYRANGE_FILES_PER_K; j++) {
char dir[1024], path[1200];
int dn = snprintf(dir, sizeof(dir), "%s/pyrange/mod%d/p%d", root, m, j);
if (dn < 0 || (size_t)dn >= sizeof(dir) || th_mkdir_p(dir) != 0)
return -1;
int pn = snprintf(path, sizeof(path), "%s/__init__.py", dir);
if (pn < 0 || (size_t)pn >= sizeof(path))
return -1;
FILE *f = fopen(path, "w");
if (!f)
return -1;
bool written = true;
for (int w = 0; written && w < CX_PYRANGE_WILDCARDS; w++)
written = fprintf(f, "from range_vendor_%d import *\n", w) >= 0;
int peer = (j + 1) % CX_PYRANGE_FILES_PER_K;
if (written)
written = fprintf(f, "from pyrange.mod%d.p%d import Peer_%d_%d\n\n", m, peer, m,
peer) >= 0;
for (int d = 0; written && d < CX_PYRANGE_DEFS; d++) {
written = fprintf(f,
"def cx_pyrange_definition_%d(x):\n return x + %d\n\n"
"class RangeClass_%d:\n def value(self):\n"
" return %d\n\n",
d, d, d, d) >= 0;
}
if (written)
written = fprintf(f,
"class Peer_%d_%d:\n def remote_%d_%d(self):\n"
" return %d\n\n"
"def cx_pyrange_use():\n obj = RangeClass_0()\n"
" peer = Peer_%d_%d()\n"
" return (cx_pyrange_definition_0(1) + obj.value()\n"
" + peer.remote_%d_%d())\n",
m, j, m, j, j, m, peer, m, peer) >= 0;
int closed = fclose(f);
if (!written || closed != 0)
return -1;
}
}
return 0;
}
static bool cx_pyrange_exact_control(cbm_store_t *store, const char *project,
const cbm_node_t *caller, const char *qn,
const char *target_file, const char *label) {
if (!caller->qualified_name || !caller->file_path || !caller->label ||
strcmp(caller->label, "Function") != 0)
return false;
cbm_node_t target = {0};
cbm_edge_t *edges = NULL;
int count = 0, exact = 0;
bool ok = cbm_store_find_node_by_qn(store, project, qn, &target) == CBM_STORE_OK &&
target.qualified_name && strcmp(target.qualified_name, qn) == 0 && target.label &&
strcmp(target.label, label) == 0 && target.file_path &&
strcmp(target.file_path, target_file) == 0 &&
cbm_store_find_edges_by_source_type(store, caller->id, "CALLS", &edges, &count) ==
CBM_STORE_OK;
for (int i = 0; ok && i < count; i++) {
if (edges[i].target_id != target.id)
continue;
yyjson_doc *doc =
edges[i].properties_json
? yyjson_read(edges[i].properties_json, strlen(edges[i].properties_json), 0)
: NULL;
/* Every admitted semantic edge carries the shared resolver proof rule. */
const char *strategy =
doc ? yyjson_get_str(yyjson_obj_get(yyjson_doc_get_root(doc), "strategy")) : NULL;
if (strategy && !strcmp(strategy, "shared_resolver") &&
yyjson_get_int(yyjson_obj_get(yyjson_doc_get_root(doc), "resolver_rule")) > 0)
exact++;
yyjson_doc_free(doc);
}
cbm_store_free_edges(edges, count);
cbm_node_free_fields(&target);
return ok && exact == 1;
}
/* A remote method has no declaration in the importer/raw file registry. Its
* source=lsp edge proves that cross-file registry evidence reached resolution. */
static bool cx_pyrange_controls(cbm_store_t *store, const char *project, const cbm_node_t *caller) {
if (!caller->qualified_name || !caller->file_path)
return false;
const char *dot = strrchr(caller->qualified_name, '.');
if (!dot || dot - caller->qualified_name > 1000)
return false;
char module[1200], qn[1400];
int n = snprintf(module, sizeof(module), "%.*s", (int)(dot - caller->qualified_name),
caller->qualified_name);
if (n < 0 || (size_t)n >= sizeof(module))
return false;
n = snprintf(qn, sizeof(qn), "%s.cx_pyrange_definition_0", module);
bool function =
n >= 0 && (size_t)n < sizeof(qn) &&
cx_pyrange_exact_control(store, project, caller, qn, caller->file_path, "Function");
n = snprintf(qn, sizeof(qn), "%s.RangeClass_0.value", module);
bool method = n >= 0 && (size_t)n < sizeof(qn) &&
cx_pyrange_exact_control(store, project, caller, qn, caller->file_path, "Method");
int m = -1, j = -1;
if (sscanf(caller->file_path, "pyrange/mod%d/p%d/__init__.py", &m, &j) != 2 || m < 0 || j < 0 ||
j >= CX_PYRANGE_FILES_PER_K)
return false;
char path[1200], slot_name[64];
n = snprintf(path, sizeof(path), "pyrange/mod%d/p%d/__init__.py", m, j);
if (n < 0 || (size_t)n >= sizeof(path) || strcmp(path, caller->file_path) != 0)
return false;
n = snprintf(slot_name, sizeof(slot_name), "p%d", j);
char *slot = strrchr(module, '.');
if (n < 0 || (size_t)n >= sizeof(slot_name) || !slot || strcmp(slot + 1, slot_name) != 0)
return false;
*slot = '\0';
int peer = (j + 1) % CX_PYRANGE_FILES_PER_K;
n = snprintf(qn, sizeof(qn), "%s.p%d.Peer_%d_%d.remote_%d_%d", module, peer, m, peer, m, peer);
int pn = snprintf(path, sizeof(path), "pyrange/mod%d/p%d/__init__.py", m, peer);
bool remote = n >= 0 && (size_t)n < sizeof(qn) && pn >= 0 && (size_t)pn < sizeof(path) &&
strcmp(path, caller->file_path) != 0 &&
cx_pyrange_exact_control(store, project, caller, qn, path, "Method");
return function && method && remote;
}
static bool cx_pyrange_population(cbm_store_t *store, const char *project, CxMetrics *out) {
static const char *const labels[] = {"File", "Function", "Class"};
bool ok = true;
for (int l = 0; l < 3; l++) {
cbm_node_t *nodes = NULL;
int count = 0;
bool queried = cbm_store_find_nodes_by_label(store, project, labels[l], &nodes, &count) ==
CBM_STORE_OK;
ok = queried && ok;
for (int i = 0; queried && i < count; i++) {
const cbm_node_t *node = &nodes[i];
if (!node->file_path || strncmp(node->file_path, "pyrange/", 8) != 0)
continue;
if (l == 0)
out->pyrange_files++;
if (l == 1 && node->name) {
static const char def_prefix[] = "cx_pyrange_definition_";
if (strncmp(node->name, def_prefix, sizeof(def_prefix) - 1) == 0)
out->pyrange_defs++;
if (strcmp(node->name, "cx_pyrange_use") == 0 &&
cx_pyrange_controls(store, project, node))
out->pyrange_controls++;
}
if (l == 2 && node->name && strncmp(node->name, "RangeClass_", 11) == 0)
out->pyrange_classes++;
if (l == 2 && node->name && strncmp(node->name, "Peer_", 5) == 0)
out->pyrange_peer_classes++;
}
cbm_store_free_nodes(nodes, count);
}
return ok;
}
static double cx_now_s(void) {
struct timespec ts;
cbm_profile_now(&ts);
return (double)ts.tv_sec + (double)ts.tv_nsec / 1e9;
}
static int cx_run(const char *root, const char *db_path, CxMetrics *out, bool pyext, bool pyrange) {
memset(out, 0, sizeof(*out));
uint64_t d0;
uint64_t b0;
uint64_t f0;
uint64_t x0;
cbm_pxc_filter_stats(&d0, &b0, &f0, &x0);
uint64_t tl0 = atomic_load_explicit(&g_lsp_tail_lookups, memory_order_relaxed);
uint64_t tc0 = atomic_load_explicit(&g_lsp_tail_candidates, memory_order_relaxed);
uint64_t fb0 = cbm_pp_lsp_linear_fallback_rows();
uint64_t in0 = atomic_load_explicit(&g_importance_nodes, memory_order_relaxed);
uint64_t iv0 = atomic_load_explicit(&g_importance_name_visits, memory_order_relaxed);
uint64_t kc0 = atomic_load_explicit(&g_k8s_candidates, memory_order_relaxed);
uint64_t oc0 = atomic_load_explicit(&g_op_candidates, memory_order_relaxed);
uint64_t sc0 = atomic_load_explicit(&g_sh_candidates, memory_order_relaxed);
uint64_t fc0 = atomic_load_explicit(&g_tf_candidates, memory_order_relaxed);
uint64_t dh0 = atomic_load_explicit(&g_dk_hub_edges, memory_order_relaxed);
uint64_t pp0 = atomic_load_explicit(&cbm_py_submodule_probes, memory_order_relaxed);
uint64_t pv0 = atomic_load_explicit(&cbm_py_submodule_probe_visits, memory_order_relaxed);
uint64_t pc0 = atomic_load_explicit(&cbm_py_module_class_binds, memory_order_relaxed);
uint64_t pcv0 = atomic_load_explicit(&cbm_py_module_class_bind_visits, memory_order_relaxed);
uint64_t pw0 = atomic_load_explicit(&cbm_py_wildcard_invalidations, memory_order_relaxed);
uint64_t pwv0 =
atomic_load_explicit(&cbm_py_wildcard_invalidation_visits, memory_order_relaxed);
atomic_store_explicit(&g_cx_pass_count, 0, memory_order_relaxed);
cbm_log_set_sink_ex(cx_pass_sink, CBM_LOG_SINK_TEE);
double t0 = cx_now_s();
cbm_pipeline_t *p = cbm_pipeline_new(root, db_path, CBM_MODE_FULL);
if (!p) {
return -1;
}
int rc = cbm_pipeline_run(p);
out->resolver = cbm_pipeline_resolver_stats(p);
out->wall_s = cx_now_s() - t0;
cbm_pipeline_get_file_errors(p, NULL, &out->errors);
cbm_log_set_sink(NULL);
int captured = atomic_load_explicit(&g_cx_pass_count, memory_order_relaxed);
out->pass_count = captured < CX_MAX_PASSES ? captured : CX_MAX_PASSES;
memcpy(out->passes, g_cx_passes, (size_t)out->pass_count * sizeof(CxPassMs));
char project[512];
snprintf(project, sizeof(project), "%s", cbm_pipeline_project_name(p));
cbm_pipeline_free(p);
if (rc != 0) {
return rc;
}
uint64_t d1;
uint64_t b1;
uint64_t f1;
uint64_t x1;
cbm_pxc_filter_stats(&d1, &b1, &f1, &x1);
out->perfile_defs = d1 - d0;
out->build_files = b1 - b0;
out->filter_failed = x1 - x0;
out->tail_lookups = atomic_load_explicit(&g_lsp_tail_lookups, memory_order_relaxed) - tl0;
out->tail_candidates = atomic_load_explicit(&g_lsp_tail_candidates, memory_order_relaxed) - tc0;
out->fallback_rows = cbm_pp_lsp_linear_fallback_rows() - fb0;
out->imp_nodes = atomic_load_explicit(&g_importance_nodes, memory_order_relaxed) - in0;
out->imp_name_visits =
atomic_load_explicit(&g_importance_name_visits, memory_order_relaxed) - iv0;
out->k8s_candidates = atomic_load_explicit(&g_k8s_candidates, memory_order_relaxed) - kc0;
out->op_candidates = atomic_load_explicit(&g_op_candidates, memory_order_relaxed) - oc0;
out->sh_candidates = atomic_load_explicit(&g_sh_candidates, memory_order_relaxed) - sc0;
out->tf_candidates = atomic_load_explicit(&g_tf_candidates, memory_order_relaxed) - fc0;
out->dk_hub_edges = atomic_load_explicit(&g_dk_hub_edges, memory_order_relaxed) - dh0;
out->py_probes = atomic_load_explicit(&cbm_py_submodule_probes, memory_order_relaxed) - pp0;
out->py_probe_visits =
atomic_load_explicit(&cbm_py_submodule_probe_visits, memory_order_relaxed) - pv0;
cbm_store_t *s = cbm_store_open_path(db_path);
if (!s) {
return -1;
}
out->nodes = cbm_store_count_nodes(s, project);
out->edges = cbm_store_count_edges(s, project);
for (int t = 0; t < CX_TEMPLATE_COUNT; t++) {
out->lang_nodes[t] = cbm_store_count_nodes_scoped(s, project, CX_TEMPLATES[t].dirname);
out->lang_edges[t] = cbm_store_count_edges_scoped(s, project, CX_TEMPLATES[t].dirname);
}
for (int i = 0; i < CX_INFRA_COUNT; i++) {
out->infra_nodes[i] = cbm_store_count_nodes_scoped(s, project, CX_INFRA_DIRS[i]);
out->infra_edges[i] = cbm_store_count_edges_scoped(s, project, CX_INFRA_DIRS[i]);
}
out->py_class_binds =
atomic_load_explicit(&cbm_py_module_class_binds, memory_order_relaxed) - pc0;
out->py_class_visits =
atomic_load_explicit(&cbm_py_module_class_bind_visits, memory_order_relaxed) - pcv0;
out->py_wildcards =
atomic_load_explicit(&cbm_py_wildcard_invalidations, memory_order_relaxed) - pw0;
out->py_wildcard_visits =
atomic_load_explicit(&cbm_py_wildcard_invalidation_visits, memory_order_relaxed) - pwv0;
bool extra_ok = (!pyext || cx_pyext_population(s, project, out)) &&
(!pyrange || cx_pyrange_population(s, project, out));
cbm_store_close(s);
return extra_ok ? 0 : -1;
}
static double cx_ratio(double num, double den) {
return den > 0.0 ? num / den : 0.0;
}
typedef int (*CxCorpusBuilder)(const char *root, int k);
static int cx_remove_corpus(const char *root) {
int rc = th_rmtree(root);
if (rc != 0) {
fprintf(stderr, " [complexity] WARN: could not remove temp corpus %s\n", root);
}
return rc;
}
/* Build one corpus shape at k and 2k in two fresh temp dirs, run the pipeline
* on both, and remove both dirs before returning — on success and on every
* failure path. Only the metrics outlive the call; the corpora used to stay
* behind in the temp root, four per suite run. Only dirs this call created
* are removed (the temp root is shared with concurrent runs). */
static int cx_measure(CxCorpusBuilder build, const char *prefix_a, const char *prefix_b,
CxMetrics *out_a, CxMetrics *out_b) {
const char *tmp = cbm_tmpdir();
char root_a[512];
char root_b[512];
int na = snprintf(root_a, sizeof(root_a), "%s/%s_XXXXXX", tmp, prefix_a);
int nb = snprintf(root_b, sizeof(root_b), "%s/%s_XXXXXX", tmp, prefix_b);
if (na < 0 || (size_t)na >= sizeof(root_a) || nb < 0 || (size_t)nb >= sizeof(root_b))
return -1;
bool made_a = cbm_mkdtemp(root_a) != NULL;
bool made_b = made_a && cbm_mkdtemp(root_b) != NULL;
int rc = -1;
if (made_b && build(root_a, CX_K_BASE) == 0 && build(root_b, CX_K_BASE * 2) == 0) {
char db_a[600];
char db_b[600];
int da = snprintf(db_a, sizeof(db_a), "%s/cx.db", root_a);
int db = snprintf(db_b, sizeof(db_b), "%s/cx.db", root_b);
bool paths = da >= 0 && (size_t)da < sizeof(db_a) && db >= 0 && (size_t)db < sizeof(db_b);
bool pyext = build == cx_build_pyext;
bool pyrange = build == cx_build_pyrange;
if (paths && cx_run(root_a, db_a, out_a, pyext, pyrange) == 0 &&
cx_run(root_b, db_b, out_b, pyext, pyrange) == 0) {
rc = 0;
}
}
if (made_b && cx_remove_corpus(root_b) != 0 && build == cx_build_pyrange)
rc = -1;
if (made_a && cx_remove_corpus(root_a) != 0 && build == cx_build_pyrange)
rc = -1;
return rc;
}
/* Shared across the suite so the report test reuses the measured pair instead
* of paying two more pipeline runs. */
static CxMetrics g_cx_base;
static CxMetrics g_cx_doubled;
static bool g_cx_measured = false;
static int cx_measure_pair(void) {
if (g_cx_measured) {
return 0;
}
if (cx_measure(cx_build_corpus, "cbm_cx_base", "cbm_cx_dbl", &g_cx_base, &g_cx_doubled) != 0) {
return -1;
}
g_cx_measured = true;
return 0;
}
static bool cx_resolver_linear(const CxMetrics *a, const CxMetrics *b) {
bool ok = true;
for (unsigned i = 0; i < CBM_RESOLVE_STAT_COUNT; i++) {
/* Only languages with root-level semantic facts walk syntax here; the
* flat-file test below gates this counter with a corpus that does. */
if (i == CBM_RESOLVE_STAT_SYNTAX_CHILD_STEPS || i == CBM_RESOLVE_STAT_SYNTAX_INDEX_NODES)
continue;
uint64_t first = a->resolver.values[i], second = b->resolver.values[i];
double ratio = cx_ratio((double)second, (double)first);
printf(" resolver_%s %llu -> %llu (ratio %.2f)\n",
cbm_resolve_stat_name((cbm_resolve_stat_t)i), (unsigned long long)first,
(unsigned long long)second, ratio);
/* Every category is exercised by source, including unknown-call
* invalidations. Removing a hook cannot produce a vacuous green. */
ok = ok && first > 0 && second > 0 && ratio >= CX_RATIO_LO && ratio <= CX_RATIO_HI;
}
return ok;
}
typedef struct {
_Atomic unsigned ready;
_Atomic bool release;
} CxResolverGate;
typedef struct {
cbm_resolve_stats_t stats;
uint64_t amount;
CxResolverGate *gate;
} CxResolverThread;
static void *cx_resolver_count_thread(void *opaque) {
CxResolverThread *thread = opaque;
cbm_resolve_stats_t *previous = cbm_resolve_stats_bind(&thread->stats);
atomic_fetch_add_explicit(&thread->gate->ready, 1, memory_order_release);
while (!atomic_load_explicit(&thread->gate->release, memory_order_acquire)) {
/* Both sinks must be installed before either thread records work. */
}
for (uint64_t i = 0; i < thread->amount; i++)
cbm_resolve_stats_add(CBM_RESOLVE_STAT_LOOKUP_PROBES, 1);
cbm_resolve_stats_bind(previous);
return NULL;
}
/* ── Tests ───────────────────────────────────────────────────────────── */
TEST(complexity_resolver_counters_are_run_scoped) {
cbm_resolve_stats_t owner = {0}, nested = {0};
CxResolverGate gate = {0};
CxResolverThread first = {.amount = 1000, .gate = &gate};
CxResolverThread second = {.amount = 2000, .gate = &gate};
cbm_thread_t a, b;
cbm_resolve_stats_t *previous = cbm_resolve_stats_bind(&owner);
cbm_resolve_stats_add(CBM_RESOLVE_STAT_LOOKUP_PROBES, 3);
cbm_resolve_stats_t *outer = cbm_resolve_stats_bind(&nested);
cbm_resolve_stats_add(CBM_RESOLVE_STAT_LOOKUP_PROBES, 11);
cbm_resolve_stats_bind(outer);
int created_a = cbm_thread_create(&a, 0, cx_resolver_count_thread, &first);
int created_b = cbm_thread_create(&b, 0, cx_resolver_count_thread, &second);
unsigned created = (unsigned)(created_a == 0) + (unsigned)(created_b == 0);
while (atomic_load_explicit(&gate.ready, memory_order_acquire) < created) {
/* A held gate makes isolation deterministic, independent of scheduling. */
}
cbm_resolve_stats_add(CBM_RESOLVE_STAT_LOOKUP_PROBES, 4);
atomic_store_explicit(&gate.release, true, memory_order_release);
int joined_a = created_a == 0 ? cbm_thread_join(&a) : -1;
int joined_b = created_b == 0 ? cbm_thread_join(&b) : -1;
cbm_resolve_stats_bind(previous);
ASSERT_EQ(created_a, 0);
ASSERT_EQ(created_b, 0);
ASSERT_EQ(joined_a, 0);
ASSERT_EQ(joined_b, 0);
ASSERT_EQ(cbm_resolve_stats_snapshot(&owner).values[CBM_RESOLVE_STAT_LOOKUP_PROBES], 7);
ASSERT_EQ(cbm_resolve_stats_snapshot(&nested).values[CBM_RESOLVE_STAT_LOOKUP_PROBES], 11);
ASSERT_EQ(cbm_resolve_stats_snapshot(&first.stats).values[CBM_RESOLVE_STAT_LOOKUP_PROBES],
1000);
ASSERT_EQ(cbm_resolve_stats_snapshot(&second.stats).values[CBM_RESOLVE_STAT_LOOKUP_PROBES],
2000);
cbm_resolve_stats_reset(&first.stats);
ASSERT_EQ(cbm_resolve_stats_snapshot(&first.stats).values[CBM_RESOLVE_STAT_LOOKUP_PROBES], 0);
ASSERT_EQ(cbm_resolve_stats_snapshot(&second.stats).values[CBM_RESOLVE_STAT_LOOKUP_PROBES],
2000);
PASS();
}
/* The flagship gate. Independent module copies ⇒ every extensive quantity must
* scale linearly in the copy count. A files×corpus coupling shows up as a
* ratio near 4 on the counter that sums per-file work. */
TEST(complexity_replicated_modules_scale_linearly) {
if (cx_measure_pair() != 0) {
FAIL("failed to build/run the complexity corpus pair");
}
const CxMetrics *a = &g_cx_base;
const CxMetrics *b = &g_cx_doubled;
ASSERT_TRUE(cx_resolver_linear(a, b));
/* Sanity: the corpus is real. */
ASSERT_GT(a->nodes, 50);
ASSERT_GT(a->edges, 20);
double node_r = cx_ratio(b->nodes, a->nodes);
double edge_r = cx_ratio(b->edges, a->edges);
printf(" nodes %d -> %d (ratio %.2f) edges %d -> %d (ratio %.2f)\n", a->nodes, b->nodes,
node_r, a->edges, b->edges, edge_r);
ASSERT_TRUE(node_r >= CX_RATIO_LO && node_r <= CX_RATIO_HI);
ASSERT_TRUE(edge_r >= CX_RATIO_LO && edge_r <= CX_RATIO_HI);
/* Per-language linearity, from the same run pair (scoped by subtree). If
* scoped counting returns 0 for the base run the scope semantics changed —
* surface that rather than silently skipping. */
for (int t = 0; t < CX_TEMPLATE_COUNT; t++) {
ASSERT_GT(a->lang_nodes[t], 0);
double lr = cx_ratio(b->lang_nodes[t], a->lang_nodes[t]);
printf(" %-8s nodes %d -> %d (ratio %.2f)\n", CX_TEMPLATES[t].dirname, a->lang_nodes[t],
b->lang_nodes[t], lr);
ASSERT_TRUE(lr >= CX_RATIO_LO && lr <= CX_RATIO_HI);
}
PASS();
}
/* The infra resolvers (K8s references / selectors / CRDs / chartRefs, the Go
* operator's controller kinds, CRD types and loaded charts, the shell layer's
* manifest dirs and make rules, Terraform's helm_release blocks) examine
* candidates by name. Independent
* copies share every name, so a candidate set that is not scoped to the
* bundle / package / module grows with the copy count: the examined count
* goes ~4x per doubling while the edges stay ~2x. Both are gated, on the
* fixture corpora's own subtrees, and neither may be vacuous. */
TEST(complexity_infra_resolution_is_linear) {
if (cx_measure_pair() != 0) {
FAIL("failed to build/run the complexity corpus pair");
}
const CxMetrics *a = &g_cx_base;
const CxMetrics *b = &g_cx_doubled;
printf(" k8s_candidates %llu -> %llu op_candidates %llu -> %llu\n",
(unsigned long long)a->k8s_candidates, (unsigned long long)b->k8s_candidates,
(unsigned long long)a->op_candidates, (unsigned long long)b->op_candidates);
ASSERT_GT((long long)a->k8s_candidates, 0);
ASSERT_GT((long long)a->op_candidates, 0);
double kr = cx_ratio((double)b->k8s_candidates, (double)a->k8s_candidates);
double orr = cx_ratio((double)b->op_candidates, (double)a->op_candidates);
printf(" k8s_candidates ratio %.2f op_candidates ratio %.2f\n", kr, orr);
ASSERT_TRUE(kr >= CX_RATIO_LO && kr <= CX_RATIO_HI);
ASSERT_TRUE(orr >= CX_RATIO_LO && orr <= CX_RATIO_HI);
printf(" sh_candidates %llu -> %llu tf_candidates %llu -> %llu\n",
(unsigned long long)a->sh_candidates, (unsigned long long)b->sh_candidates,
(unsigned long long)a->tf_candidates, (unsigned long long)b->tf_candidates);
ASSERT_GT((long long)a->sh_candidates, 0);
ASSERT_GT((long long)a->tf_candidates, 0);
double sr = cx_ratio((double)b->sh_candidates, (double)a->sh_candidates);
double fr = cx_ratio((double)b->tf_candidates, (double)a->tf_candidates);
printf(" sh_candidates ratio %.2f tf_candidates ratio %.2f\n", sr, fr);
ASSERT_TRUE(sr >= CX_RATIO_LO && sr <= CX_RATIO_HI);
ASSERT_TRUE(fr >= CX_RATIO_LO && fr <= CX_RATIO_HI);
printf(" dk_hub_edges %llu -> %llu\n", (unsigned long long)a->dk_hub_edges,
(unsigned long long)b->dk_hub_edges);
ASSERT_GT((long long)a->dk_hub_edges, 0);
double dr = cx_ratio((double)b->dk_hub_edges, (double)a->dk_hub_edges);
printf(" dk_hub_edges ratio %.2f\n", dr);
ASSERT_TRUE(dr >= CX_RATIO_LO && dr <= CX_RATIO_HI);
for (int i = 0; i < CX_INFRA_COUNT; i++) {
ASSERT_GT(a->infra_nodes[i], 0);
ASSERT_GT(a->infra_edges[i], 0);
double nr = cx_ratio(b->infra_nodes[i], a->infra_nodes[i]);
double er = cx_ratio(b->infra_edges[i], a->infra_edges[i]);
printf(" %-14s nodes %d -> %d (%.2f) edges %d -> %d (%.2f)\n", CX_INFRA_DIRS[i],
a->infra_nodes[i], b->infra_nodes[i], nr, a->infra_edges[i], b->infra_edges[i], er);
ASSERT_TRUE(nr >= CX_RATIO_LO && nr <= CX_RATIO_HI);
ASSERT_TRUE(er >= CX_RATIO_LO && er <= CX_RATIO_HI);
}
PASS();
}
/* Σ per-file registry work must be linear in independent copies. This is the
* counter that measured ~4x/doubling on the v0.10.x Java path (defs_per_file
* tracked defs_total — #1669). Guarded against vacuous passes: the base run
* must have produced real registry work, so an accidental zeroing of the
* counter fails loudly instead of green-washing the gate. */
TEST(complexity_perfile_registry_work_is_linear) {
if (cx_measure_pair() != 0) {
FAIL("failed to build/run the complexity corpus pair");
}
const CxMetrics *a = &g_cx_base;
const CxMetrics *b = &g_cx_doubled;
printf(" perfile_defs %llu -> %llu build_files %llu -> %llu filter_failed %llu\n",
(unsigned long long)a->perfile_defs, (unsigned long long)b->perfile_defs,
(unsigned long long)a->build_files, (unsigned long long)b->build_files,
(unsigned long long)(a->filter_failed + b->filter_failed));
/* Non-vacuous: the corpus includes languages that register per-file defs
* (Java at minimum). If this is ever 0 the counter wiring broke — that is
* a test defect to fix, not a pass. */
ASSERT_GT((long long)a->perfile_defs, (long long)CX_MIN_BASE_WORK);
double defs_r = cx_ratio((double)b->perfile_defs, (double)a->perfile_defs);
printf(" perfile_defs ratio %.2f (linear ~2, files x corpus ~4)\n", defs_r);
ASSERT_TRUE(defs_r <= CX_RATIO_HI);
/* Recorded, deliberately NOT gated (the O10 note): tail_candidates and
* fallback_rows are legitimately superlinear under replication TODAY —
* same-short-name candidate sets grow with the copy count by design of
* the current tail scan. Both measured ~1 ns/unit (#1669: indexing the
* scan away cut 242M visits for 0% wall). Gate them only after those
* scans are bounded; until then they are trend data for the report. */
printf(" [info] tail_lookups %llu -> %llu tail_candidates %llu -> %llu fallback_rows "
"%llu -> %llu\n",
(unsigned long long)a->tail_lookups, (unsigned long long)b->tail_lookups,
(unsigned long long)a->tail_candidates, (unsigned long long)b->tail_candidates,
(unsigned long long)a->fallback_rows, (unsigned long long)b->fallback_rows);
PASS();
}
static CxMetrics g_cx_big_base;
static CxMetrics g_cx_big_doubled;
static bool g_cx_big_measured = false;
static int cx_measure_bigpkg_pair(void) {
if (g_cx_big_measured) {
return 0;
}
if (cx_measure(cx_build_bigpkg, "cbm_cxbig_a", "cbm_cxbig_b", &g_cx_big_base,
&g_cx_big_doubled) != 0) {
return -1;
}
g_cx_big_measured = true;
return 0;
}
/* One growing package instead of independent modules. Nodes and edges must
* still be linear in file count — and so must Σ per-file registry work: a
* build scoped to the file's MODULE or NAMESPACE pays package-size per file
* and lands at ratio ~4 here. Only a per-FILE-scoped build stays at ~2. This
* is the exact #1669 growth pattern (per-file work tracking corpus share). */
TEST(complexity_shared_package_growth_stays_linear) {
if (cx_measure_bigpkg_pair() != 0) {
FAIL("failed to build/run the big-package corpus pair");
}
const CxMetrics *a = &g_cx_big_base;
const CxMetrics *b = &g_cx_big_doubled;
ASSERT_TRUE(cx_resolver_linear(a, b));
double node_r = cx_ratio(b->nodes, a->nodes);
double edge_r = cx_ratio(b->edges, a->edges);
printf(" nodes %d -> %d (ratio %.2f) edges %d -> %d (ratio %.2f)\n", a->nodes, b->nodes,
node_r, a->edges, b->edges, edge_r);
ASSERT_GT(a->nodes, 30);
ASSERT_TRUE(node_r >= CX_RATIO_LO && node_r <= CX_RATIO_HI);
ASSERT_TRUE(edge_r >= CX_RATIO_LO && edge_r <= CX_RATIO_HI);
printf(" perfile_defs %llu -> %llu (ratio %.2f; linear ~2, namespace/module-scoped ~4)\n",
(unsigned long long)a->perfile_defs, (unsigned long long)b->perfile_defs,
cx_ratio((double)b->perfile_defs, (double)a->perfile_defs));
/* Non-vacuous floor (false-guard audit): the package produces real
* registry work; zero means the counter wiring broke. */
ASSERT_GT((long long)a->perfile_defs, (long long)CX_MIN_BASE_WORK);
double defs_r = cx_ratio((double)b->perfile_defs, (double)a->perfile_defs);
ASSERT_TRUE(defs_r <= CX_RATIO_HI);
PASS();
}
TEST(complexity_py_submodule_probe_avoids_registry_scan) {
static const char *const keys[] = {"CBM_WORKERS", "CBM_INDEX_SINGLE_THREAD"};
char *saved[2] = {NULL, NULL};
bool ready = true;
for (int i = 0; i < 2; i++) {
const char *old = getenv(keys[i]);
saved[i] = old ? strdup(old) : NULL;
if (old && !saved[i])
ready = false;
}
CxMetrics a = {0}, b = {0};
int rc = -1, restored = 0;
if (ready) {
int configured = cbm_setenv(keys[0], "2", 1);
configured |= cbm_unsetenv(keys[1]);
if (configured == 0)
rc = cx_measure(cx_build_pyext, "cbm_cxpy_a", "cbm_cxpy_b", &a, &b);
for (int i = 0; i < 2; i++)
restored |= saved[i] ? cbm_setenv(keys[i], saved[i], 1) : cbm_unsetenv(keys[i]);
}
for (int i = 0; i < 2; i++)
free(saved[i]);
/* cx_measure removes both owned corpora before any assertion below. */
ASSERT_TRUE(ready);
ASSERT_EQ(restored, 0);
ASSERT_EQ(rc, 0);
int files = CX_K_BASE * CX_PYEXT_FILES_PER_MOD;
ASSERT_EQ(a.errors, 0);
ASSERT_EQ(b.errors, 0);
ASSERT_EQ(a.pyext_files, files);
ASSERT_EQ(b.pyext_files, files * 2);
ASSERT_EQ(a.pyext_defs, files * CX_PYEXT_DEFS_PER_FILE);
ASSERT_EQ(b.pyext_defs, files * 2 * CX_PYEXT_DEFS_PER_FILE);
ASSERT_EQ(a.pyext_controls, files);
ASSERT_EQ(b.pyext_controls, files * 2);
ASSERT_GT((long long)a.py_probes, (long long)CX_MIN_BASE_WORK);
ASSERT_GT((long long)b.py_probes, (long long)CX_MIN_BASE_WORK);
ASSERT_GT((long long)a.py_probe_visits, 0);
ASSERT_GT((long long)b.py_probe_visits, 0);
double probes_ratio = cx_ratio((double)b.py_probes, (double)a.py_probes);
double visits_ratio = cx_ratio((double)b.py_probe_visits, (double)a.py_probe_visits);
printf(" py_probes %llu -> %llu (%.2f), visits %llu -> %llu (%.2f)\n",
(unsigned long long)a.py_probes, (unsigned long long)b.py_probes, probes_ratio,
(unsigned long long)a.py_probe_visits, (unsigned long long)b.py_probe_visits,
visits_ratio);
ASSERT_TRUE(probes_ratio >= CX_RATIO_LO && probes_ratio <= CX_RATIO_HI);
ASSERT_TRUE(visits_ratio <= CX_RATIO_HI);
/* The direct registry test also enforces a logarithmic comparison bound,
* independently of fixed stdlib size and this corpus-growth ratio. */
PASS();
}
/* Throughput report — information only, never a gate (CI-determinism rule:
* rates depend on the machine and scheduler, so a threshold would be a
* lottery). Written locally under private/ (gitignored); CBM_COMPLEXITY_
* REPORT_DIR overrides. Skipped on starved legs where rates are meaningless. */
TEST(complexity_throughput_report_written) {
const char *skip_perf = getenv("CBM_SKIP_PERF");
if (skip_perf && skip_perf[0] == '1') {
/* Deliberate operator config, not a hidden environment failure
* (no-skips policy): rates measured under CBM_SKIP_PERF starvation
* would only mislead, so reporting is OFF and there is nothing left
* for this test to assert. */
fprintf(stderr, " [complexity] CBM_SKIP_PERF=1: throughput report disabled by config\n");
PASS();
}
if (cx_measure_pair() != 0) {
FAIL("failed to build/run the complexity corpus pair");
}
const char *dir = getenv("CBM_COMPLEXITY_REPORT_DIR");
if (!dir || !dir[0]) {
dir = "private/benchmarks";
}
if (th_mkdir_p(dir) != 0) {
FAIL("report dir not creatable (set CBM_COMPLEXITY_REPORT_DIR to a writable path)");
}
char path[1024];
snprintf(path, sizeof(path), "%s/complexity-%lld.json", dir, (long long)time(NULL));
FILE *f = fopen(path, "w");
ASSERT_NOT_NULL(f);
const CxMetrics *a = &g_cx_base;
const CxMetrics *b = &g_cx_doubled;
#if defined(__APPLE__)
const char *plat = "darwin";
#elif defined(_WIN32)
const char *plat = "windows";
#else
const char *plat = "linux";
#endif
#if defined(__aarch64__) || defined(_M_ARM64)
const char *arch = "arm64";
#else
const char *arch = "x86_64";
#endif
fprintf(f, "{\n \"schema\": 1,\n \"suite\": \"complexity\",\n");
fprintf(f, " \"timestamp\": %lld,\n \"platform\": \"%s\",\n \"arch\": \"%s\",\n",
(long long)time(NULL), plat, arch);
fprintf(f, " \"k_base\": %d,\n \"files_per_module\": %d,\n", CX_K_BASE, CX_FILES_PER_MOD);
fprintf(f, " \"runs\": [\n");
const CxMetrics *runs[2] = {a, b};
for (int i = 0; i < 2; i++) {
const CxMetrics *m = runs[i];
fprintf(f,
" {\"k\": %d, \"nodes\": %d, \"edges\": %d, \"wall_s\": %.3f,\n"
" \"nodes_per_s\": %.0f, \"edges_per_s\": %.0f,\n"
" \"perfile_defs\": %llu, \"tail_candidates\": %llu, \"fallback_rows\": "
"%llu, \"resolver\": {",
i == 0 ? CX_K_BASE : CX_K_BASE * 2, m->nodes, m->edges, m->wall_s,
m->wall_s > 0 ? (double)m->nodes / m->wall_s : 0.0,
m->wall_s > 0 ? (double)m->edges / m->wall_s : 0.0,
(unsigned long long)m->perfile_defs, (unsigned long long)m->tail_candidates,
(unsigned long long)m->fallback_rows);
for (unsigned event = 0; event < CBM_RESOLVE_STAT_COUNT; event++)
fprintf(f, "%s\"%s\": %llu", event ? ", " : "",
cbm_resolve_stat_name((cbm_resolve_stat_t)event),
(unsigned long long)m->resolver.values[event]);
fprintf(f, "}}%s\n", i == 0 ? "," : "");
}
fprintf(f, " ],\n");
fprintf(f, " \"languages\": [\n");
for (int t = 0; t < CX_TEMPLATE_COUNT; t++) {
fprintf(f,
" {\"name\": \"%s\", \"nodes\": [%d, %d], \"edges\": [%d, %d], "
"\"node_ratio\": %.3f}%s\n",
CX_TEMPLATES[t].dirname, a->lang_nodes[t], b->lang_nodes[t], a->lang_edges[t],
b->lang_edges[t], cx_ratio(b->lang_nodes[t], a->lang_nodes[t]),
t + 1 < CX_TEMPLATE_COUNT ? "," : "");
}
fprintf(f, " ],\n");
fprintf(f, " \"passes_ms\": {\n");
for (int i = 0; i < 2; i++) {
const CxMetrics *m = runs[i];
fprintf(f, " \"k%d\": {", i == 0 ? CX_K_BASE : CX_K_BASE * 2);
for (int j = 0; j < m->pass_count; j++) {
fprintf(f, "%s\"%s\": %ld", j == 0 ? "" : ", ", m->passes[j].name, m->passes[j].ms);
}
fprintf(f, "}%s\n", i == 0 ? "," : "");
}
fprintf(f, " },\n");
fprintf(f, " \"ratios\": {\"nodes\": %.3f, \"edges\": %.3f, \"perfile_defs\": %.3f},\n",
cx_ratio(b->nodes, a->nodes), cx_ratio(b->edges, a->edges),
cx_ratio((double)b->perfile_defs, (double)a->perfile_defs));
/* Languages without a provider, so the coverage boundary is explicit in
* the artifact rather than implied. */
fprintf(f, " \"skipped_languages\": [");
bool first = true;
for (int lang = 0; lang < CBM_LANG_COUNT; lang++) {
bool covered = false;
for (int t = 0; t < CX_TEMPLATE_COUNT; t++) {
if (CX_TEMPLATES[t].lang == (CBMLanguage)lang) {
covered = true;
}
}
char fixdir[512];
if (!covered && cx_fixture_dir_for((CBMLanguage)lang, fixdir, sizeof(fixdir))) {
covered = true;
}
if (!covered) {
const char *name = cbm_language_name((CBMLanguage)lang);
if (name && name[0]) {
fprintf(f, "%s\"%s\"", first ? "" : ", ", name);
first = false;
}
}
}
fprintf(f, "]\n}\n");
fclose(f);
printf(" report: %s\n", path);
PASS();
}
/* Importance scoring must stay linear in the graph. Its generic-name
* multiplier needs |{files a name is defined in}|; computing that per NODE
* instead of once per distinct NAME makes a same-name group of size k cost
* O(k^3) — measured on a large Java corpus as a multi-minute cost for a
* handful of names. Under replication the same-name groups grow with the copy
* count, so the per-node shape lands at ~4x per doubling here while the
* memoized shape stays at ~2x. Gated on the counter ratio, never on wall time
* (O9). This counter is GATED, not merely recorded: the whole point of the
* work is that this quantity must not go superlinear. */
TEST(complexity_importance_scoring_is_linear) {
if (cx_measure_pair() != 0) {
FAIL("failed to build/run the complexity corpus pair");
}
const CxMetrics *a = &g_cx_base;
const CxMetrics *b = &g_cx_doubled;
printf(" imp_nodes %llu -> %llu imp_name_visits %llu -> %llu\n",
(unsigned long long)a->imp_nodes, (unsigned long long)b->imp_nodes,
(unsigned long long)a->imp_name_visits, (unsigned long long)b->imp_name_visits);
/* Non-vacuous, twice over: the pass must have run at all (a miscounted
* PREDUMP_PASS_COUNT would silently skip it and zero both counters), and
* the base leg must have produced enough work for a ratio to mean
* anything. A zero here is a wiring defect to fix, never a pass. */
ASSERT_GT((long long)a->imp_nodes, 0);
ASSERT_GT((long long)a->imp_name_visits, (long long)CX_MIN_BASE_WORK);
double node_r = cx_ratio((double)b->imp_nodes, (double)a->imp_nodes);
double visit_r = cx_ratio((double)b->imp_name_visits, (double)a->imp_name_visits);
printf(" imp_nodes ratio %.2f imp_name_visits ratio %.2f (linear ~2, per-node ~4)\n",
node_r, visit_r);
ASSERT_TRUE(node_r >= CX_RATIO_LO && node_r <= CX_RATIO_HI);
ASSERT_TRUE(visit_r <= CX_RATIO_HI);
PASS();
}
/* ── Lexer work in error recovery (#2176) ──────────────────────────────
* tree-sitter lexes an unparseable stretch by retrying at every byte with
* every external token marked valid. The ReScript scanner then ran its
* template-string loop from each byte to the next '`', '$', '\\' or NUL — to
* the end of the file when there is none — and threw the result away. That
* is O(stretch) per byte, O(n^2) per file, all inside lexing where the parse
* budget's progress callback never runs; a binary Godot `.res` of high bytes
* (or plain text such as a run of '~') was dropped by the clock instead of
* parsed. Work is counted as the bytes the lexer pulls through a chunked
* TSInput: a pure function of (grammar, input), independent of speed. */
enum { CX_LEX_CHUNK = 64, CX_LEX_BASE_BYTES = 4096 };
typedef struct {
const char *src;
uint32_t len;
uint64_t bytes_pulled;
} CxLexInput;
static const char *cx_lex_read(void *payload, uint32_t byte_index, TSPoint position,
uint32_t *bytes_read) {
(void)position;
CxLexInput *in = (CxLexInput *)payload;
if (byte_index >= in->len) {
*bytes_read = 0;
return "";
}
uint32_t n = in->len - byte_index;
if (n > CX_LEX_CHUNK) {
n = CX_LEX_CHUNK;
}
in->bytes_pulled += n;
*bytes_read = n;
return in->src + byte_index;
}
/* Bytes pulled while parsing `len` copies of `fill` as ReScript; 0 on failure. */
static uint64_t cx_rescript_lex_work(unsigned char fill, uint32_t len) {
char *src = malloc(len);
TSParser *parser = ts_parser_new();
uint64_t work = 0;
if (src && parser && ts_parser_set_language(parser, cbm_ts_language(CBM_LANG_RESCRIPT))) {
memset(src, fill, len);
CxLexInput in = {src, len, 0};
TSInput input = {&in, cx_lex_read, TSInputEncodingUTF8, NULL};
TSTree *tree = ts_parser_parse(parser, NULL, input);
if (tree) {
work = in.bytes_pulled;
ts_tree_delete(tree);
}
}
if (parser) {
ts_parser_delete(parser);
}
free(src);
return work;
}
TEST(complexity_rescript_error_recovery_lexing_is_linear) {
/* 0xFF: the reporter's binary bytes (invalid UTF-8). '~': plain ASCII text
* that ReScript cannot parse either — the defect is not binary-only. */
static const unsigned char fills[] = {0xFF, '~'};
for (size_t i = 0; i < sizeof(fills); i++) {
uint64_t base = cx_rescript_lex_work(fills[i], CX_LEX_BASE_BYTES);
uint64_t doubled = cx_rescript_lex_work(fills[i], 2 * CX_LEX_BASE_BYTES);
double r = cx_ratio((double)doubled, (double)base);
printf(" fill 0x%02x: lexer bytes %llu -> %llu ratio %.2f (linear ~2, quadratic ~4)\n",
fills[i], (unsigned long long)base, (unsigned long long)doubled, r);
/* Non-vacuous: the lexer must at least have read the input once. */
ASSERT_GTE(base, (uint64_t)CX_LEX_BASE_BYTES);
ASSERT_TRUE(r >= CX_RATIO_LO && r <= CX_RATIO_HI);
}
PASS();
}
TEST(complexity_py_module_ranges_preserve_calls_with_bounded_work) {
static const char *const keys[] = {"CBM_WORKERS", "CBM_INDEX_SINGLE_THREAD"};
char *saved[2] = {NULL, NULL};
bool ready = true;
for (int i = 0; i < 2; i++) {
const char *old = getenv(keys[i]);
saved[i] = old ? strdup(old) : NULL;
if (old && !saved[i])
ready = false;
}
CxMetrics a = {0}, b = {0};
int rc = -1, restored = 0;
if (ready) {
int configured = cbm_setenv(keys[0], "2", 1);
configured |= cbm_unsetenv(keys[1]);
if (configured == 0)
rc = cx_measure(cx_build_pyrange, "cbm_cxrange_a", "cbm_cxrange_b", &a, &b);
for (int i = 0; i < 2; i++)
restored |= saved[i] ? cbm_setenv(keys[i], saved[i], 1) : cbm_unsetenv(keys[i]);
}
for (int i = 0; i < 2; i++)
free(saved[i]);
/* Both owned corpora are removed and environment restored before assertions. */
ASSERT_TRUE(ready);
ASSERT_EQ(restored, 0);
ASSERT_EQ(rc, 0);
const int files = CX_K_BASE * CX_PYRANGE_FILES_PER_K;
ASSERT_GT(files, 50);
ASSERT_EQ(a.errors, 0);
ASSERT_EQ(b.errors, 0);
ASSERT_EQ(a.pyrange_files, files);
ASSERT_EQ(b.pyrange_files, files * 2);
ASSERT_EQ(a.pyrange_defs, files * CX_PYRANGE_DEFS);
ASSERT_EQ(b.pyrange_defs, files * 2 * CX_PYRANGE_DEFS);
ASSERT_EQ(a.pyrange_classes, files * CX_PYRANGE_DEFS);
ASSERT_EQ(b.pyrange_classes, files * 2 * CX_PYRANGE_DEFS);
ASSERT_EQ(a.pyrange_peer_classes, files);
ASSERT_EQ(b.pyrange_peer_classes, files * 2);
ASSERT_EQ(a.pyrange_controls, files);
ASSERT_EQ(b.pyrange_controls, files * 2);
ASSERT_GTE(a.py_class_binds, (uint64_t)files);
ASSERT_GTE(b.py_class_binds, (uint64_t)(files * 2));
ASSERT_GTE(a.py_wildcards, (uint64_t)(files * CX_PYRANGE_WILDCARDS));
ASSERT_GTE(b.py_wildcards, (uint64_t)(files * 2 * CX_PYRANGE_WILDCARDS));
ASSERT_GT(a.py_class_visits, (uint64_t)0);
ASSERT_GT(b.py_class_visits, (uint64_t)0);
ASSERT_GT(a.py_wildcard_visits, (uint64_t)0);
ASSERT_GT(b.py_wildcard_visits, (uint64_t)0);
double classes = cx_ratio((double)b.py_class_binds, (double)a.py_class_binds);
double wildcards = cx_ratio((double)b.py_wildcards, (double)a.py_wildcards);
ASSERT_TRUE(classes >= CX_RATIO_LO && classes <= CX_RATIO_HI);
ASSERT_TRUE(wildcards >= CX_RATIO_LO && wildcards <= CX_RATIO_HI);
ASSERT_TRUE(cx_ratio((double)b.py_class_visits, (double)a.py_class_visits) <= CX_RATIO_HI);
ASSERT_TRUE(cx_ratio((double)b.py_wildcard_visits, (double)a.py_wildcard_visits) <=
CX_RATIO_HI);
/* These counters exclude sorting/string-length work. The direct registry
* tests separately pin absolute bounds and exact ordered answers. */
PASS();
}
/* One file whose top level grows, not more files. TypeScript's declaration
* baselines held the semantic pass for over 11 minutes on one such root, because
* fetching a child by index walks from the first child: the steps to enumerate
* n children were n*n/2. Doubling the file must only double them. */
enum { CX_FLAT_CLASSES_PER_K = 500 };
static int cx_build_flat_ts(const char *root, int k) {
char path[1200];
int pn = snprintf(path, sizeof(path), "%s/flat.ts", root);
if (pn < 0 || (size_t)pn >= sizeof(path))
return -1;
FILE *f = fopen(path, "w");
if (!f)
return -1;
bool written = true;
for (int i = 0; written && i < k * CX_FLAT_CLASSES_PER_K; i++)
written = fprintf(f, "export class Flat%d {}\n", i) >= 0;
return fclose(f) == 0 && written ? 0 : -1;
}
/* Java, Kotlin and C# bodies: each identifier asks whether it is a local. */
static int cx_build_long_java_body(const char *root, int k) {
char path[1200];
int pn = snprintf(path, sizeof(path), "%s/Body.java", root);
if (pn < 0 || (size_t)pn >= sizeof(path))
return -1;
FILE *f = fopen(path, "w");
if (!f)
return -1;
bool written = fprintf(f, "class Body { int f(int a) { return a; } void big() {\n") >= 0;
for (int i = 0; written && i < k * CX_FLAT_CLASSES_PER_K; i++)
written = fprintf(f, " int v%d = f(1);\n", i) >= 0;
written = written && fprintf(f, "} }\n") >= 0;
return fclose(f) == 0 && written ? 0 : -1;
}
TEST(complexity_oo_local_lookup_is_linear) {
CxMetrics a = {0}, b = {0};
ASSERT_EQ(cx_measure(cx_build_long_java_body, "cbm_cxbody_a", "cbm_cxbody_b", &a, &b), 0);
uint64_t first = a.resolver.values[CBM_RESOLVE_STAT_LOOKUP_PROBES];
uint64_t second = b.resolver.values[CBM_RESOLVE_STAT_LOOKUP_PROBES];
double ratio = cx_ratio((double)second, (double)first);
printf(" oo locals lookup_probes %llu -> %llu (ratio %.2f, linear ~2, quadratic ~4)\n",
(unsigned long long)first, (unsigned long long)second, ratio);
ASSERT_GTE(first, (uint64_t)(CX_K_BASE * CX_FLAT_CLASSES_PER_K));
ASSERT_TRUE(ratio >= CX_RATIO_LO && ratio <= CX_RATIO_HI);
PASS();
}
/* The same bodies asking which import a name means. */
static int cx_build_long_java_imports(const char *root, int k) {
char path[1200];
int pn = snprintf(path, sizeof(path), "%s/Imports.java", root);
if (pn < 0 || (size_t)pn >= sizeof(path))
return -1;
FILE *f = fopen(path, "w");
if (!f)
return -1;
int lines = k * CX_FLAT_CLASSES_PER_K;
bool written = true;
for (int i = 0; written && i < lines; i++)
written = fprintf(f, "import q.r%d.S%d;\n", i, i) >= 0;
written = written && fprintf(f, "class Imports { void big() {\n") >= 0;
for (int i = 0; written && i < lines; i++)
written = fprintf(f, " S%d.call();\n", i) >= 0;
written = written && fprintf(f, "} }\n") >= 0;
return fclose(f) == 0 && written ? 0 : -1;
}
TEST(complexity_oo_explicit_imports_are_linear) {
CxMetrics a = {0}, b = {0};
ASSERT_EQ(cx_measure(cx_build_long_java_imports, "cbm_cximp_a", "cbm_cximp_b", &a, &b), 0);
uint64_t first = a.resolver.values[CBM_RESOLVE_STAT_LOOKUP_PROBES];
uint64_t second = b.resolver.values[CBM_RESOLVE_STAT_LOOKUP_PROBES];
double ratio = cx_ratio((double)second, (double)first);
printf(" oo imports lookup_probes %llu -> %llu (ratio %.2f, linear ~2, quadratic ~4)\n",
(unsigned long long)first, (unsigned long long)second, ratio);
ASSERT_GTE(first, (uint64_t)(CX_K_BASE * CX_FLAT_CLASSES_PER_K));
ASSERT_TRUE(ratio >= CX_RATIO_LO && ratio <= CX_RATIO_HI);
PASS();
}
TEST(complexity_flat_file_syntax_walk_is_linear) {
CxMetrics a = {0}, b = {0};
ASSERT_EQ(cx_measure(cx_build_flat_ts, "cbm_cxflat_a", "cbm_cxflat_b", &a, &b), 0);
uint64_t first = a.resolver.values[CBM_RESOLVE_STAT_SYNTAX_CHILD_STEPS];
uint64_t second = b.resolver.values[CBM_RESOLVE_STAT_SYNTAX_CHILD_STEPS];
double ratio = cx_ratio((double)second, (double)first);
printf(" syntax_child_steps %llu -> %llu (ratio %.2f, linear ~2, quadratic ~4)\n",
(unsigned long long)first, (unsigned long long)second, ratio);
/* Non-vacuous: every class at the root was reached in both runs. */
ASSERT_GTE(first, (uint64_t)(CX_K_BASE * CX_FLAT_CLASSES_PER_K));
ASSERT_GTE(second, (uint64_t)(2 * CX_K_BASE * CX_FLAT_CLASSES_PER_K));
ASSERT_GTE(a.nodes, CX_K_BASE * CX_FLAT_CLASSES_PER_K);
ASSERT_TRUE(ratio >= CX_RATIO_LO && ratio <= CX_RATIO_HI);
PASS();
}
/* ── One file's source analysis ─────────────────────────────────────
* These run the binding index alone, in-process, so a shape can be doubled cheaply and
* each counter speaks for the analysis and nothing else. The plain mode asks tree-sitter
* the way this code used to and charges the sibling steps that really takes: a fixture
* that does not show about 4 there could not show a quadratic here either. */
enum { CX_BINDING_LINES = 1500, CX_BINDING_LINE_ROOM = 128, TARGET_FILES_SHORT = 12 };
typedef const char *(*CxLineFn)(char *out, size_t cap, int i);
/* The steps to reach a child: walking its siblings, or listing them once in an index. */
static uint64_t cx_stat_work(const cbm_resolve_stats_t *stats, cbm_resolve_stat_t stat) {
cbm_resolve_stats_snapshot_t seen = cbm_resolve_stats_snapshot(stats);
return seen.values[stat] + (stat == CBM_RESOLVE_STAT_SYNTAX_CHILD_STEPS
? seen.values[CBM_RESOLVE_STAT_SYNTAX_INDEX_NODES]
: 0);
}
static uint64_t cx_binding_work(CBMLanguage language, const char *path, const char *head,
CxLineFn line, const char *tail, int lines, bool plain,
cbm_resolve_stat_t stat) {
size_t room = strlen(head) + strlen(tail) + (size_t)lines * CX_BINDING_LINE_ROOM + 1;
char *source = malloc(room);
if (!source)
return 0;
size_t length = (size_t)snprintf(source, room, "%s", head);
for (int i = 0; i < lines; i++) {
char row[CX_BINDING_LINE_ROOM];
length += (size_t)snprintf(source + length, room - length, "%s", line(row, sizeof(row), i));
}
length += (size_t)snprintf(source + length, room - length, "%s", tail);
CBMFileResult *result =
cbm_extract_file(source, (int)length, language, "test", path, 0, NULL, NULL);
uint64_t work = 0;
if (result) {
CBMArena arena;
cbm_arena_init(&arena);
cbm_resolve_stats_t stats;
cbm_resolve_stats_reset(&stats);
cbm_resolve_stats_t *previous = cbm_resolve_stats_bind(&stats);
cbm_syntax_index_test_set_plain(plain);
cbm_resolve_binding_index_t *index =
cbm_resolve_binding_build(&arena, result, language, source, length);
cbm_syntax_index_test_set_plain(false);
cbm_resolve_stats_bind(previous);
if (index &&
!strcmp(cbm_resolve_binding_status(index), "binding_complete_supported_subset"))
work = cx_stat_work(&stats, stat);
cbm_arena_destroy(&arena);
cbm_free_result(result);
}
free(source);
return work;
}
static double cx_binding_ratio(CBMLanguage language, const char *path, const char *head,
CxLineFn line, const char *tail, bool plain, cbm_resolve_stat_t stat,
uint64_t *base) {
uint64_t first =
cx_binding_work(language, path, head, line, tail, CX_BINDING_LINES, plain, stat);
uint64_t second =
cx_binding_work(language, path, head, line, tail, CX_BINDING_LINES * 2, plain, stat);
double ratio = cx_ratio((double)second, (double)first);
printf(" %s%s %s %llu -> %llu (ratio %.2f)\n", path, plain ? " plain" : "",
cbm_resolve_stat_name(stat), (unsigned long long)first, (unsigned long long)second,
ratio);
*base = first;
return ratio;
}
/* Quadratic when asked plainly, linear through the syntax index. */
static bool cx_syntax_shape_binds(CBMLanguage language, const char *path, const char *head,
CxLineFn line, const char *tail) {
uint64_t base = 0;
double plain = cx_binding_ratio(language, path, head, line, tail, true,
CBM_RESOLVE_STAT_SYNTAX_CHILD_STEPS, &base);
if (base < CX_BINDING_LINES || plain < 3.2)
return false;
double indexed = cx_binding_ratio(language, path, head, line, tail, false,
CBM_RESOLVE_STAT_SYNTAX_CHILD_STEPS, &base);
return base >= CX_BINDING_LINES && indexed >= CX_RATIO_LO && indexed <= CX_RATIO_HI;
}
static const char *cx_line_py_statement(char *out, size_t cap, int i) {
snprintf(out, cap, "value_%d = make_%d(input_%d)\n", i, i, i);
return out;
}
static const char *cx_line_ts_statement(char *out, size_t cap, int i) {
snprintf(out, cap, "export const value_%d = make_%d(input_%d);\n", i, i, i);
return out;
}
static const char *cx_line_ts_member(char *out, size_t cap, int i) {
snprintf(out, cap, " member_%d: helper_%d,\n", i, i);
return out;
}
static const char *cx_line_py_member(char *out, size_t cap, int i) {
snprintf(out, cap, " value_%d = make_%d(input_%d)\n", i, i, i);
return out;
}
/* A module's width: every occurrence asks for its parent. */
TEST(complexity_binding_wide_module_questions_are_linear) {
ASSERT_TRUE(cx_syntax_shape_binds(CBM_LANG_PYTHON, "flat.py", "", cx_line_py_statement, ""));
ASSERT_TRUE(
cx_syntax_shape_binds(CBM_LANG_TYPESCRIPT, "flat.ts", "", cx_line_ts_statement, ""));
PASS();
}
/* A wide node that is not the root: an object literal's members and a function's body. */
TEST(complexity_binding_wide_inner_nodes_are_linear) {
ASSERT_TRUE(cx_syntax_shape_binds(CBM_LANG_TYPESCRIPT, "object.ts", "export const table = {\n",
cx_line_ts_member, "};\n"));
ASSERT_TRUE(
cx_syntax_shape_binds(CBM_LANG_PYTHON, "body.py", "def body():\n", cx_line_py_member, ""));
PASS();
}
/* An answer is exactly what tree-sitter says, including at the edges of the tree. */
TEST(complexity_syntax_index_answers_as_tree_sitter_does) {
static const char source[] =
"def f(a, b=1):\n return [a, (b), {'k': a}]\n\nclass C:\n x = 1\n";
TSParser *parser = ts_parser_new();
ASSERT_NOT_NULL(parser);
ASSERT_TRUE(ts_parser_set_language(parser, cbm_ts_language(CBM_LANG_PYTHON)));
TSTree *tree = ts_parser_parse_string(parser, NULL, source, (uint32_t)strlen(source));
ts_parser_delete(parser);
ASSERT_NOT_NULL(tree);
cbm_syntax_index_t index;
cbm_syntax_index_begin(&index, tree);
TSTreeCursor cursor = ts_tree_cursor_new(ts_tree_root_node(tree));
size_t visited = 0;
bool same = true;
for (bool more = true; more;) {
TSNode node = ts_tree_cursor_current_node(&cursor);
visited++;
same = same && ts_node_eq(cbm_syntax_parent(node), ts_node_parent(node));
uint32_t count = ts_node_child_count(node);
for (uint32_t i = 0; i <= count; i++)
same = same && ts_node_eq(cbm_syntax_child(node, i), ts_node_child(node, i));
for (uint32_t i = 0; i < count; i++) {
TSNode before = i ? ts_node_child(node, i - 1) : (TSNode){0};
TSNode child = ts_node_child(node, i);
same = same && ts_node_eq(cbm_syntax_previous_child(child), before);
cbm_syntax_index_test_set_plain(true);
same = same && ts_node_eq(cbm_syntax_previous_child(child), before);
cbm_syntax_index_test_set_plain(false);
}
uint32_t named = ts_node_named_child_count(node);
for (uint32_t i = 0; i <= named; i++)
same =
same && ts_node_eq(cbm_syntax_named_child(node, i), ts_node_named_child(node, i));
if (ts_tree_cursor_goto_first_child(&cursor))
continue;
while (!ts_tree_cursor_goto_next_sibling(&cursor)) {
if (!ts_tree_cursor_goto_parent(&cursor)) {
more = false;
break;
}
}
}
ts_tree_cursor_delete(&cursor);
cbm_syntax_index_end(&index);
ts_tree_delete(tree);
ASSERT_TRUE(same);
ASSERT_GTE(visited, (size_t)20);
PASS();
}
/* Past the width where children are listed: every position, and one past the end. */
TEST(complexity_syntax_index_wide_lists_match_tree_sitter) {
enum { MEMBERS = 200 };
char *source = malloc((size_t)MEMBERS * 32 + 64);
ASSERT_NOT_NULL(source);
size_t length = (size_t)snprintf(source, 64, "values = [\n");
for (int i = 0; i < MEMBERS; i++)
length += (size_t)snprintf(source + length, 32, " item_%d, # note\n", i);
length += (size_t)snprintf(source + length, 8, "]\n");
TSParser *parser = ts_parser_new();
ASSERT_NOT_NULL(parser);
ASSERT_TRUE(ts_parser_set_language(parser, cbm_ts_language(CBM_LANG_PYTHON)));
TSTree *tree = ts_parser_parse_string(parser, NULL, source, (uint32_t)length);
ts_parser_delete(parser);
ASSERT_NOT_NULL(tree);
TSNode list = ts_node_named_descendant_for_byte_range(ts_tree_root_node(tree), 9, 10);
ASSERT_TRUE(!strcmp(ts_node_type(list), "list"));
ASSERT_GT(ts_node_child_count(list), (uint32_t)MEMBERS);
cbm_syntax_index_t index;
cbm_syntax_index_begin(&index, tree);
bool same = true;
for (uint32_t i = 0; i <= ts_node_child_count(list); i++)
same = same && ts_node_eq(cbm_syntax_child(list, i), ts_node_child(list, i));
for (uint32_t i = 0; i <= ts_node_named_child_count(list); i++)
same = same && ts_node_eq(cbm_syntax_named_child(list, i), ts_node_named_child(list, i));
cbm_syntax_index_end(&index);
ts_tree_delete(tree);
free(source);
ASSERT_TRUE(same);
PASS();
}
/* Each helper is a declaration and a definition spelled alike, so a join on the name
* alone visits every pair. Generated files repeat names exactly like this. */
static const char *cx_line_js_same_named(char *out, size_t cap, int i) {
snprintf(out, cap, "export function outer_%d() { function helper() { return %d; } }\n", i, i);
return out;
}
TEST(complexity_js_value_join_is_linear_in_a_repeated_name) {
uint64_t base = 0;
double ratio = cx_binding_ratio(CBM_LANG_TYPESCRIPT, "same.ts", "", cx_line_js_same_named, "",
false, CBM_RESOLVE_STAT_CANDIDATE_VISITS, &base);
ASSERT_GTE(base, (uint64_t)CX_BINDING_LINES);
ASSERT_TRUE(ratio >= CX_RATIO_LO && ratio <= CX_RATIO_HI);
PASS();
}
/* Each dotted import asks whether its package is bound yet. */
static const char *cx_line_py_dotted_import(char *out, size_t cap, int i) {
snprintf(out, cap, "import package_%d.part\n", i);
return out;
}
TEST(complexity_python_dotted_imports_are_linear) {
uint64_t base = 0;
double ratio = cx_binding_ratio(CBM_LANG_PYTHON, "imports.py", "", cx_line_py_dotted_import, "",
false, CBM_RESOLVE_STAT_LOOKUP_PROBES, &base);
ASSERT_GTE(base, (uint64_t)CX_BINDING_LINES);
ASSERT_TRUE(ratio >= CX_RATIO_LO && ratio <= CX_RATIO_HI);
PASS();
}
/* Each decorator's root is joined to the file's imports. */
static const char *cx_line_py_decorated(char *out, size_t cap, int i) {
snprintf(out, cap, "from package_%d import deco_%d\n@deco_%d\ndef run_%d():\n return 1\n", i,
i, i, i);
return out;
}
TEST(complexity_python_import_join_is_linear) {
uint64_t base = 0;
double ratio = cx_binding_ratio(CBM_LANG_PYTHON, "decorated.py", "", cx_line_py_decorated, "",
false, CBM_RESOLVE_STAT_LOOKUP_PROBES, &base);
ASSERT_GTE(base, (uint64_t)CX_BINDING_LINES);
ASSERT_TRUE(ratio >= CX_RATIO_LO && ratio <= CX_RATIO_HI);
PASS();
}
/* Extraction runs a language's own pass over the file, so these shapes count that. */
static uint64_t cx_extract_work(CBMLanguage language, const char *path, const char *head,
CxLineFn line, const char *tail, int lines, bool plain,
cbm_resolve_stat_t stat) {
size_t room = strlen(head) + strlen(tail) + (size_t)lines * CX_BINDING_LINE_ROOM + 1;
char *source = malloc(room);
if (!source)
return 0;
size_t length = (size_t)snprintf(source, room, "%s", head);
for (int i = 0; i < lines; i++) {
char row[CX_BINDING_LINE_ROOM];
length += (size_t)snprintf(source + length, room - length, "%s", line(row, sizeof(row), i));
}
length += (size_t)snprintf(source + length, room - length, "%s", tail);
cbm_resolve_stats_t stats;
cbm_resolve_stats_reset(&stats);
cbm_resolve_stats_t *previous = cbm_resolve_stats_bind(&stats);
cbm_syntax_index_test_set_plain(plain);
CBMFileResult *result =
cbm_extract_file(source, (int)length, language, "test", path, 0, NULL, NULL);
cbm_syntax_index_test_set_plain(false);
cbm_resolve_stats_bind(previous);
uint64_t work = result ? cx_stat_work(&stats, stat) : 0;
if (result)
cbm_free_result(result);
free(source);
return work;
}
static double cx_extract_ratio(CBMLanguage language, const char *path, const char *head,
CxLineFn line, const char *tail, bool plain, cbm_resolve_stat_t stat,
uint64_t *base) {
uint64_t first =
cx_extract_work(language, path, head, line, tail, CX_BINDING_LINES, plain, stat);
uint64_t second =
cx_extract_work(language, path, head, line, tail, CX_BINDING_LINES * 2, plain, stat);
double ratio = cx_ratio((double)second, (double)first);
printf(" %s%s %s %llu -> %llu (ratio %.2f)\n", path, plain ? " plain" : "",
cbm_resolve_stat_name(stat), (unsigned long long)first, (unsigned long long)second,
ratio);
*base = first;
return ratio;
}
/* What the language's own pass compared while searching its registry and scopes. */
static uint64_t cx_extract_lsp_steps(CBMLanguage language, const char *path, const char *head,
CxLineFn line, const char *tail, int lines) {
size_t room = strlen(head) + strlen(tail) + (size_t)lines * CX_BINDING_LINE_ROOM + 1;
char *source = malloc(room);
if (!source)
return 0;
size_t length = (size_t)snprintf(source, room, "%s", head);
for (int i = 0; i < lines; i++) {
char row[CX_BINDING_LINE_ROOM];
length += (size_t)snprintf(source + length, room - length, "%s", line(row, sizeof(row), i));
}
length += (size_t)snprintf(source + length, room - length, "%s", tail);
(void)cbm_lsp_work_take();
CBMFileResult *result =
cbm_extract_file(source, (int)length, language, "test", path, 0, NULL, NULL);
uint64_t work = cbm_lsp_work_take();
if (result)
cbm_free_result(result);
free(source);
return work;
}
/* Before a JavaScript file's registry was indexed, its own pass searched it from the top
* for every function and every identifier in the body it inferred a return type from. */
static const char *cx_line_js_function_pair(char *out, size_t cap, int i) {
snprintf(out, cap, "function f_%d(a) { return g_%d(a); }\nfunction g_%d(b) { return b; }\n", i,
i, i);
return out;
}
static const char *cx_line_kotlin_function_pair(char *out, size_t cap, int i) {
snprintf(out, cap, "fun f_%d(a: Int): Int = g_%d(a)\nfun g_%d(b: Int): Int = b\n", i, i, i);
return out;
}
/* The same with Rust's registration passes, one patch per function and per method. */
static const char *cx_line_rust_function_pair(char *out, size_t cap, int i) {
snprintf(out, cap, "pub fn f_%d(a: i32) -> i32 { g_%d(a) }\nfn g_%d(b: i32) -> i32 { b }\n", i,
i, i);
return out;
}
static bool cx_pass_steps_are_linear(CBMLanguage language, const char *path, const char *head,
CxLineFn line) {
uint64_t first = cx_extract_lsp_steps(language, path, head, line, "", CX_BINDING_LINES);
uint64_t second = cx_extract_lsp_steps(language, path, head, line, "", CX_BINDING_LINES * 2);
double ratio = cx_ratio((double)second, (double)first);
printf(" %s lsp steps %llu -> %llu (ratio %.2f)\n", path, (unsigned long long)first,
(unsigned long long)second, ratio);
return first >= CX_BINDING_LINES && ratio >= CX_RATIO_LO && ratio <= CX_RATIO_HI;
}
TEST(complexity_js_pass_registry_lookups_are_linear) {
ASSERT_TRUE(cx_pass_steps_are_linear(CBM_LANG_JAVASCRIPT, "pairs.js", "export {};\n",
cx_line_js_function_pair));
PASS();
}
TEST(complexity_rust_pass_registration_is_linear) {
ASSERT_TRUE(
cx_pass_steps_are_linear(CBM_LANG_RUST, "pairs.rs", "", cx_line_rust_function_pair));
PASS();
}
static const char *cx_line_go_type_and_method(char *out, size_t cap, int i) {
snprintf(out, cap, "type T%d struct{ A int }\nfunc (t T%d) M() int { return t.A }\n", i, i);
return out;
}
TEST(complexity_go_pass_registration_is_linear) {
ASSERT_TRUE(cx_pass_steps_are_linear(CBM_LANG_GO, "types.go", "package p\n",
cx_line_go_type_and_method));
PASS();
}
/* A type name no import explains looked through every registered type. */
static const char *cx_line_java_unknown_type(char *out, size_t cap, int i) {
snprintf(out, cap, "class A%d { Unknown%d field; Unknown%d get() { return field; } }\n", i, i,
i);
return out;
}
TEST(complexity_java_short_name_fallback_is_linear) {
ASSERT_TRUE(
cx_pass_steps_are_linear(CBM_LANG_JAVA, "Types.java", "", cx_line_java_unknown_type));
PASS();
}
/* Each C# method and class patched the first registration of its name after a scan. */
static const char *cx_line_csharp_class(char *out, size_t cap, int i) {
snprintf(out, cap, "class C%d { public int X; int F(int a) { return a; } }\n", i);
return out;
}
static const char *cx_line_csharp_methods(char *out, size_t cap, int i) {
snprintf(out, cap,
"class C%d { public int X; int F(int a) { return a; } int G() { return 1; } "
"int H() { return 1; } }\n",
i);
return out;
}
/* Every definition does linear work too, so it takes this many before the scans show. */
TEST(complexity_csharp_pass_registration_is_linear) {
enum { LINES = 3000 };
uint64_t first =
cx_extract_lsp_steps(CBM_LANG_CSHARP, "methods.cs", "", cx_line_csharp_methods, "", LINES);
uint64_t second = cx_extract_lsp_steps(CBM_LANG_CSHARP, "methods.cs", "",
cx_line_csharp_methods, "", LINES * 2);
double ratio = cx_ratio((double)second, (double)first);
printf(" methods.cs lsp steps %llu -> %llu (ratio %.2f)\n", (unsigned long long)first,
(unsigned long long)second, ratio);
ASSERT_GTE(first, (uint64_t)LINES);
ASSERT_TRUE(ratio <= CX_RATIO_HI);
PASS();
}
/* A file of classes: each one's registration looked at all that were registered before. */
static const char *cx_line_kotlin_class(char *out, size_t cap, int i) {
snprintf(out, cap, "class C%d { val x: Int = 1\n fun f(a: Int): Int = a }\n", i);
return out;
}
TEST(complexity_kotlin_class_registration_is_linear) {
ASSERT_TRUE(cx_pass_steps_are_linear(CBM_LANG_KOTLIN, "classes.kt", "package p\n",
cx_line_kotlin_class));
PASS();
}
static const char *cx_line_java_class(char *out, size_t cap, int i) {
snprintf(out, cap, "class C%d extends B { int f; void m(int a) { } }\n", i);
return out;
}
TEST(complexity_java_class_registration_is_linear) {
ASSERT_TRUE(cx_pass_steps_are_linear(CBM_LANG_JAVA, "Classes.java", "", cx_line_java_class));
PASS();
}
static const char *cx_line_ts_class(char *out, size_t cap, int i) {
snprintf(out, cap, "export class C%d { x: number = 1; m(a: number): number { return a; } }\n",
i);
return out;
}
/* TypeScript's checker.ts names thousands of imports and makes a hundred thousand calls: the proof
* looked through the imports for each callee and each annotation, and through all the narrowings of
* a path for each receiver on it. */
static const char *cx_line_ts_imported_call(char *out, size_t cap, int i) {
snprintf(out, cap, "import { f%d } from './m';\nf%d().m();\n", i, i);
return out;
}
static const char *cx_line_ts_imported_annotation(char *out, size_t cap, int i) {
snprintf(out, cap, "import { t%d } from './m';\nlet a%d: C[] = [];\na%d[0].m();\n", i, i, i);
return out;
}
/* A narrowing covers its whole function, so the search for the one covering a receiver only
* gets long across functions narrowing the same name. */
static const char *cx_line_ts_narrowing(char *out, size_t cap, int i) {
snprintf(out, cap, "function f_%d(x: C) { if (x instanceof C) { x.m(); } }\n", i);
return out;
}
TEST(complexity_ts_proof_callee_imports_are_linear) {
ASSERT_TRUE(cx_pass_steps_are_linear(CBM_LANG_TYPESCRIPT, "calls.ts", "class C { m() {} }\n",
cx_line_ts_imported_call));
PASS();
}
TEST(complexity_ts_proof_annotation_imports_are_linear) {
ASSERT_TRUE(cx_pass_steps_are_linear(CBM_LANG_TYPESCRIPT, "typed.ts", "class C { m() {} }\n",
cx_line_ts_imported_annotation));
PASS();
}
/* ── A file that never parses ───────────────────────────────────────
* Declarations left open end up side by side under one error node, so every question
* about a child is a question about a very wide parent. A 990 KB Kotlin file of these
* lines held an index run for 50 minutes. Extraction has its index; a language's cross
* pass parses again and needs its own. No index open means no work counted at all, so
* the floor on the first count fails then too. */
static const char *cx_line_kotlin_unclosed(char *out, size_t cap, int i) {
(void)out, (void)cap, (void)i;
return "object A {\n";
}
static const char *cx_line_c_unclosed(char *out, size_t cap, int i) {
(void)out, (void)cap, (void)i;
return "struct A {\n";
}
static const char *cx_line_cpp_unclosed(char *out, size_t cap, int i) {
(void)out, (void)cap, (void)i;
return "namespace a {\n";
}
static const char *cx_line_js_unclosed(char *out, size_t cap, int i) {
(void)out, (void)cap, (void)i;
return "function a() {\n";
}
static const char *cx_line_dart_unclosed(char *out, size_t cap, int i) {
(void)out, (void)cap, (void)i;
return "class A {\n";
}
typedef void (*CxCrossFn)(CBMArena *arena, const char *source, int length,
CBMResolvedCallArray *out);
static void cx_cross_kotlin(CBMArena *arena, const char *source, int length,
CBMResolvedCallArray *out) {
cbm_run_kotlin_lsp_cross(arena, source, length, "test.m", NULL, 0, NULL, NULL, 0, NULL, out);
}
static void cx_cross_c(CBMArena *arena, const char *source, int length, CBMResolvedCallArray *out) {
(void)cbm_run_c_lsp_cross(arena, source, length, "test.m", false, NULL, 0, NULL, NULL, 0, NULL,
out);
}
static void cx_cross_cpp(CBMArena *arena, const char *source, int length,
CBMResolvedCallArray *out) {
(void)cbm_run_c_lsp_cross(arena, source, length, "test.m", true, NULL, 0, NULL, NULL, 0, NULL,
out);
}
static void cx_cross_c_shared(CBMArena *arena, const char *source, int length,
CBMResolvedCallArray *out) {
CBMTypeRegistry registry;
cbm_registry_init(&registry, arena);
cbm_registry_finalize(&registry);
(void)cbm_run_c_lsp_cross_with_registry_status(arena, source, length, "test.m", false,
&registry, NULL, NULL, 0, NULL, out);
}
static uint64_t cx_cross_work(CxCrossFn run, CxLineFn line, int lines) {
size_t room = (size_t)lines * CX_BINDING_LINE_ROOM + 1;
char *source = malloc(room);
if (!source)
return 0;
size_t length = 0;
for (int i = 0; i < lines; i++) {
char row[CX_BINDING_LINE_ROOM];
length += (size_t)snprintf(source + length, room - length, "%s", line(row, sizeof(row), i));
}
CBMArena arena;
cbm_arena_init(&arena);
CBMResolvedCallArray out;
memset(&out, 0, sizeof(out));
cbm_resolve_stats_t stats;
cbm_resolve_stats_reset(&stats);
cbm_resolve_stats_t *previous = cbm_resolve_stats_bind(&stats);
run(&arena, source, (int)length, &out);
cbm_resolve_stats_bind(previous);
uint64_t work = cx_stat_work(&stats, CBM_RESOLVE_STAT_SYNTAX_CHILD_STEPS);
cbm_arena_destroy(&arena);
free(source);
return work;
}
static bool cx_cross_is_linear(CxCrossFn run, CxLineFn line, const char *name) {
uint64_t first = cx_cross_work(run, line, CX_BINDING_LINES);
uint64_t second = cx_cross_work(run, line, CX_BINDING_LINES * 2);
double ratio = cx_ratio((double)second, (double)first);
printf(" %s cross steps %llu -> %llu (ratio %.2f)\n", name, (unsigned long long)first,
(unsigned long long)second, ratio);
return first >= CX_BINDING_LINES && ratio >= CX_RATIO_LO && ratio <= CX_RATIO_HI;
}
static const char *cx_line_rust_unclosed(char *out, size_t cap, int i) {
(void)out, (void)cap, (void)i;
return "mod a {\n";
}
static void cx_cross_rust_facts(CBMArena *arena, const char *source, int length,
CBMResolvedCallArray *out) {
(void)out;
(void)cbm_rust_collect_source_facts(arena, source, length, "test.m", NULL, 0);
}
TEST(complexity_rust_source_facts_on_unclosed_nesting_are_linear) {
ASSERT_TRUE(cx_cross_is_linear(cx_cross_rust_facts, cx_line_rust_unclosed, "unclosed.rs"));
PASS();
}
TEST(complexity_kotlin_cross_on_unclosed_nesting_is_linear) {
ASSERT_TRUE(cx_cross_is_linear(cx_cross_kotlin, cx_line_kotlin_unclosed, "unclosed.kt"));
PASS();
}
TEST(complexity_c_cross_on_unclosed_nesting_is_linear) {
ASSERT_TRUE(cx_cross_is_linear(cx_cross_c, cx_line_c_unclosed, "unclosed.c"));
PASS();
}
TEST(complexity_cpp_cross_on_unclosed_nesting_is_linear) {
ASSERT_TRUE(cx_cross_is_linear(cx_cross_cpp, cx_line_cpp_unclosed, "unclosed.cpp"));
PASS();
}
TEST(complexity_c_shared_cross_on_unclosed_nesting_is_linear) {
ASSERT_TRUE(cx_cross_is_linear(cx_cross_c_shared, cx_line_c_unclosed, "shared.c"));
PASS();
}
TEST(complexity_js_proof_on_unclosed_nesting_is_linear) {
uint64_t base = 0;
double ratio = cx_extract_ratio(CBM_LANG_JAVASCRIPT, "unclosed.js", "", cx_line_js_unclosed, "",
false, CBM_RESOLVE_STAT_SYNTAX_CHILD_STEPS, &base);
ASSERT_GTE(base, (uint64_t)CX_BINDING_LINES);
ASSERT_TRUE(ratio >= CX_RATIO_LO && ratio <= CX_RATIO_HI);
PASS();
}
TEST(complexity_dart_usages_on_unclosed_nesting_is_linear) {
/* The walk's own cursor answers, so nothing need be counted at all. */
uint64_t base = 0;
(void)cx_extract_ratio(CBM_LANG_DART, "unclosed.dart", "", cx_line_dart_unclosed, "", false,
CBM_RESOLVE_STAT_SYNTAX_CHILD_STEPS, &base);
uint64_t twice =
cx_extract_work(CBM_LANG_DART, "unclosed.dart", "", cx_line_dart_unclosed, "",
CX_BINDING_LINES * 2, false, CBM_RESOLVE_STAT_SYNTAX_CHILD_STEPS);
ASSERT_TRUE((double)twice <= CX_RATIO_HI * (double)(base + CX_BINDING_LINES));
PASS();
}
/* The same declarations closed: now the depth is real. Each identifier's usage
* classifiers climb towards the root, and Kotlin's grammar gives them little to stop at. */
enum { CX_NEST_DEPTH = 400 };
static uint64_t cx_usage_climb_work(int depth) {
static const char open[] = "object A {\n", close[] = "}\n";
size_t room = (size_t)depth * (sizeof(open) + sizeof(close)) + 1;
char *source = malloc(room);
if (!source)
return 0;
size_t length = 0;
for (int i = 0; i < depth; i++)
length += (size_t)snprintf(source + length, room - length, "%s", open);
for (int i = 0; i < depth; i++)
length += (size_t)snprintf(source + length, room - length, "%s", close);
cbm_usage_field_lookup_test_reset();
CBMFileResult *result =
cbm_extract_file(source, (int)length, CBM_LANG_KOTLIN, "test", "nest.kt", 0, NULL, NULL);
uint64_t work = cbm_usage_field_lookup_test_work();
if (result)
cbm_free_result(result);
free(source);
return work;
}
TEST(complexity_usage_classifiers_on_deep_nesting_are_linear) {
uint64_t first = cx_usage_climb_work(CX_NEST_DEPTH);
uint64_t second = cx_usage_climb_work(CX_NEST_DEPTH * 2);
double ratio = cx_ratio((double)second, (double)first);
printf(" nest.kt usage climb %llu -> %llu (ratio %.2f)\n", (unsigned long long)first,
(unsigned long long)second, ratio);
ASSERT_GTE(first, (uint64_t)CX_NEST_DEPTH);
ASSERT_TRUE(ratio >= CX_RATIO_LO && ratio <= CX_RATIO_HI);
PASS();
}
/* A definition's decorators start from the sibling before it. */
static const char *cx_line_kotlin_object(char *out, size_t cap, int i) {
snprintf(out, cap, "object A_%d { }\n", i);
return out;
}
TEST(complexity_decorator_lookup_is_linear) {
uint64_t base = 0;
double ratio = cx_extract_ratio(CBM_LANG_KOTLIN, "objects.kt", "", cx_line_kotlin_object, "",
false, CBM_RESOLVE_STAT_SYNTAX_CHILD_STEPS, &base);
ASSERT_GTE(base, (uint64_t)CX_BINDING_LINES);
ASSERT_TRUE(ratio >= CX_RATIO_LO && ratio <= CX_RATIO_HI);
PASS();
}
TEST(complexity_ts_proof_narrowing_lookup_is_linear) {
static const char head[] = "class C { m() {} }\n";
uint64_t first = cx_extract_lsp_steps(CBM_LANG_TYPESCRIPT, "narrow.ts", head,
cx_line_ts_narrowing, "", CX_BINDING_LINES);
uint64_t second = cx_extract_lsp_steps(CBM_LANG_TYPESCRIPT, "narrow.ts", head,
cx_line_ts_narrowing, "", CX_BINDING_LINES * 2);
double ratio = cx_ratio((double)second, (double)first);
printf(" narrow.ts lsp steps %llu -> %llu (ratio %.2f)\n", (unsigned long long)first,
(unsigned long long)second, ratio);
ASSERT_GTE(first, (uint64_t)CX_BINDING_LINES);
ASSERT_TRUE(ratio >= CX_RATIO_LO && ratio <= CX_RATIO_HI);
PASS();
}
TEST(complexity_ts_class_registration_is_linear) {
ASSERT_TRUE(cx_pass_steps_are_linear(CBM_LANG_TYPESCRIPT, "classes.ts", "", cx_line_ts_class));
PASS();
}
static const char *cx_line_rust_derive(char *out, size_t cap, int i) {
snprintf(out, cap, "#[derive(Clone, Debug)]\npub struct S%d { pub a: i32 }\n", i);
return out;
}
TEST(complexity_rust_derive_registration_is_linear) {
ASSERT_TRUE(cx_pass_steps_are_linear(CBM_LANG_RUST, "derive.rs", "", cx_line_rust_derive));
PASS();
}
static const char *cx_line_go_generic(char *out, size_t cap, int i) {
snprintf(out, cap, "func F%d[T any](a T) T { return a }\n", i);
return out;
}
TEST(complexity_go_generic_registration_is_linear) {
ASSERT_TRUE(
cx_pass_steps_are_linear(CBM_LANG_GO, "generic.go", "package p\n", cx_line_go_generic));
PASS();
}
/* Each C# class looked through the file's top level for a file-scoped namespace. */
TEST(complexity_csharp_namespace_lookup_is_linear) {
uint64_t base = 0;
double ratio = cx_extract_ratio(CBM_LANG_CSHARP, "classes.cs", "", cx_line_csharp_class, "",
false, CBM_RESOLVE_STAT_SYNTAX_CHILD_STEPS, &base);
ASSERT_GTE(base, (uint64_t)CX_BINDING_LINES);
ASSERT_TRUE(ratio >= CX_RATIO_LO && ratio <= CX_RATIO_HI);
PASS();
}
/* The tail index must answer what the scan answers, duplicates and later indexes included. */
static int cx_type_row(const CBMTypeRegistry *reg, const char *name) {
const CBMRegisteredType *found = cbm_registry_lookup_type(reg, name);
return found ? (int)(found - reg->types) : -1;
}
static int cx_func_row(const CBMTypeRegistry *reg, const char *name) {
const CBMRegisteredFunc *found = cbm_registry_lookup_func(reg, name);
return found ? (int)(found - reg->funcs) : -1;
}
TEST(complexity_registry_tail_answers_as_a_scan_does) {
enum { ROWS = 600, NAMES = 250, INDEX_AT = 200, PROBE_STEP = 37 };
CBMArena arena, tail_arena, idx_arena;
cbm_arena_init(&arena);
cbm_arena_init(&tail_arena);
cbm_arena_init(&idx_arena);
CBMTypeRegistry with, without;
cbm_registry_init(&with, &arena);
cbm_registry_init(&without, &arena);
with.tail_arena = &tail_arena;
bool memo_used = false;
for (int i = 0; i < ROWS; i++) {
char name[CX_BINDING_LINE_ROOM];
snprintf(name, sizeof(name), "t%d", i % NAMES);
const char *qn = cbm_arena_strdup(&arena, name);
CBMRegisteredType type = {.qualified_name = qn, .short_name = qn};
CBMRegisteredFunc func = {.qualified_name = qn, .short_name = qn};
cbm_registry_add_type(&with, type);
cbm_registry_add_type(&without, type);
cbm_registry_add_func(&with, func);
cbm_registry_add_func(&without, func);
if (i == INDEX_AT) {
cbm_registry_finalize_into(&with, &idx_arena);
cbm_registry_finalize_into(&without, &idx_arena);
}
memo_used = memo_used || with.type_tail.synced > with.type_tail.start;
for (int probe = 0; probe < NAMES + PROBE_STEP; probe += PROBE_STEP) {
char ask[CX_BINDING_LINE_ROOM];
snprintf(ask, sizeof(ask), "t%d", probe);
ASSERT_EQ(cx_type_row(&with, ask), cx_type_row(&without, ask));
ASSERT_EQ(cx_func_row(&with, ask), cx_func_row(&without, ask));
const CBMRegisteredType *own_with = cbm_registry_first_own_type(&with, ask);
const CBMRegisteredType *own_without = cbm_registry_first_own_type(&without, ask);
ASSERT_EQ(own_with ? (int)(own_with - with.types) : -1,
own_without ? (int)(own_without - without.types) : -1);
}
}
ASSERT_TRUE(memo_used);
cbm_arena_destroy(&idx_arena);
cbm_arena_destroy(&tail_arena);
cbm_arena_destroy(&arena);
PASS();
}
TEST(complexity_kotlin_pass_registry_lookups_are_linear) {
ASSERT_TRUE(cx_pass_steps_are_linear(CBM_LANG_KOTLIN, "pairs.kt", "package p\n",
cx_line_kotlin_function_pair));
PASS();
}
/* Each call in a body asks whether its name is a local, and the answer walked them all. */
static const char *cx_line_rust_let(char *out, size_t cap, int i) {
snprintf(out, cap, " let v_%d = f(1);\n", i);
return out;
}
static const char *cx_line_rust_inline_mod(char *out, size_t cap, int i) {
snprintf(out, cap, "pub mod m_%d { pub fn f() -> i32 { 1 } }\nfn g_%d() -> i32 { m_%d::f() }\n",
i, i, i);
return out;
}
static uint64_t cx_rust_flow_probes(const char *head, CxLineFn line, const char *tail, int lines) {
static const char manifest[] = "[package]\nname = \"p\"\nversion = \"0.1.0\"\n";
size_t room = strlen(head) + strlen(tail) + (size_t)lines * CX_BINDING_LINE_ROOM + 1;
char *source = malloc(room);
if (!source)
return 0;
size_t length = (size_t)snprintf(source, room, "%s", head);
for (int i = 0; i < lines; i++) {
char row[CX_BINDING_LINE_ROOM];
length += (size_t)snprintf(source + length, room - length, "%s", line(row, sizeof(row), i));
}
length += (size_t)snprintf(source + length, room - length, "%s", tail);
CBMFileResult *result =
cbm_extract_file(source, (int)length, CBM_LANG_RUST, "p", "src/lib.rs", 0, NULL, NULL);
TSParser *parser = ts_parser_new();
TSTree *tree = NULL;
if (parser && ts_parser_set_language(parser, cbm_ts_language(CBM_LANG_RUST)))
tree = ts_parser_parse_string(parser, NULL, source, (uint32_t)length);
if (parser)
ts_parser_delete(parser);
cbm_rust_project_t *project = cbm_rust_project_new();
uint64_t work = 0;
if (result && tree && project &&
cbm_rust_project_add_manifest(project, "Cargo.toml", manifest, strlen(manifest)) &&
cbm_rust_project_add_file(project, "src/lib.rs", result, source, length,
ts_tree_root_node(tree)) &&
cbm_rust_project_seal(project)) {
CBMArena arena;
cbm_arena_init(&arena);
cbm_resolve_stats_t stats;
cbm_resolve_stats_reset(&stats);
cbm_resolve_stats_t *previous = cbm_resolve_stats_bind(&stats);
cbm_rust_file_analysis_t *analysis = cbm_rust_analyze_file(
project, &arena, "src/lib.rs", source, length, ts_tree_root_node(tree));
cbm_resolve_stats_bind(previous);
if (analysis)
work = cbm_resolve_stats_snapshot(&stats).values[CBM_RESOLVE_STAT_LOOKUP_PROBES];
cbm_arena_destroy(&arena);
}
cbm_rust_project_free(project);
if (tree)
ts_tree_delete(tree);
if (result)
cbm_free_result(result);
free(source);
return work;
}
static bool cx_rust_flow_is_linear(const char *what, const char *head, CxLineFn line,
const char *tail) {
uint64_t first = cx_rust_flow_probes(head, line, tail, CX_BINDING_LINES);
uint64_t second = cx_rust_flow_probes(head, line, tail, CX_BINDING_LINES * 2);
double ratio = cx_ratio((double)second, (double)first);
printf(" rust %s probes %llu -> %llu (ratio %.2f)\n", what, (unsigned long long)first,
(unsigned long long)second, ratio);
return first >= CX_BINDING_LINES && ratio >= CX_RATIO_LO && ratio <= CX_RATIO_HI;
}
TEST(complexity_rust_local_lookup_is_linear) {
ASSERT_TRUE(cx_rust_flow_is_linear("locals", "fn f(a: i32) -> i32 { a }\nfn big() {\n",
cx_line_rust_let, "}\n"));
PASS();
}
/* A crate root's modules were a list, walked per path and per inline module. */
/* Every method on a std type looked through all the project's impls on outside types. */
static const char *cx_line_rust_ext_impl(char *out, size_t cap, int i) {
snprintf(out, cap,
"trait T%d{fn m%d(&self);}\nimpl T%d for String{fn m%d(&self){}}\n"
"fn g%d(s:String){s.m%d();}\n",
i, i, i, i, i, i);
return out;
}
TEST(complexity_rust_outside_impls_are_linear) {
ASSERT_TRUE(cx_rust_flow_is_linear("outside impls", "", cx_line_rust_ext_impl, ""));
PASS();
}
/* Each impl walked looked itself up in its module's list. */
static const char *cx_line_rust_impl(char *out, size_t cap, int i) {
snprintf(out, cap, "pub struct S%d;\nimpl S%d { pub fn m(&self) -> i32 { 1 } }\n", i, i);
return out;
}
TEST(complexity_rust_impl_lookup_is_linear) {
ASSERT_TRUE(cx_rust_flow_is_linear("impls", "", cx_line_rust_impl, ""));
PASS();
}
/* A module's uses, walked for each local item found (a conditional use of its name), for each
* imported name, and for each trait a method call asks about. */
static const char *cx_line_rust_use_and_fn(char *out, size_t cap, int i) {
snprintf(out, cap, "use a::b%d;\nfn f%d() { f%d(); }\n", i, i, i);
return out;
}
static const char *cx_line_rust_use_and_call(char *out, size_t cap, int i) {
snprintf(out, cap, "use a::b%d;\nfn f%d() { b%d(); }\n", i, i, i);
return out;
}
static const char *cx_line_rust_use_of_trait(char *out, size_t cap, int i) {
snprintf(out, cap,
"mod b%d{pub trait T%d{fn m%d(&self);}impl T%d for u8{fn m%d(&self){}}}\n"
"use b%d::T%d;\nfn f%d(s:u8){s.m%d();}\n",
i, i, i, i, i, i, i, i, i);
return out;
}
TEST(complexity_rust_conditional_use_lookup_is_linear) {
ASSERT_TRUE(cx_rust_flow_is_linear("uses beside items", "", cx_line_rust_use_and_fn, ""));
PASS();
}
TEST(complexity_rust_named_use_lookup_is_linear) {
ASSERT_TRUE(cx_rust_flow_is_linear("uses named", "", cx_line_rust_use_and_call, ""));
PASS();
}
TEST(complexity_rust_trait_in_scope_is_linear) {
ASSERT_TRUE(cx_rust_flow_is_linear("uses of traits", "", cx_line_rust_use_of_trait, ""));
PASS();
}
TEST(complexity_rust_module_children_are_linear) {
ASSERT_TRUE(cx_rust_flow_is_linear("children", "", cx_line_rust_inline_mod, ""));
PASS();
}
/* Imports, then a use of each: every use used to scan them. */
typedef struct {
CBMLanguage language;
const char *path, *head, *imported, *middle, *used, *tail;
} CxImportShape;
static const CxImportShape CX_IMPORT_SHAPES[] = {
{CBM_LANG_JAVASCRIPT, "uses.js", "", "import { n_%d } from './m_%d.js';\n", "", "n_%d();\n",
""},
{CBM_LANG_TYPESCRIPT, "uses.ts", "", "import { n_%d } from './m_%d';\n", "", "n_%d();\n", ""},
{CBM_LANG_JAVA, "Uses.java", "", "import p.q%d.R%d;\n", "public class Uses { void f() {\n",
" R%d.call();\n", "} }\n"},
{CBM_LANG_KOTLIN, "uses.kt", "package p\n", "import q.r%d.S%d\n", "fun f() {\n",
" S%d.call()\n", "}\n"},
{CBM_LANG_PHP, "uses.php", "<?php\nnamespace App;\n", "use Lib\\A%d;\n", "function f() {\n",
" A%d::go();\n", "}\n"},
{CBM_LANG_RUST, "uses.rs", "", "use crate::m%d::f%d;\n", "fn run() {\n", " f%d();\n", "}\n"},
{CBM_LANG_GO, "uses.go", "package p\nimport (\n", "\tq%d \"example.com/q%d\"\n",
")\nfunc F() {\n", "\tq%d.G()\n", "}\n"},
};
static uint64_t cx_import_shape_steps(const CxImportShape *shape, int lines) {
size_t room = (size_t)lines * CX_BINDING_LINE_ROOM * 2 + CX_BINDING_LINE_ROOM * 2;
char *source = malloc(room);
if (!source)
return 0;
size_t length = (size_t)snprintf(source, room, "%s", shape->head);
for (int i = 0; i < lines; i++)
length += (size_t)snprintf(source + length, room - length, shape->imported, i, i);
length += (size_t)snprintf(source + length, room - length, "%s", shape->middle);
for (int i = 0; i < lines; i++)
length += (size_t)snprintf(source + length, room - length, shape->used, i, i);
length += (size_t)snprintf(source + length, room - length, "%s", shape->tail);
(void)cbm_lsp_work_take();
CBMFileResult *result =
cbm_extract_file(source, (int)length, shape->language, "test", shape->path, 0, NULL, NULL);
uint64_t work = cbm_lsp_work_take();
if (result)
cbm_free_result(result);
free(source);
return work;
}
TEST(complexity_import_uses_are_linear_in_every_language_pass) {
bool all = true;
for (size_t i = 0; i < sizeof(CX_IMPORT_SHAPES) / sizeof(CX_IMPORT_SHAPES[0]); i++) {
const CxImportShape *shape = &CX_IMPORT_SHAPES[i];
uint64_t first = cx_import_shape_steps(shape, CX_BINDING_LINES);
uint64_t second = cx_import_shape_steps(shape, CX_BINDING_LINES * 2);
double ratio = cx_ratio((double)second, (double)first);
printf(" %s lsp steps %llu -> %llu (ratio %.2f)\n", shape->path,
(unsigned long long)first, (unsigned long long)second, ratio);
all = all && first >= CX_BINDING_LINES && ratio >= CX_RATIO_LO && ratio <= CX_RATIO_HI;
}
ASSERT_TRUE(all);
PASS();
}
/* Which `import a.b` rows carry their root's fact. A root's first dotted row does, unless a
* row imports that root plainly. */
extern int cbm_resolver_test_python_root_facts(const CBMFileResult *result);
static const char *cx_line_py_root_import(char *out, size_t cap, int i) {
snprintf(out, cap, "import pkg_%d.part\n", i);
return out;
}
static uint64_t cx_python_root_probes(const char *source, int *facts) {
CBMFileResult *result = cbm_extract_file(source, (int)strlen(source), CBM_LANG_PYTHON, "test",
"roots.py", 0, NULL, NULL);
if (!result)
return 0;
cbm_resolve_stats_t stats;
cbm_resolve_stats_reset(&stats);
cbm_resolve_stats_t *previous = cbm_resolve_stats_bind(&stats);
*facts = cbm_resolver_test_python_root_facts(result);
cbm_resolve_stats_bind(previous);
cbm_free_result(result);
return cbm_resolve_stats_snapshot(&stats).values[CBM_RESOLVE_STAT_LOOKUP_PROBES];
}
static uint64_t cx_python_root_shape(int lines, int *facts) {
size_t room = (size_t)lines * CX_BINDING_LINE_ROOM + 1;
char *source = malloc(room);
if (!source)
return 0;
size_t length = 0;
source[0] = '\0';
for (int i = 0; i < lines; i++) {
char row[CX_BINDING_LINE_ROOM];
length += (size_t)snprintf(source + length, room - length, "%s",
cx_line_py_root_import(row, sizeof(row), i));
}
uint64_t work = cx_python_root_probes(source, facts);
free(source);
return work;
}
TEST(complexity_python_root_facts_are_linear) {
int first_facts = 0, second_facts = 0;
uint64_t first = cx_python_root_shape(CX_BINDING_LINES, &first_facts);
uint64_t second = cx_python_root_shape(CX_BINDING_LINES * 2, &second_facts);
double ratio = cx_ratio((double)second, (double)first);
printf(" python root facts probes %llu -> %llu (ratio %.2f)\n", (unsigned long long)first,
(unsigned long long)second, ratio);
ASSERT_EQ(first_facts, CX_BINDING_LINES);
ASSERT_EQ(second_facts, CX_BINDING_LINES * 2);
ASSERT_GTE(first, (uint64_t)CX_BINDING_LINES);
ASSERT_TRUE(ratio >= CX_RATIO_LO && ratio <= CX_RATIO_HI);
/* The rule, row by row: only a's first row counts, and b has a plain row. */
int facts = -1;
(void)cx_python_root_probes("import a.x\nimport a.y\nimport b.x\nimport b\nimport c.d.e\n",
&facts);
ASSERT_EQ(facts, 2);
(void)cx_python_root_probes("import b\nimport b.x\n", &facts);
ASSERT_EQ(facts, 0);
PASS();
}
/* Python's builtins are declared by every Python file, so each new file asked whether it
* was on the list of each of them, and each call to a builtin could ask again. */
extern bool cbm_resolver_test_target_files(int count);
static uint64_t cx_target_file_probes(int count, bool *answers) {
cbm_resolve_stats_t stats;
cbm_resolve_stats_reset(&stats);
cbm_resolve_stats_t *previous = cbm_resolve_stats_bind(&stats);
*answers = cbm_resolver_test_target_files(count);
cbm_resolve_stats_bind(previous);
return cbm_resolve_stats_snapshot(&stats).values[CBM_RESOLVE_STAT_LOOKUP_PROBES];
}
TEST(complexity_target_file_list_is_near_linear) {
bool first_ok = false, second_ok = false;
uint64_t first = cx_target_file_probes(CX_BINDING_LINES, &first_ok);
uint64_t second = cx_target_file_probes(CX_BINDING_LINES * 2, &second_ok);
double ratio = cx_ratio((double)second, (double)first);
printf(" target file probes %llu -> %llu (ratio %.2f)\n", (unsigned long long)first,
(unsigned long long)second, ratio);
ASSERT_TRUE(first_ok);
ASSERT_TRUE(second_ok);
ASSERT_GTE(first, (uint64_t)CX_BINDING_LINES);
ASSERT_TRUE(ratio >= CX_RATIO_LO && ratio <= CX_RATIO_HI);
/* Short lists are walked, and answer the same. */
uint64_t small_probes = cx_target_file_probes(TARGET_FILES_SHORT, &first_ok);
ASSERT_TRUE(first_ok);
ASSERT_GTE(small_probes, (uint64_t)TARGET_FILES_SHORT);
PASS();
}
/* The table says the same whichever way a name is found. */
TEST(complexity_name_rows_answer_as_a_scan_does) {
enum { COUNT = 64 };
const char *names[COUNT];
char storage[COUNT][8];
for (int i = 0; i < COUNT; i++) {
snprintf(storage[i], sizeof(storage[i]), "n%d", i % 7);
names[i] = i % 11 == 5 ? NULL : storage[i];
}
CBMArena arena;
cbm_arena_init(&arena);
CBMNameRows rows = {0};
bool same = true;
/* Both short of the threshold and past it; the table may grow between questions. */
for (int count = 1; count <= COUNT; count++) {
for (int v = 0; v < 8; v++) {
char name[8];
snprintf(name, sizeof(name), "n%d", v);
int expected = -1;
for (int i = 0; i < count && expected < 0; i++)
if (names[i] && !strcmp(names[i], name))
expected = i;
int got = cbm_name_rows_first(&rows, &arena, names, count, name);
while (same && expected >= 0) {
same = got == expected;
int after = -1;
for (int i = expected + 1; i < count && after < 0; i++)
if (names[i] && !strcmp(names[i], name))
after = i;
got = cbm_name_rows_next(&rows, names, count, name, got);
expected = after;
}
same = same && got == -1;
}
}
cbm_arena_destroy(&arena);
ASSERT_TRUE(same);
PASS();
}
/* A definition's parent comes through a short list of cached children; the wide root has to
* stay on that list while its narrow children come and go. */
static const char *cx_line_erlang_function(char *out, size_t cap, int i) {
snprintf(out, cap, "%% doc %d\nf_%d(A) -> g_%d(A).\ng_%d(B) -> B.\n", i, i, i, i);
return out;
}
TEST(complexity_doc_lookup_keeps_a_wide_root_cached) {
uint64_t base = 0;
double ratio =
cx_extract_ratio(CBM_LANG_ERLANG, "funcs.erl", "-module(m).\n", cx_line_erlang_function, "",
false, CBM_RESOLVE_STAT_SYNTAX_CHILD_STEPS, &base);
ASSERT_GTE(base, (uint64_t)CX_BINDING_LINES);
ASSERT_TRUE(ratio >= CX_RATIO_LO && ratio <= CX_RATIO_HI);
PASS();
}
/* Each struct's fields fold into one text, and each struct used to scan the file's defs. */
static const char *cx_line_go_struct(char *out, size_t cap, int i) {
snprintf(out, cap, "type T%d struct {\n\tA int\n\tB string\n}\n", i);
return out;
}
static uint64_t cx_field_fold_probes(int lines, int *folded) {
size_t room = (size_t)lines * CX_BINDING_LINE_ROOM + CX_BINDING_LINE_ROOM;
char *source = malloc(room);
if (!source)
return 0;
size_t length = (size_t)snprintf(source, room, "package p\n");
for (int i = 0; i < lines; i++) {
char row[CX_BINDING_LINE_ROOM];
length += (size_t)snprintf(source + length, room - length, "%s",
cx_line_go_struct(row, sizeof(row), i));
}
CBMFileResult *result =
cbm_extract_file(source, (int)length, CBM_LANG_GO, "p", "a.go", 0, NULL, NULL);
uint64_t work = 0;
if (result) {
CBMFileResult *cache[] = {result};
cbm_file_info_t files[] = {{.rel_path = "a.go", .language = CBM_LANG_GO}};
char *modules[1] = {NULL};
int starts[2] = {0}, count = 0;
const char *saved_source = result->source;
int saved_length = result->source_len;
result->source = source;
result->source_len = (int)length;
CBMArena arena;
cbm_arena_init(&arena);
cbm_resolve_stats_t stats;
cbm_resolve_stats_reset(&stats);
cbm_resolve_stats_t *previous = cbm_resolve_stats_bind(&stats);
CBMLSPDef *defs =
cbm_pxc_collect_all_defs(NULL, &arena, cache, files, 1, "p", modules, &count, starts);
cbm_resolve_stats_bind(previous);
work = cbm_resolve_stats_snapshot(&stats).values[CBM_RESOLVE_STAT_LOOKUP_PROBES];
*folded = 0;
for (int i = 0; defs && i < count; i++) {
if (defs[i].label && !strcmp(defs[i].label, "Struct") && defs[i].field_defs &&
!strcmp(defs[i].field_defs, "A:int|B:string")) {
(*folded)++;
}
}
cbm_arena_destroy(&arena);
result->source = saved_source;
result->source_len = saved_length;
cbm_free_result(result);
}
free(source);
return work;
}
TEST(complexity_struct_field_fold_is_linear) {
int folded_first = 0, folded_second = 0;
uint64_t first = cx_field_fold_probes(CX_BINDING_LINES, &folded_first);
uint64_t second = cx_field_fold_probes(CX_BINDING_LINES * 2, &folded_second);
double ratio = cx_ratio((double)second, (double)first);
printf(" struct field fold probes %llu -> %llu (ratio %.2f)\n", (unsigned long long)first,
(unsigned long long)second, ratio);
ASSERT_EQ(folded_first, CX_BINDING_LINES);
ASSERT_EQ(folded_second, CX_BINDING_LINES * 2);
ASSERT_GTE(first, (uint64_t)CX_BINDING_LINES);
ASSERT_TRUE(ratio >= CX_RATIO_LO && ratio <= CX_RATIO_HI);
PASS();
}
/* A leading doc comment is the child before the definition; it used to be found by
* counting up to the definition from the parent's first child. */
static const char *cx_line_php_function(char *out, size_t cap, int i) {
snprintf(out, cap, "function f_%d() { return 1; }\n", i);
return out;
}
static const char *cx_line_php_method(char *out, size_t cap, int i) {
snprintf(out, cap, " public function m_%d() { return 1; }\n", i);
return out;
}
static bool cx_php_doc_shape_binds(const char *path, const char *head, CxLineFn line,
const char *tail) {
uint64_t base = 0;
double plain = cx_extract_ratio(CBM_LANG_PHP, path, head, line, tail, true,
CBM_RESOLVE_STAT_SYNTAX_CHILD_STEPS, &base);
if (base < CX_BINDING_LINES || plain < 3.2)
return false;
double indexed = cx_extract_ratio(CBM_LANG_PHP, path, head, line, tail, false,
CBM_RESOLVE_STAT_SYNTAX_CHILD_STEPS, &base);
return base >= CX_BINDING_LINES && indexed >= CX_RATIO_LO && indexed <= CX_RATIO_HI;
}
/* A call to a name nothing declares looked through every registered function. */
static const char *cx_line_php_unknown_call(char *out, size_t cap, int i) {
snprintf(out, cap, "function f_%d() { return g_%d(); }\n", i, i);
return out;
}
TEST(complexity_php_short_name_fallback_is_linear) {
ASSERT_TRUE(
cx_pass_steps_are_linear(CBM_LANG_PHP, "calls.php", "<?php\n", cx_line_php_unknown_call));
PASS();
}
TEST(complexity_php_doc_lookup_is_linear) {
ASSERT_TRUE(cx_php_doc_shape_binds("flat.php", "<?php\n", cx_line_php_function, ""));
ASSERT_TRUE(
cx_php_doc_shape_binds("wide.php", "<?php\nclass C {\n", cx_line_php_method, "}\n"));
PASS();
}
/* Python's own pass joined each import row to every import statement, each statement's
* names to every row, and each reference to every row. */
static const char *cx_line_py_import_pair(char *out, size_t cap, int i) {
snprintf(out, cap, "import pkg_%d.part\nfrom mod_%d import name_%d\nname_%d()\n", i, i, i, i);
return out;
}
static const char *cx_line_py_from_item(char *out, size_t cap, int i) {
snprintf(out, cap, " name_%d,\n", i);
return out;
}
TEST(complexity_python_pass_import_join_is_linear) {
uint64_t base = 0;
double ratio = cx_extract_ratio(CBM_LANG_PYTHON, "pairs.py", "", cx_line_py_import_pair, "",
false, CBM_RESOLVE_STAT_LOOKUP_PROBES, &base);
ASSERT_GTE(base, (uint64_t)CX_BINDING_LINES);
ASSERT_TRUE(ratio >= CX_RATIO_LO && ratio <= CX_RATIO_HI);
PASS();
}
/* One statement can hold them all: generated SDK packages re-export thousands of names. */
TEST(complexity_python_pass_wide_from_import_is_linear) {
uint64_t base = 0;
double ratio =
cx_extract_ratio(CBM_LANG_PYTHON, "reexport.py", "from .models import (\n",
cx_line_py_from_item, ")\n", false, CBM_RESOLVE_STAT_LOOKUP_PROBES, &base);
ASSERT_GTE(base, (uint64_t)CX_BINDING_LINES);
ASSERT_TRUE(ratio >= CX_RATIO_LO && ratio <= CX_RATIO_HI);
PASS();
}
/* ── Faster routes store the same thing ────────────────────────────── */
extern bool cbm_pipeline_test_resolver_stage_off;
extern bool cbm_pipeline_test_prepare_on_one_thread;
extern _Atomic size_t cbm_binding_cache_test_hits;
extern _Atomic size_t cbm_parse_test_trees_shared;
/* Everything the views say, except each row's ordinal: that follows worker scheduling. */
static bool cx_evidence_hash(const char *db_path, unsigned char digest[32], bool *intact) {
static const char occurrences[] =
"SELECT project,file_path,scope,relation,spelling,start_byte,end_byte,source_origin,"
"binding,domain,analysis,rule,facts,target,candidate_count,reason,emitted,rules_version,"
"properties_json FROM resolver_occurrences ORDER BY "
"1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19";
static const char edges[] =
"SELECT s.qualified_name,e.type,t.qualified_name,e.properties FROM edges e "
"JOIN nodes s ON s.id=e.source_id JOIN nodes t ON t.id=e.target_id ORDER BY 1,2,3,4";
const char *const queries[] = {
occurrences,
"SELECT project,id,kind,source_file,digest,payload FROM resolver_facts ORDER BY project,id",
"SELECT project,fact,ordinal,premise FROM resolver_supports ORDER BY project,fact,ordinal",
"SELECT project,fact,domain,key,status FROM resolver_lookups ORDER BY project,fact",
"SELECT * FROM resolver_file_summaries ORDER BY project,file_path",
"SELECT * FROM resolver_derived_bindings ORDER BY project,file_path,key,kind",
edges,
"SELECT name,sql FROM sqlite_master WHERE name LIKE 'resolver_%' ORDER BY name"};
sqlite3 *db = NULL;
if (sqlite3_open_v2(db_path, &db, SQLITE_OPEN_READONLY, NULL) != SQLITE_OK) {
sqlite3_close(db);
return false;
}
cbm_sha256_ctx hash;
cbm_sha256_init(&hash);
uint64_t rows = 0;
bool ok = true;
for (size_t q = 0; ok && q < sizeof(queries) / sizeof(queries[0]); q++) {
sqlite3_stmt *s = NULL;
ok = sqlite3_prepare_v2(db, queries[q], -1, &s, NULL) == SQLITE_OK;
int step = SQLITE_DONE;
while (ok && (step = sqlite3_step(s)) == SQLITE_ROW) {
rows++;
for (int c = 0; c < sqlite3_column_count(s); c++) {
const void *bytes = sqlite3_column_blob(s, c);
int length = sqlite3_column_bytes(s, c);
int type = sqlite3_column_type(s, c);
cbm_sha256_update(&hash, &type, sizeof(type));
cbm_sha256_update(&hash, &length, sizeof(length));
if (bytes && length)
cbm_sha256_update(&hash, bytes, (size_t)length);
}
}
ok = ok && step == SQLITE_DONE;
sqlite3_finalize(s);
}
sqlite3_stmt *check = NULL;
*intact = sqlite3_prepare_v2(db, "PRAGMA integrity_check(1)", -1, &check, NULL) == SQLITE_OK &&
sqlite3_step(check) == SQLITE_ROW &&
!strcmp((const char *)sqlite3_column_text(check, 0), "ok");
sqlite3_finalize(check);
sqlite3_close(db);
cbm_sha256_final(&hash, digest);
return ok && rows > 0;
}
static bool cx_index_and_hash(const char *root, const char *name, unsigned char digest[32],
bool *intact) {
char db_path[600];
int n = snprintf(db_path, sizeof(db_path), "%s/%s", root, name);
if (n < 0 || (size_t)n >= sizeof(db_path))
return false;
cbm_pipeline_t *p = cbm_pipeline_new(root, db_path, CBM_MODE_FULL);
if (!p)
return false;
int rc = cbm_pipeline_run(p);
cbm_pipeline_free(p);
return rc == 0 && cx_evidence_hash(db_path, digest, intact);
}
/* Evidence written straight into a new file's pages reads back exactly as SQL's does,
* with the same schema text, and SQLite finds nothing wrong with the file. */
TEST(complexity_evidence_page_route_stores_what_sql_stores) {
char root[512];
snprintf(root, sizeof(root), "%s/cbm_cxroute_XXXXXX", cbm_tmpdir());
ASSERT_NOT_NULL(cbm_mkdtemp(root));
ASSERT_EQ(cx_build_corpus(root, CX_K_BASE), 0);
unsigned char direct[32], sql[32];
bool direct_intact = false, sql_intact = false;
bool direct_ok = cx_index_and_hash(root, "direct.db", direct, &direct_intact);
cbm_pipeline_test_resolver_stage_off = true;
bool sql_ok = cx_index_and_hash(root, "sql.db", sql, &sql_intact);
cbm_pipeline_test_resolver_stage_off = false;
ASSERT_EQ(cx_remove_corpus(root), 0);
ASSERT_TRUE(direct_ok);
ASSERT_TRUE(sql_ok);
ASSERT_TRUE(direct_intact);
ASSERT_TRUE(sql_intact);
ASSERT_EQ(memcmp(direct, sql, sizeof(direct)), 0);
PASS();
}
/* The shared indexes and parse batches change how long preparation takes and nothing it
* decides. The counters show they really were used on the first run. */
TEST(complexity_shared_preparation_decides_the_same) {
char root[512];
snprintf(root, sizeof(root), "%s/cbm_cxprep_XXXXXX", cbm_tmpdir());
ASSERT_NOT_NULL(cbm_mkdtemp(root));
ASSERT_EQ(cx_build_corpus(root, CX_K_BASE), 0);
unsigned char shared[32], alone[32];
bool shared_intact = false, alone_intact = false;
size_t hits_before = atomic_load(&cbm_binding_cache_test_hits);
size_t trees_before = atomic_load(&cbm_parse_test_trees_shared);
bool shared_ok = cx_index_and_hash(root, "shared.db", shared, &shared_intact);
size_t hits = atomic_load(&cbm_binding_cache_test_hits) - hits_before;
size_t trees = atomic_load(&cbm_parse_test_trees_shared) - trees_before;
cbm_pipeline_test_prepare_on_one_thread = true;
hits_before = atomic_load(&cbm_binding_cache_test_hits);
trees_before = atomic_load(&cbm_parse_test_trees_shared);
bool alone_ok = cx_index_and_hash(root, "alone.db", alone, &alone_intact);
size_t hits_alone = atomic_load(&cbm_binding_cache_test_hits) - hits_before;
size_t trees_alone = atomic_load(&cbm_parse_test_trees_shared) - trees_before;
cbm_pipeline_test_prepare_on_one_thread = false;
ASSERT_EQ(cx_remove_corpus(root), 0);
ASSERT_TRUE(shared_ok);
ASSERT_TRUE(alone_ok);
ASSERT_TRUE(shared_intact);
ASSERT_GT(hits, (size_t)0);
ASSERT_GT(trees, (size_t)0);
ASSERT_EQ(hits_alone, (size_t)0);
/* Workers still share a tree between their passes over one file; preparation does not. */
ASSERT_LT(trees_alone, trees);
ASSERT_EQ(memcmp(shared, alone, sizeof(shared)), 0);
PASS();
}
/* A bound tree answers only the exact parse it stands for. */
TEST(complexity_parse_bind_answers_only_the_same_parse) {
static const char source[] = "x = 1\n";
static const char other[] = "y = 1\n";
static const char unterminated[] = "x = 1";
TSParser *parser = ts_parser_new();
ASSERT_NOT_NULL(parser);
const TSLanguage *python = cbm_ts_language(CBM_LANG_PYTHON);
ASSERT_TRUE(ts_parser_set_language(parser, python));
uint32_t length = (uint32_t)strlen(source);
TSTree *tree = ts_parser_parse_string(parser, NULL, source, length);
ASSERT_NOT_NULL(tree);
cbm_parse_bound_t bound = {.mode = CBM_PARSE_PLAIN,
.language = python,
.source = source,
.length = length,
.tree = tree};
cbm_parse_bind(&bound);
size_t before = atomic_load(&cbm_parse_test_trees_shared);
#define CX_SHARED() (atomic_load(&cbm_parse_test_trees_shared) - before)
/* The same bytes elsewhere in memory, and either mode: a newline-terminated file. */
char copy[sizeof(source)];
memcpy(copy, source, sizeof(source));
TSTree *same = cbm_parse_plain(parser, copy, length);
ASSERT_EQ(CX_SHARED(), (size_t)1);
ts_tree_delete(same);
same = cbm_parse_source(parser, source, length, (TSParseOptions){0});
ASSERT_EQ(CX_SHARED(), (size_t)2);
ts_tree_delete(same);
/* Different bytes of the same length, a different length, another grammar, the C mode. */
same = cbm_parse_plain(parser, other, length);
ASSERT_EQ(CX_SHARED(), (size_t)2);
ts_tree_delete(same);
same = cbm_parse_plain(parser, unterminated, (uint32_t)strlen(unterminated));
ASSERT_EQ(CX_SHARED(), (size_t)2);
ts_tree_delete(same);
TSParser *go = ts_parser_new();
ASSERT_TRUE(ts_parser_set_language(go, cbm_ts_language(CBM_LANG_GO)));
same = cbm_parse_plain(go, source, length);
ASSERT_EQ(CX_SHARED(), (size_t)2);
ts_tree_delete(same);
ts_parser_delete(go);
/* A tree of the C route holds what that route decided, and only it gets that tree. */
bound.mode = CBM_PARSE_C;
same = cbm_parse_plain(parser, source, length);
ASSERT_EQ(CX_SHARED(), (size_t)2);
ts_tree_delete(same);
bound.mode = CBM_PARSE_PLAIN;
/* A parser reading part of the file does not get the whole file's tree. */
TSRange part = {.start_byte = 0, .end_byte = 1, .start_point = {0, 0}, .end_point = {0, 1}};
ASSERT_TRUE(ts_parser_set_included_ranges(parser, &part, 1));
same = cbm_parse_plain(parser, source, length);
ASSERT_EQ(CX_SHARED(), (size_t)2);
ts_tree_delete(same);
ASSERT_TRUE(ts_parser_set_included_ranges(parser, NULL, 0));
/* An unterminated file: the two modes are different parses. */
TSTree *plain =
ts_parser_parse_string(parser, NULL, unterminated, (uint32_t)strlen(unterminated));
bound = (cbm_parse_bound_t){.mode = CBM_PARSE_PLAIN,
.language = python,
.source = unterminated,
.length = (uint32_t)strlen(unterminated),
.tree = plain};
same =
cbm_parse_source(parser, unterminated, (uint32_t)strlen(unterminated), (TSParseOptions){0});
ASSERT_EQ(CX_SHARED(), (size_t)2);
ts_tree_delete(same);
same = cbm_parse_plain(parser, unterminated, (uint32_t)strlen(unterminated));
ASSERT_EQ(CX_SHARED(), (size_t)3);
ts_tree_delete(same);
cbm_parse_bind(NULL);
same = cbm_parse_plain(parser, unterminated, (uint32_t)strlen(unterminated));
ASSERT_EQ(CX_SHARED(), (size_t)3);
ts_tree_delete(same);
#undef CX_SHARED
ts_tree_delete(plain);
ts_tree_delete(tree);
ts_parser_delete(parser);
PASS();
}
/* A file's import map: each import asks what path its name has and whether it is mapped
* already. A page of generated HTML has thousands of them; so can a Kotlin file. */
static uint64_t cx_import_map_probes(CBMLanguage language, const char *path, CxLineFn line,
int lines, int *mapped) {
size_t room = (size_t)lines * CX_BINDING_LINE_ROOM + CX_BINDING_LINE_ROOM;
char *source = malloc(room);
if (!source)
return 0;
size_t length = 0;
for (int i = 0; i < lines; i++) {
char row[CX_BINDING_LINE_ROOM];
length += (size_t)snprintf(source + length, room - length, "%s", line(row, sizeof(row), i));
}
CBMFileResult *result =
cbm_extract_file(source, (int)length, language, "test", path, 0, NULL, NULL);
uint64_t work = 0;
cbm_gbuf_t *gbuf = cbm_gbuf_new("test", "/tmp/test");
if (result && gbuf) {
cbm_pipeline_ctx_t ctx = {.project_name = "test", .gbuf = gbuf};
cbm_resolve_stats_t stats;
cbm_resolve_stats_reset(&stats);
cbm_resolve_stats_t *previous = cbm_resolve_stats_bind(&stats);
const char **keys = NULL, **values = NULL;
int count = 0;
int rc =
cbm_pxc_build_import_map_ex(&ctx, path, language, result, true, &keys, &values, &count);
cbm_resolve_stats_bind(previous);
if (rc == 0)
work = cbm_resolve_stats_snapshot(&stats).values[CBM_RESOLVE_STAT_LOOKUP_PROBES];
*mapped = count;
cbm_pxc_free_import_map(keys, values, count);
}
cbm_gbuf_free(gbuf);
if (result)
cbm_free_result(result);
free(source);
return work;
}
static const char *cx_line_kt_import(char *out, size_t cap, int i) {
snprintf(out, cap, "import q.r_%d.S_%d\n", i, i);
return out;
}
TEST(complexity_import_map_is_linear_in_a_files_imports) {
int mapped_first = 0, mapped_second = 0;
uint64_t first = cx_import_map_probes(CBM_LANG_KOTLIN, "imports.kt", cx_line_kt_import,
CX_BINDING_LINES, &mapped_first);
uint64_t second = cx_import_map_probes(CBM_LANG_KOTLIN, "imports.kt", cx_line_kt_import,
CX_BINDING_LINES * 2, &mapped_second);
double ratio = cx_ratio((double)second, (double)first);
printf(" import map lookups %llu -> %llu (ratio %.2f)\n", (unsigned long long)first,
(unsigned long long)second, ratio);
ASSERT_EQ(mapped_first, CX_BINDING_LINES);
ASSERT_EQ(mapped_second, CX_BINDING_LINES * 2);
ASSERT_GTE(first, (uint64_t)CX_BINDING_LINES);
ASSERT_TRUE(ratio >= CX_RATIO_LO && ratio <= CX_RATIO_HI);
PASS();
}
/* A conflicting name blanks its edge entry and every later entry, and a repeated plain
* name is mapped once: what the two scans did, in the order they did it. */
TEST(complexity_import_map_keeps_conflicts_and_repeats) {
static const char source[] =
"import a.b.S\nimport c.d.S\nimport e.f.T\nimport e.f.T\nimport g.h.U\n";
CBMFileResult *result = cbm_extract_file(source, (int)strlen(source), CBM_LANG_KOTLIN, "test",
"conflicts.kt", 0, NULL, NULL);
ASSERT_NOT_NULL(result);
cbm_gbuf_t *gbuf = cbm_gbuf_new("test", "/tmp/test");
ASSERT_NOT_NULL(gbuf);
cbm_pipeline_ctx_t ctx = {.project_name = "test", .gbuf = gbuf};
const char **keys = NULL, **values = NULL;
int count = 0;
ASSERT_EQ(cbm_pxc_build_import_map_ex(&ctx, "conflicts.kt", CBM_LANG_KOTLIN, result, true,
&keys, &values, &count),
0);
ASSERT_EQ(count, 4);
ASSERT_STR_EQ(keys[0], "S");
ASSERT_STR_EQ(values[0], "");
ASSERT_STR_EQ(keys[1], "S");
ASSERT_STR_EQ(values[1], "");
ASSERT_STR_EQ(keys[2], "T");
ASSERT_STR_EQ(values[2], "e.f.T");
ASSERT_STR_EQ(keys[3], "U");
ASSERT_STR_EQ(values[3], "g.h.U");
cbm_pxc_free_import_map(keys, values, count);
cbm_gbuf_free(gbuf);
cbm_free_result(result);
PASS();
}
/* A worker's passes over one file share one parse, and a later file gets its own. */
TEST(complexity_parse_memo_lasts_one_file) {
static const char first[] = "x = 1\n";
static const char second[] = "yy = 2\n";
TSParser *parser = ts_parser_new();
ASSERT_NOT_NULL(parser);
ASSERT_TRUE(ts_parser_set_language(parser, cbm_ts_language(CBM_LANG_PYTHON)));
uint32_t first_length = (uint32_t)strlen(first), second_length = (uint32_t)strlen(second);
size_t before = atomic_load(&cbm_parse_test_trees_shared);
#define CX_SHARED() (atomic_load(&cbm_parse_test_trees_shared) - before)
/* Nothing is kept outside a memo. */
TSTree *tree = cbm_parse_plain(parser, first, first_length);
ts_tree_delete(tree);
tree = cbm_parse_plain(parser, first, first_length);
ts_tree_delete(tree);
ASSERT_EQ(CX_SHARED(), (size_t)0);
cbm_parse_memo_begin();
tree = cbm_parse_source(parser, first, first_length, (TSParseOptions){0});
ASSERT_EQ(CX_SHARED(), (size_t)0);
/* The caller's tree and bytes may go away: the memo keeps its own. */
char *moved = malloc(first_length);
ASSERT_NOT_NULL(moved);
memcpy(moved, first, first_length);
ts_tree_delete(tree);
tree = cbm_parse_plain(parser, moved, first_length);
free(moved);
ASSERT_EQ(CX_SHARED(), (size_t)1);
ASSERT_STR_EQ(ts_node_type(ts_tree_root_node(tree)), "module");
ts_tree_delete(tree);
tree = cbm_parse_plain(parser, second, second_length);
ASSERT_EQ(CX_SHARED(), (size_t)1);
ts_tree_delete(tree);
tree = cbm_parse_plain(parser, second, second_length);
ASSERT_EQ(CX_SHARED(), (size_t)2);
ts_tree_delete(tree);
cbm_parse_memo_begin();
tree = cbm_parse_plain(parser, first, first_length);
ASSERT_EQ(CX_SHARED(), (size_t)2);
ts_tree_delete(tree);
cbm_parse_memo_end();
tree = cbm_parse_plain(parser, first, first_length);
ASSERT_EQ(CX_SHARED(), (size_t)2);
ts_tree_delete(tree);
#undef CX_SHARED
ts_parser_delete(parser);
PASS();
}
/* ── A language adapter's two phases ────────────────────────────────
* Over files already extracted, so what is counted is what the adapter itself walks: a
* name's definitions in a file, a library's directives, a type's bases. */
#define CX_LINE(fn, format) \
static const char *fn(char *out, size_t cap, int i) { \
snprintf(out, cap, format, i); \
return out; \
}
#define CX_LINE3(fn, format) \
static const char *fn(char *out, size_t cap, int i) { \
snprintf(out, cap, format, i, i, i); \
return out; \
}
typedef struct {
char path[64];
char *text;
} CxSourceFile;
static char *cx_lines_text(const char *head, CxLineFn line, const char *tail, int lines) {
size_t room = strlen(head) + strlen(tail) + (size_t)lines * CX_BINDING_LINE_ROOM + 1;
char *source = malloc(room);
if (!source)
return NULL;
size_t length = (size_t)snprintf(source, room, "%s", head);
for (int i = 0; i < lines; i++) {
char row[CX_BINDING_LINE_ROOM];
length += (size_t)snprintf(source + length, room - length, "%s", line(row, sizeof(row), i));
}
(void)snprintf(source + length, room - length, "%s", tail);
return source;
}
static uint64_t cx_source_work(CBMLanguage language, const CxSourceFile *files, int count) {
CBMFileResult **results = calloc((size_t)count, sizeof(*results));
cbm_source_table_t *table = cbm_source_table_new();
bool ok = results && table;
for (int f = 0; ok && f < count; f++)
results[f] = cbm_extract_file(files[f].text, (int)strlen(files[f].text), language, "test",
files[f].path, 0, NULL, NULL);
cbm_resolve_stats_t stats;
cbm_resolve_stats_reset(&stats);
cbm_resolve_stats_t *previous = cbm_resolve_stats_bind(&stats);
for (int f = 0; ok && f < count; f++)
ok = results[f] && cbm_source_collect(table, results[f], language, files[f].path,
files[f].text, strlen(files[f].text));
for (int f = 0; ok && f < count; f++) {
CBMArena arena;
cbm_arena_init(&arena);
ok = cbm_source_unit_build(&arena, table, results[f], language, files[f].path,
files[f].text, strlen(files[f].text)) != NULL;
cbm_arena_destroy(&arena);
}
cbm_resolve_stats_bind(previous);
uint64_t work = ok ? cx_stat_work(&stats, CBM_RESOLVE_STAT_LOOKUP_PROBES) : 0;
for (int f = 0; results && f < count; f++)
if (results[f])
cbm_free_result(results[f]);
free(results);
if (table)
cbm_source_table_free(table);
return work;
}
/* One generated file, at CX_BINDING_LINES lines and at twice that. */
static bool cx_source_shape_is_near_linear(CBMLanguage language, const char *path, const char *head,
CxLineFn line, const char *tail) {
uint64_t work[2] = {0, 0};
for (int round = 0; round < 2; round++) {
CxSourceFile file = {.text = cx_lines_text(head, line, tail, CX_BINDING_LINES << round)};
snprintf(file.path, sizeof(file.path), "%s", path);
if (file.text)
work[round] = cx_source_work(language, &file, 1);
free(file.text);
}
double ratio = cx_ratio((double)work[1], (double)work[0]);
printf(" %s source probes %llu -> %llu (ratio %.2f)\n", path, (unsigned long long)work[0],
(unsigned long long)work[1], ratio);
return work[0] >= CX_BINDING_LINES && ratio >= CX_RATIO_LO && ratio <= CX_RATIO_HI;
}
/* A name can repeat all through one file: a script's variable, a chunk's `local ok`, `init` in
* a generated file. Each declaration asks which definition is its own. */
CX_LINE(cx_line_lua_same_function, "local function helper() return %d end\n")
CX_LINE(cx_line_lua_same_local, "local x = f(%d)\n")
CX_LINE(cx_line_shell_same_variable, "x=%d\n")
CX_LINE(cx_line_vim_same_function, "function! Foo()\n return %d\nendfunction\n")
CX_LINE(cx_line_tcl_same_proc, "proc f {} { return %d }\n")
CX_LINE(cx_line_swift_same_function, "func f(a%d: Int) {}\n")
CX_LINE(cx_line_dart_same_method, "class C_%d { void go() {} }\n")
CX_LINE(cx_line_haskell_same_clause, "f %d = 1\n")
TEST(complexity_source_same_named_definitions_are_near_linear) {
/* Every shape runs and prints, so a failure shows all of them. */
bool ok = cx_source_shape_is_near_linear(CBM_LANG_LUA, "same.lua", "",
cx_line_lua_same_function, "");
ok = cx_source_shape_is_near_linear(CBM_LANG_LUA, "same.lua", "", cx_line_lua_same_local, "") &&
ok;
ok = cx_source_shape_is_near_linear(CBM_LANG_BASH, "same.sh", "", cx_line_shell_same_variable,
"") &&
ok;
ok = cx_source_shape_is_near_linear(CBM_LANG_VIMSCRIPT, "same.vim", "",
cx_line_vim_same_function, "") &&
ok;
ok = cx_source_shape_is_near_linear(CBM_LANG_TCL, "same.tcl", "", cx_line_tcl_same_proc, "") &&
ok;
ok = cx_source_shape_is_near_linear(CBM_LANG_SWIFT, "same.swift", "",
cx_line_swift_same_function, "") &&
ok;
ok = cx_source_shape_is_near_linear(CBM_LANG_DART, "same.dart", "", cx_line_dart_same_method,
"") &&
ok;
ok = cx_source_shape_is_near_linear(CBM_LANG_HASKELL, "Same.hs", "module Same where\n",
cx_line_haskell_same_clause, "") &&
ok;
ASSERT_TRUE(ok);
PASS();
}
/* A barrel library re-exports the project, and every library that imports it read its whole
* export list again. */
static uint64_t cx_dart_barrel_work(int libraries) {
CxSourceFile *files = calloc((size_t)libraries + 1, sizeof(*files));
if (!files)
return 0;
size_t room = (size_t)libraries * 32 + 1;
char *barrel = malloc(room);
size_t used = 0;
bool ok = barrel != NULL;
for (int i = 0; ok && i < libraries; i++) {
used += (size_t)snprintf(barrel + used, room - used, "export 'a%d.dart';\n", i);
snprintf(files[i].path, sizeof(files[i].path), "lib/a%d.dart", i);
files[i].text = malloc(96);
ok = files[i].text != NULL;
if (ok)
snprintf(files[i].text, 96, "import 'all.dart';\nint f%d() => f%d();\n", i,
(i + 1) % libraries);
}
snprintf(files[libraries].path, sizeof(files[libraries].path), "lib/all.dart");
files[libraries].text = barrel;
uint64_t work = ok ? cx_source_work(CBM_LANG_DART, files, libraries + 1) : 0;
for (int i = 0; i <= libraries; i++)
free(files[i].text);
free(files);
return work;
}
TEST(complexity_dart_barrel_exports_are_near_linear) {
enum { BARREL_LIBRARIES = 500 };
uint64_t first = cx_dart_barrel_work(BARREL_LIBRARIES);
uint64_t second = cx_dart_barrel_work(BARREL_LIBRARIES * 2);
double ratio = cx_ratio((double)second, (double)first);
printf(" barrel source probes %llu -> %llu (ratio %.2f)\n", (unsigned long long)first,
(unsigned long long)second, ratio);
ASSERT_GTE(first, (uint64_t)BARREL_LIBRARIES);
ASSERT_TRUE(ratio >= CX_RATIO_LO && ratio <= CX_RATIO_HI);
PASS();
}
/* A function of route registrations asks, for each, what its receiver was bound to. */
CX_LINE(cx_line_go_route, " r.GET(\"/p%d\", h)\n")
CX_LINE3(cx_line_go_group, " g%d := r.Group(\"/g%d\")\n g%d.GET(\"/x\", h)\n")
static bool cx_go_route_shape_is_near_linear(const char *head, CxLineFn line) {
uint64_t base = 0;
double ratio = cx_extract_ratio(CBM_LANG_GO, "routes.go", head, line, "}\n", false,
CBM_RESOLVE_STAT_LOOKUP_PROBES, &base);
return base >= CX_BINDING_LINES && ratio >= CX_RATIO_LO && ratio <= CX_RATIO_HI;
}
TEST(complexity_go_route_bindings_are_near_linear) {
bool ok = cx_go_route_shape_is_near_linear("package main\nfunc Register(r Router) {\n",
cx_line_go_route);
ok = cx_go_route_shape_is_near_linear("package main\nfunc Register() {\n r := New()\n",
cx_line_go_route) &&
ok;
ok = cx_go_route_shape_is_near_linear("package main\nfunc Register(r Router) {\n",
cx_line_go_group) &&
ok;
ASSERT_TRUE(ok);
PASS();
}
/* Flat assembly and routines: each statement looks back for the label above it. */
CX_LINE(cx_line_nasm_instruction, " mov eax, %d\n")
CX_LINE(cx_line_objectscript_statement, " Set x = %d\n")
static bool cx_flat_shape_is_linear(CBMLanguage language, const char *path, const char *head,
CxLineFn line) {
uint64_t base = 0;
double ratio =
cx_extract_ratio(language, path, head, line, "", false, CBM_RESOLVE_STAT_LOOKUP_PROBES, &base);
return base >= CX_BINDING_LINES && ratio >= CX_RATIO_LO && ratio <= CX_RATIO_HI;
}
TEST(complexity_flat_preceding_label_is_linear) {
bool ok = cx_flat_shape_is_linear(CBM_LANG_NASM, "flat.nasm", "global start\nstart:\n",
cx_line_nasm_instruction);
ok = cx_flat_shape_is_linear(CBM_LANG_OBJECTSCRIPT_ROUTINE, "Flat.mac", "Main()\n",
cx_line_objectscript_statement) &&
ok;
ASSERT_TRUE(ok);
PASS();
}
/* Every node of sources that parse, that do not, and that hold zero-width MISSING tokens: the
* routed calls answer exactly as tree-sitter does. */
TEST(complexity_syntax_index_prev_siblings_answer_as_tree_sitter_does) {
static const struct {
CBMLanguage language;
const char *source;
} cases[] = {
{CBM_LANG_PYTHON, "def f(a, b=1):\n return [a, (b), {'k': a}]\n\nclass C:\n x = 1\n"},
{CBM_LANG_TYPESCRIPT,
"@D() class A { @G('/x') m(a: number) { return a; } /* c */ n() {} }\nexport { A };\n"},
{CBM_LANG_GO, "package p\nfunc f() {\n r := g()\n r.GET(\"/x\", h)\n // c\n if r != nil "
"{ return }\n}\n"},
{CBM_LANG_C, "int f(void { return 1 }\nstruct S { int a };\nint g(void) { x = ; }\n"},
{CBM_LANG_DART, "int f() { return 1; }\nclass C { void m() {} }\nint g( { }\n"},
{CBM_LANG_JAVASCRIPT, "class { m( }\nfunction f(a { return a\n"},
{CBM_LANG_NASM, "global s\ns:\n mov eax, 1\n ret\n"},
{CBM_LANG_ELIXIR, "defmodule M do\n def f(a), do: a &&\nend\n"},
};
size_t visited = 0, zero_width = 0;
bool same = true;
for (size_t c = 0; c < sizeof(cases) / sizeof(cases[0]); c++) {
TSParser *parser = ts_parser_new();
ASSERT_NOT_NULL(parser);
ASSERT_TRUE(ts_parser_set_language(parser, cbm_ts_language(cases[c].language)));
TSTree *tree =
ts_parser_parse_string(parser, NULL, cases[c].source, (uint32_t)strlen(cases[c].source));
ts_parser_delete(parser);
ASSERT_NOT_NULL(tree);
cbm_syntax_index_t index;
cbm_syntax_index_begin(&index, tree);
TSTreeCursor cursor = ts_tree_cursor_new(ts_tree_root_node(tree));
for (bool more = true; more;) {
TSNode node = ts_tree_cursor_current_node(&cursor);
visited++;
zero_width += ts_node_start_byte(node) == ts_node_end_byte(node);
same = same && ts_node_eq(cbm_syntax_prev_sibling(node), ts_node_prev_sibling(node));
same = same &&
ts_node_eq(cbm_syntax_prev_named_sibling(node), ts_node_prev_named_sibling(node));
if (ts_tree_cursor_goto_first_child(&cursor))
continue;
while (!ts_tree_cursor_goto_next_sibling(&cursor)) {
if (!ts_tree_cursor_goto_parent(&cursor)) {
more = false;
break;
}
}
}
ts_tree_cursor_delete(&cursor);
cbm_syntax_index_end(&index);
ts_tree_delete(tree);
}
ASSERT_TRUE(same);
ASSERT_GTE(visited, (size_t)100);
ASSERT_GTE(zero_width, (size_t)1);
PASS();
}
/* Asking for the sibling before each child of a wide parent, plainly and through the index. */
static uint64_t cx_prev_sibling_work(int lines, bool plain) {
char *source = cx_lines_text("", cx_line_py_statement, "", lines);
TSParser *parser = ts_parser_new();
TSTree *tree = NULL;
if (source && parser && ts_parser_set_language(parser, cbm_ts_language(CBM_LANG_PYTHON)))
tree = ts_parser_parse_string(parser, NULL, source, (uint32_t)strlen(source));
if (parser)
ts_parser_delete(parser);
uint64_t work = 0;
if (tree) {
cbm_resolve_stats_t stats;
cbm_resolve_stats_reset(&stats);
cbm_resolve_stats_t *previous = cbm_resolve_stats_bind(&stats);
cbm_syntax_index_t index;
cbm_syntax_index_begin(&index, tree);
cbm_syntax_index_test_set_plain(plain);
TSTreeCursor cursor = ts_tree_cursor_new(ts_tree_root_node(tree));
if (ts_tree_cursor_goto_first_child(&cursor)) {
do {
TSNode child = ts_tree_cursor_current_node(&cursor);
(void)cbm_syntax_prev_named_sibling(child);
} while (ts_tree_cursor_goto_next_sibling(&cursor));
}
ts_tree_cursor_delete(&cursor);
cbm_syntax_index_test_set_plain(false);
cbm_syntax_index_end(&index);
cbm_resolve_stats_bind(previous);
work = cx_stat_work(&stats, CBM_RESOLVE_STAT_SYNTAX_CHILD_STEPS);
ts_tree_delete(tree);
}
free(source);
return work;
}
TEST(complexity_prev_sibling_walk_is_linear) {
uint64_t first = cx_prev_sibling_work(CX_BINDING_LINES, true);
uint64_t second = cx_prev_sibling_work(CX_BINDING_LINES * 2, true);
double plain = cx_ratio((double)second, (double)first);
printf(" plain prev sibling steps %llu -> %llu (ratio %.2f)\n", (unsigned long long)first,
(unsigned long long)second, plain);
ASSERT_GTE(first, (uint64_t)CX_BINDING_LINES);
ASSERT_TRUE(plain >= 3.2);
first = cx_prev_sibling_work(CX_BINDING_LINES, false);
second = cx_prev_sibling_work(CX_BINDING_LINES * 2, false);
double indexed = cx_ratio((double)second, (double)first);
printf(" indexed prev sibling steps %llu -> %llu (ratio %.2f)\n", (unsigned long long)first,
(unsigned long long)second, indexed);
ASSERT_GTE(first, (uint64_t)CX_BINDING_LINES);
ASSERT_TRUE(indexed >= CX_RATIO_LO && indexed <= CX_RATIO_HI);
PASS();
}
SUITE(complexity) {
RUN_TEST(complexity_syntax_index_prev_siblings_answer_as_tree_sitter_does);
RUN_TEST(complexity_prev_sibling_walk_is_linear);
RUN_TEST(complexity_source_same_named_definitions_are_near_linear);
RUN_TEST(complexity_dart_barrel_exports_are_near_linear);
RUN_TEST(complexity_go_route_bindings_are_near_linear);
RUN_TEST(complexity_flat_preceding_label_is_linear);
RUN_TEST(complexity_import_map_is_linear_in_a_files_imports);
RUN_TEST(complexity_import_map_keeps_conflicts_and_repeats);
RUN_TEST(complexity_parse_memo_lasts_one_file);
RUN_TEST(complexity_evidence_page_route_stores_what_sql_stores);
RUN_TEST(complexity_shared_preparation_decides_the_same);
RUN_TEST(complexity_parse_bind_answers_only_the_same_parse);
RUN_TEST(complexity_binding_wide_module_questions_are_linear);
RUN_TEST(complexity_binding_wide_inner_nodes_are_linear);
RUN_TEST(complexity_syntax_index_answers_as_tree_sitter_does);
RUN_TEST(complexity_syntax_index_wide_lists_match_tree_sitter);
RUN_TEST(complexity_js_value_join_is_linear_in_a_repeated_name);
RUN_TEST(complexity_python_dotted_imports_are_linear);
RUN_TEST(complexity_python_import_join_is_linear);
RUN_TEST(complexity_js_pass_registry_lookups_are_linear);
RUN_TEST(complexity_import_uses_are_linear_in_every_language_pass);
RUN_TEST(complexity_name_rows_answer_as_a_scan_does);
RUN_TEST(complexity_target_file_list_is_near_linear);
RUN_TEST(complexity_python_root_facts_are_linear);
RUN_TEST(complexity_rust_pass_registration_is_linear);
RUN_TEST(complexity_rust_local_lookup_is_linear);
RUN_TEST(complexity_rust_conditional_use_lookup_is_linear);
RUN_TEST(complexity_rust_named_use_lookup_is_linear);
RUN_TEST(complexity_rust_trait_in_scope_is_linear);
RUN_TEST(complexity_rust_module_children_are_linear);
RUN_TEST(complexity_rust_outside_impls_are_linear);
RUN_TEST(complexity_rust_impl_lookup_is_linear);
RUN_TEST(complexity_go_pass_registration_is_linear);
RUN_TEST(complexity_java_short_name_fallback_is_linear);
RUN_TEST(complexity_csharp_pass_registration_is_linear);
RUN_TEST(complexity_kotlin_class_registration_is_linear);
RUN_TEST(complexity_java_class_registration_is_linear);
RUN_TEST(complexity_ts_proof_callee_imports_are_linear);
RUN_TEST(complexity_ts_proof_annotation_imports_are_linear);
RUN_TEST(complexity_ts_proof_narrowing_lookup_is_linear);
RUN_TEST(complexity_rust_source_facts_on_unclosed_nesting_are_linear);
RUN_TEST(complexity_kotlin_cross_on_unclosed_nesting_is_linear);
RUN_TEST(complexity_c_cross_on_unclosed_nesting_is_linear);
RUN_TEST(complexity_cpp_cross_on_unclosed_nesting_is_linear);
RUN_TEST(complexity_c_shared_cross_on_unclosed_nesting_is_linear);
RUN_TEST(complexity_js_proof_on_unclosed_nesting_is_linear);
RUN_TEST(complexity_dart_usages_on_unclosed_nesting_is_linear);
RUN_TEST(complexity_usage_classifiers_on_deep_nesting_are_linear);
RUN_TEST(complexity_decorator_lookup_is_linear);
RUN_TEST(complexity_ts_class_registration_is_linear);
RUN_TEST(complexity_rust_derive_registration_is_linear);
RUN_TEST(complexity_go_generic_registration_is_linear);
RUN_TEST(complexity_csharp_namespace_lookup_is_linear);
RUN_TEST(complexity_registry_tail_answers_as_a_scan_does);
RUN_TEST(complexity_kotlin_pass_registry_lookups_are_linear);
RUN_TEST(complexity_doc_lookup_keeps_a_wide_root_cached);
RUN_TEST(complexity_struct_field_fold_is_linear);
RUN_TEST(complexity_php_doc_lookup_is_linear);
RUN_TEST(complexity_php_short_name_fallback_is_linear);
RUN_TEST(complexity_python_pass_import_join_is_linear);
RUN_TEST(complexity_python_pass_wide_from_import_is_linear);
RUN_TEST(complexity_flat_file_syntax_walk_is_linear);
RUN_TEST(complexity_oo_local_lookup_is_linear);
RUN_TEST(complexity_oo_explicit_imports_are_linear);
RUN_TEST(complexity_resolver_counters_are_run_scoped);
RUN_TEST(complexity_py_module_ranges_preserve_calls_with_bounded_work);
RUN_TEST(complexity_rescript_error_recovery_lexing_is_linear);
RUN_TEST(complexity_replicated_modules_scale_linearly);
RUN_TEST(complexity_perfile_registry_work_is_linear);
RUN_TEST(complexity_infra_resolution_is_linear);
RUN_TEST(complexity_importance_scoring_is_linear);
RUN_TEST(complexity_shared_package_growth_stays_linear);
RUN_TEST(complexity_py_submodule_probe_avoids_registry_scan);
RUN_TEST(complexity_throughput_report_written);
}