/* * repro_invariant_breadth.c -- Cross-language CALLS callable-sourcing invariant. * * INVARIANT (gap #6, QUALITY_ANALYSIS.md): * For every language, a function call written INSIDE a function body must * produce a CALLS edge whose source node carries label "Function" or "Method" * (i.e. callable-sourced). It must NOT be sourced at a "Module" node. * Calls at the top level of a file may legitimately be Module-sourced; only * in-body calls are asserted here. * * QUALITY_ANALYSIS.md gap #6 reports 27 languages failing this. This file * is the "large breadth table" — one per-language case, table-driven, asserting * the invariant across 26 languages. * * Fixture design rule: * Each fixture defines exactly TWO functions: a callee (helper) and a caller * (run) that calls helper strictly INSIDE its body. There are NO top-level * calls in any fixture. This means ANY Module-sourced CALLS edge is a * direct violation of the invariant. * * Expected RED/GREEN split (as of QUALITY_ANALYSIS.md, 2026-06-24): * GREEN (already correctly callable-sourced, regression guards): * elixir, ocaml, fortran, pascal, cuda, d, glsl, hlsl, ispc, * odin, slang, squirrel, vimscript, cairo (14 cases) * * RED (module-sourced or no CALLS at all -- reproduces the gap): * r, julia, dart, groovy, commonlisp, powershell, ada, clojure, * fsharp, racket, rescript, scheme (12 cases) * * Note: the "suspicious" group (r, julia, ...) from QUALITY_ANALYSIS may be * GREEN because the calls-breadth table (test_lang_contract.c) already shows * expect_calls=true for most. The module-sourcing assertion is STRICTER: a * language can produce a CALLS edge (calls >= 1) but still fail here if the * edge is sourced at Module rather than Function. Individual case comments * explain the known failure mode where root-caused. * * How to read results: * PASS -- callable-sourced (Function/Method), no Module-sourced in-body calls. * If currently GREEN: regression guard -- a future grammar/pipeline * change that breaks sourcing will turn it RED. * If currently RED: the bug is confirmed reproduced; fix the * enclosing-function detection for this language. */ #include "test_framework.h" #include "repro_harness.h" #include #include #include /* ---- helper: count CALLS edges by source-node label --------------------- */ static int ib_calls_from_label(cbm_store_t *store, const char *project, const char *label) { cbm_edge_t *edges = NULL; int edge_count = 0; if (cbm_store_find_edges_by_type(store, project, "CALLS", &edges, &edge_count) != CBM_STORE_OK) { return -1; } int total = 0; for (int i = 0; i < edge_count; i++) { cbm_node_t src = {0}; if (cbm_store_find_node_by_id(store, edges[i].source_id, &src) != CBM_STORE_OK) { continue; } if (src.label && strcmp(src.label, label) == 0) { total++; } cbm_node_free_fields(&src); } cbm_store_free_edges(edges, edge_count); return total; } static int ib_callable_calls(cbm_store_t *store, const char *project) { int fn = ib_calls_from_label(store, project, "Function"); int mt = ib_calls_from_label(store, project, "Method"); if (fn < 0 || mt < 0) { return -1; } return fn + mt; } static int ib_module_calls(cbm_store_t *store, const char *project) { return ib_calls_from_label(store, project, "Module"); } /* ---- per-case result struct --------------------------------------------- */ typedef struct { int ok; /* graph DB opened */ int calls; /* total CALLS edges */ int callable_calls; /* CALLS sourced at Function or Method */ int module_calls; /* CALLS sourced at Module */ } IBMetrics; static IBMetrics ib_metrics(const char *filename, const char *content) { RProj lp; cbm_store_t *store = rh_index(&lp, filename, content); IBMetrics m = {0}; if (store) { m.ok = 1; m.calls = rh_count_edges(store, lp.project, "CALLS"); m.callable_calls = ib_callable_calls(store, lp.project); m.module_calls = ib_module_calls(store, lp.project); } rh_cleanup(&lp, store); return m; } /* ---- breadth case table ------------------------------------------------- */ typedef struct { const char *lang; /* human-readable language name */ const char *filename; /* fixture filename (extension selects grammar) */ const char *src; /* fixture source — caller inside a function body only */ int expect_callable; /* 1: calls should be callable-sourced (GREEN target) */ const char *gap_note; /* root cause for known gaps (NULL if expected GREEN) */ } IBCase; /* * Fixture rule: helper() is the callee; run() is the caller. * The call to helper() is strictly inside the body of run(). * No top-level calls anywhere in the fixture. */ static const IBCase IB_CASES[] = { /* ------------------------------------------------------------------ */ /* SUSPICIOUS / LIKELY-BROKEN GROUP */ /* QUALITY_ANALYSIS lists these as "expected-true but suspicious". */ /* They have expect_calls=true in the calls-breadth table, meaning a */ /* CALLS edge is produced -- but it may still be Module-sourced. */ /* ------------------------------------------------------------------ */ { "r", "a.R", "helper <- function(x) {\n" " x * 2\n" "}\n" "\n" "run <- function() {\n" " helper(21)\n" "}\n", /* * R: extract_calls.c has an R branch that reads the callee from the * call node's first child. However, enclosing-function detection * for R may fall back to Module if func_kinds_for_lang does not * include R's "function_definition" node type. RED when the CALLS * edge is sourced at Module instead of the "run" Function node. */ 0, "R enclosing-function detection likely missing from func_kinds_for_lang; " "call may be sourced at Module" }, { "julia", "a.jl", "function helper(x)\n" " return x + 1\n" "end\n" "\n" "function run(n)\n" " return helper(n)\n" "end\n", /* * Julia: same issue -- function body extraction may not detect the * enclosing Julia function node correctly, sourcing the call at Module. */ 0, "Julia enclosing-function detection may not map function_definition to " "a callable QN; call sourced at Module" }, /* ------------------------------------------------------------------ */ /* EXPECTED-GREEN GROUP (regression guards) */ /* These languages have correct callable-sourcing in the current build.*/ /* A regression that breaks enclosing-function detection for any of */ /* them will turn the corresponding case RED. */ /* ------------------------------------------------------------------ */ { "elixir", "a.ex", "defmodule Sample do\n" " def helper(x) do\n" " x + 1\n" " end\n" "\n" " def run do\n" " helper(41)\n" " end\n" "end\n", 1, NULL }, { "ocaml", "a.ml", "let helper x = x + 1\n" "\n" "let run () =\n" " let result = helper 41 in\n" " print_int result\n", 1, NULL }, { "fortran", "a.f90", "function helper(x) result(y)\n" " integer, intent(in) :: x\n" " integer :: y\n" " y = x + 1\n" "end function helper\n" "\n" "function run(n) result(total)\n" " integer, intent(in) :: n\n" " integer :: total\n" " total = helper(n) + helper(n + 1)\n" "end function run\n", 1, NULL }, { "pascal", "a.pas", "procedure Helper(x: Integer);\n" "begin\n" " WriteLn(x);\n" "end;\n" "\n" "procedure Run;\n" "begin\n" " Helper(1);\n" "end;\n", 1, NULL }, { "cuda", "a.cu", "__device__ int helper(int x) {\n" " return x * 2;\n" "}\n" "\n" "__global__ void run(int *out) {\n" " out[0] = helper(21);\n" "}\n", 1, NULL }, { "d", "a.d", "int helper(int x)\n" "{\n" " return x + 1;\n" "}\n" "\n" "void run()\n" "{\n" " int y = helper(41);\n" "}\n", 1, NULL }, { "glsl", "a.glsl", "float helper(float x) {\n" " return x * 2.0;\n" "}\n" "\n" "void run() {\n" " float y = helper(3.0);\n" "}\n", 1, NULL }, { "hlsl", "a.hlsl", "float helper(float x)\n" "{\n" " return x * 2.0;\n" "}\n" "\n" "float run(float v)\n" "{\n" " return helper(v) + 1.0;\n" "}\n", 1, NULL }, { "ispc", "a.ispc", "static inline uniform float helper(uniform float x) {\n" " return x * 2.0f;\n" "}\n" "\n" "export void run(uniform float in[], uniform float out[],\n" " uniform int n) {\n" " foreach (i = 0 ... n) {\n" " out[i] = helper(in[i]);\n" " }\n" "}\n", 1, NULL }, { "odin", "a.odin", "package fixture\n" "\n" "helper :: proc() -> int {\n" "\treturn 42\n" "}\n" "\n" "run :: proc() {\n" "\tx := helper()\n" "\t_ = x\n" "}\n", 1, NULL }, { "slang", "a.slang", "void helper()\n" "{\n" " int x = 1;\n" "}\n" "\n" "void run()\n" "{\n" " helper();\n" "}\n", 1, NULL }, { "squirrel", "a.nut", "function helper(x) {\n" " return x + 1;\n" "}\n" "\n" "function run() {\n" " return helper(41);\n" "}\n", 1, NULL }, { "vimscript", "a.vim", "function! Helper() abort\n" " return 1\n" "endfunction\n" "\n" "function! Run() abort\n" " call Helper()\n" "endfunction\n", 1, NULL }, { "cairo", "a.cairo", "fn helper(x: felt252) -> felt252 {\n" " x + 1\n" "}\n" "\n" "fn run() -> felt252 {\n" " helper(41)\n" "}\n", 1, NULL }, /* ------------------------------------------------------------------ */ /* KNOWN-GAP GROUP */ /* These languages fail in the existing calls-breadth contract too */ /* (expect_calls=false in test_lang_contract.c CALL_CASES). */ /* The primary gap is callee extraction; callable-sourcing cannot be */ /* verified until a CALLS edge exists. Both invariants are asserted: */ /* calls >= 1 AND module_calls == 0. */ /* ------------------------------------------------------------------ */ { "dart", "a.dart", "void helper() {\n" " print('helper');\n" "}\n" "\n" "void run() {\n" " helper();\n" "}\n", /* * Dart: selector call node carries no callee field and the first child * is not an identifier; no dart branch in extract_calls.c. No CALLS * edge is produced at all, so callable-sourcing cannot be tested * independently. Both gaps (no CALLS + callable-sourcing) are RED. */ 0, "selector call node: no callee field, first child not identifier; " "no dart branch in extract_calls.c" }, { "groovy", "a.groovy", "def helper() {\n" " println 'helping'\n" "}\n" "\n" "def run() {\n" " helper()\n" "}\n", /* * Groovy: function_call callee not on a function/name field and first * child is not 'identifier'; no groovy branch in extract_calls.c. */ 0, "function_call callee not on function/name field; " "first child is not identifier; no groovy branch in extract_calls.c" }, { "commonlisp", "a.lisp", "(defun helper (x)\n" " (* x 2))\n" "\n" "(defun run ()\n" " (helper 21))\n", /* * Common Lisp: list_lit call head is sym_lit not identifier; * no commonlisp branch in extract_callee_name. */ 0, "list_lit call head is sym_lit not identifier; " "no commonlisp branch in extract_callee_name" }, { "powershell", "a.ps1", "function helper {\n" " Write-Output 'hi'\n" "}\n" "\n" "function run {\n" " helper\n" "}\n", /* * PowerShell: command node child is command_name not identifier; * extract_scripting_callee handles MATLAB not PowerShell. */ 0, "command node child is command_name not identifier; " "extract_scripting_callee handles MATLAB not PowerShell" }, { "ada", "a.adb", "procedure Run is\n" " procedure Helper is\n" " begin\n" " null;\n" " end Helper;\n" "begin\n" " Helper;\n" "end Run;\n", /* * Ada: procedure_call_statement callee did not resolve to a CALLS edge; * no Ada branch in extract_calls.c. */ 0, "procedure_call_statement callee not resolved; " "no Ada branch in extract_calls.c" }, { "clojure", "a.clj", "(defn helper [] 42)\n" "\n" "(defn run [] (helper))\n", /* * Clojure: lisp call is a list_lit whose head is a sym_lit (not a * field, not a first-child 'identifier'); no lisp branch in * extract_callee_name. */ 0, "list_lit head is sym_lit not identifier; " "no lisp/clojure branch in extract_callee_name" }, { "fsharp", "a.fs", "let helper x = x + 1\n" "\n" "let run () = helper 41\n", /* * F#: application_expression callee head is a long_identifier_or_op * wrapper, not a bare identifier/field; no fsharp callee branch. */ 0, "application_expression callee head is long_identifier_or_op wrapper; " "no fsharp callee branch in extract_callee_name" }, { "racket", "a.rkt", "#lang racket\n" "\n" "(define (helper x)\n" " (+ x 1))\n" "\n" "(define (run)\n" " (helper 41))\n", /* * Racket: lisp call is a 'list' whose head is a 'symbol' (grammar has * no 'identifier' node); no racket branch in extract_callee_name. */ 0, "list head is symbol not identifier; " "no racket branch in extract_callee_name" }, { "rescript", "a.res", "let helper = (x) => x + 1\n" "\n" "let run = () => helper(41)\n", /* * ReScript: call_expression 'function' field is a 'value_identifier' * (not in extract_callee_from_fields' accepted type list). */ 0, "call_expression function field is value_identifier; " "not in extract_callee_from_fields accepted type list" }, { "scheme", "a.scm", "(define (helper x)\n" " (* x 2))\n" "\n" "(define (run)\n" " (helper 21))\n", /* * Scheme: lisp call is a 'list' whose head is a 'symbol'; * no scheme branch in extract_callee_name. */ 0, "list head is symbol not identifier; " "no scheme branch in extract_callee_name" }, }; enum { IB_CASES_COUNT = (int)(sizeof(IB_CASES) / sizeof(IB_CASES[0])) }; /* ---- single table-driven test ------------------------------------------- */ /* * repro_invariant_breadth_callable_sourcing * * Iterates every case in IB_CASES. For each language: * 1. Indexes the single-file fixture through the full production pipeline. * 2. Counts CALLS edges and their source-node labels. * 3. Asserts: * a. store opened (pipeline did not crash hard) * b. calls >= 1 (the call was detected at all) * c. callable_calls >= 1 (at least one CALLS edge is Function/Method-sourced) * d. module_calls == 0 (no CALLS edge is Module-sourced for an in-body call) * * For expect_callable=0 cases (known gaps), the test still asserts all four * conditions -- so those cases are RED (that IS the deliverable: a confirmed, * reproducible, durable bug registration for each gap language). * * For expect_callable=1 cases (regression guards), the test must PASS. * A future grammar or pipeline regression that breaks callable-sourcing for * a GREEN language will immediately turn it RED here. */ TEST(repro_invariant_breadth_callable_sourcing) { int failures = 0; for (int i = 0; i < IB_CASES_COUNT; i++) { const IBCase *c = &IB_CASES[i]; IBMetrics m = ib_metrics(c->filename, c->src); int pass = (m.ok && m.calls >= 1 && m.callable_calls >= 1 && m.module_calls == 0); if (!pass) { fprintf(stderr, " [INV-BREADTH] FAIL %-12s ok=%d calls=%d " "callable=%d module=%d%s%s\n", c->lang, m.ok, m.calls, m.callable_calls, m.module_calls, c->gap_note ? " -- " : "", c->gap_note ? c->gap_note : ""); failures++; } else { fprintf(stderr, " [INV-BREADTH] PASS %-12s calls=%d callable=%d " "module=%d\n", c->lang, m.calls, m.callable_calls, m.module_calls); } } fprintf(stderr, " [INV-BREADTH] %d langs checked: %d FAILURES " "(each = callable-sourcing invariant violated or no CALLS at all)\n", IB_CASES_COUNT, failures); ASSERT_EQ(failures, 0); PASS(); } /* ---- suite --------------------------------------------------------------- */ SUITE(repro_invariant_breadth) { RUN_TEST(repro_invariant_breadth_callable_sourcing); }