Files
wehub-resource-sync 41cb1c0170
OpenSSF Scorecard / scorecard (push) Failing after 0s
DCO / dco (push) Failing after 0s
CodeQL SAST / analyze (push) Failing after 1s
Deploy Pages / deploy (push) Failing after 1s
chore: import upstream snapshot with attribution
2026-07-13 12:28:05 +08:00

1006 lines
45 KiB
C

/*
* repro_grammar_shells.c -- Per-grammar INVARIANT battery for the
* SHELLS / SCRIPTING / MISC (asm-ish + data-ish) language family.
*
* One TEST() per language so per-language RED/GREEN shows on the bug-repro
* board. Each test runs a battery adapted to what the language actually models,
* read directly from internal/cbm/lang_specs.c (the func/class/field/call type
* arrays per CBM_LANG_*). The dimensions applied per language are documented in
* the per-TEST comment.
*
* Languages covered (19) and the CBM_LANG_* enum each uses (all verified present
* in internal/cbm/cbm.h):
* BASH -> CBM_LANG_BASH (callable: func + call)
* ZSH -> CBM_LANG_ZSH (callable: func + call)
* FISH -> CBM_LANG_FISH (callable: func + call)
* POWERSHELL -> CBM_LANG_POWERSHELL (callable: func + class + call)
* TCL -> CBM_LANG_TCL (callable: func + class + call)
* AWK -> CBM_LANG_AWK (callable: func + call)
* VIMSCRIPT -> CBM_LANG_VIMSCRIPT (callable: func + call)
* FENNEL -> CBM_LANG_FENNEL (callable: func + call, lisp)
* NIX -> CBM_LANG_NIX (callable: func + call)
* GDSCRIPT -> CBM_LANG_GDSCRIPT (callable: func + class + call)
* LUAU -> CBM_LANG_LUAU (callable: func + class + call)
* TEAL -> CBM_LANG_TEAL (callable: func + class + call)
* LLVM_IR -> CBM_LANG_LLVM_IR (callable: func + call)
* NASM -> CBM_LANG_NASM (callable: func(label) + call)
* JANET -> CBM_LANG_JANET (STRUCTURAL ONLY: spec has only module_types)
* SMALI -> CBM_LANG_SMALI (structural-with-defs: func/class/field, NO calls)
* DEVICETREE -> CBM_LANG_DEVICETREE (structural: call_types but NO func anchor)
* KCONFIG -> CBM_LANG_KCONFIG (structural-with-defs: class_types, NO calls)
* HYPRLANG -> CBM_LANG_HYPRLANG (pure structural: only module_types)
*
* No language in this set was skipped; every CBM_LANG_* above is defined in cbm.h.
*
* SPEC-DRIVEN CLASSIFICATION (from internal/cbm/lang_specs.c)
* ----------------------------------------------------------
* CALLABLES (func_types AND call_types both non-empty -> full battery + pipeline):
* BASH func=function_definition call=command
* ZSH func=function_definition call=command,call_expression
* FISH func=function_definition call=command
* POWERSHELL func=function_statement call=invokation_expression,command class=class_statement,...
* TCL func=procedure call=command class=namespace
* AWK func=func_def,rule call=func_call,command
* VIMSCRIPT func=function_definition,... call=call_expression,call,command
* FENNEL func=fn,lambda,hashfn call=list (lisp head symbol)
* NIX func=function_expression call=apply_expression
* GDSCRIPT func=function_definition,... call=call,attribute_call,base_call class=class_definition,...
* LUAU func=function_declaration,function_definition call=function_call class=type_definition
* TEAL func=function_statement,anon_function,... call=function_call class=record_declaration,...
* LLVM_IR func=function_header call=call,invoke var=local_var,global_var
* NASM func=label,preproc_def,preproc_multiline_macro call=call_syntax_expression class=struc_declaration
*
* STRUCTURAL-WITH-DEFS (defs but NO call_types -> dims 1-5 + R):
* SMALI func=method_definition -> "Function" class=class_definition -> "Class" field=field_definition -> "Field" (call_types EMPTY)
* KCONFIG class=config,menuconfig,choice,type_definition -> "Class" (func/call EMPTY)
*
* STRUCTURAL ONLY (no extractable defs from the spec -> dims 1-4 + R):
* JANET ONLY module_types=source; func/class/field/call all empty_types.
* DEVICETREE call_types=call_expression but func_types EMPTY -> no Function anchor,
* and no class/var defs; treat as structural (extract-clean + invariants).
* HYPRLANG ONLY module_types=source_file; everything else empty_types.
*
* BATTERY DIMENSIONS (identical semantics to repro_grammar_core.c /
* repro_grammar_config.c -- shared helpers reused via repro_invariant_lib.h):
* SINGLE-FILE (cbm_extract_file):
* 1. extract-clean : inv_extract_clean == 1 (non-NULL, has_error unset).
* 2. labels-valid : inv_count_bad_labels == 0.
* 3. fqn-wellformed : inv_count_bad_fqns == 0.
* 4. ranges-valid : inv_count_bad_ranges == 0.
* 5. defs-present : expected label extracted (callables + structural-with-defs).
* 6. calls-extracted : inv_has_call(callee) == 1 (callables only).
* FULL-PIPELINE (rh_index_files):
* 7. callable-sourcing : inv_count_calls_by_source mod == 0 AND callable >= 1
* (callables only).
* 8. no-dangling : inv_count_dangling_edges("CALLS") == 0 (with dim 7).
* ROBUSTNESS (every language):
* R. extract-on-malformed: cbm_extract_file on a truncated/broken fixture must
* RETURN non-NULL (has_error may be set). A NULL return means the extractor
* crashed/aborted on bad input -- a RED robustness bug.
*
* KNOWN GAP -> dim-7 RED PREDICTIONS (the point of this file).
* The enclosing-func walk cbm_find_enclosing_func() uses func_kinds_for_lang()
* in internal/cbm/helpers.c. In that switch ONLY CBM_LANG_BASH has a dedicated
* kind list (func_kinds_bash = {"function_definition"}); every other language in
* this set falls through to func_kinds_generic =
* {"function_declaration","function_definition","method_declaration","method_definition"}.
* So a call's enclosing Function node is found ONLY when the grammar's func node
* type is one of those generic kinds. Cross-referencing each callable's func node
* type (from lang_specs.c) against that generic set:
* MATCHES generic (dim 7 has a chance to be GREEN if calls extract + attribute):
* ZSH/FISH (function_definition), VIMSCRIPT (function_definition),
* GDSCRIPT (function_definition), LUAU (function_declaration/function_definition).
* BASH matches via func_kinds_bash.
* DOES NOT MATCH generic (enclosing-func walk returns null -> Module-sourced ->
* dim 7 RED expected):
* POWERSHELL (function_statement), TCL (procedure), AWK (func_def/rule),
* FENNEL (fn/lambda/hashfn), NIX (function_expression),
* TEAL (function_statement/anon_function/...), LLVM_IR (function_header),
* NASM (label/...).
* Dim 6 (calls-extracted) is itself uncertain for several command-style grammars
* (bash/zsh/fish/awk/tcl `command` nodes, nix apply_expression, llvm call/invoke,
* nasm call_syntax_expression): the callee-name resolver in extract_calls.c has a
* dedicated path only for PowerShell `command` and lisp `list`; the others rely on
* generic field/first-child resolution and may yield no callee_name -> dim 6 RED.
* Where dim 6 REDs, dim 7 also REDs (0 CALLS edges to attribute). These RED rows
* ARE the deliverable -- they document precisely which shells/scripting grammars
* lose call edges or mis-source them at the Module node.
*
* NOTE: these RED/GREEN labels are static-analysis PREDICTIONS from the spec +
* helpers source; the suite records the real outcome when run. Be honest: a row
* that flips from the predicted color is itself a finding.
*
* Coding rule: inline comments are line comments only (no block comments inside
* block comments).
*/
#include "test_framework.h"
#include "repro_invariant_lib.h"
#include <store/store.h>
#include <stdio.h>
#include <string.h>
/* ── Shared single-file battery: structural base (dims 1-4) ─────────────────
*
* Four core invariants on valid input, no defs/calls assertions. Used for the
* structural-only languages (JANET, DEVICETREE, HYPRLANG). Returns 0 on PASS.
*/
static int sh_base_battery(const char *lang_tag, const char *src, CBMLanguage lang,
const char *file) {
const char *RED = tf_red();
const char *RST = tf_reset();
/* 1. extract-clean */
if (inv_extract_clean(src, lang, file) != 1) {
printf(" %sFAIL%s [%s] extract-clean: NULL result or has_error set\n",
RED, RST, lang_tag);
return 1;
}
CBMFileResult *r = inv_rx(src, lang, file);
if (!r) {
printf(" %sFAIL%s [%s] inv_rx returned NULL after clean extract\n",
RED, RST, lang_tag);
return 1;
}
int fails = 0;
/* 2. labels-valid */
int bad_labels = inv_count_bad_labels(r);
if (bad_labels != 0) {
printf(" %sFAIL%s [%s] labels-valid: %d def(s) with invalid label\n",
RED, RST, lang_tag, bad_labels);
fails++;
}
/* 3. fqn-wellformed */
int bad_fqns = inv_count_bad_fqns(r);
if (bad_fqns != 0) {
printf(" %sFAIL%s [%s] fqn-wellformed: %d def(s) with malformed QN\n",
RED, RST, lang_tag, bad_fqns);
fails++;
}
/* 4. ranges-valid */
int bad_ranges = inv_count_bad_ranges(r);
if (bad_ranges != 0) {
printf(" %sFAIL%s [%s] ranges-valid: %d def(s) with invalid range\n",
RED, RST, lang_tag, bad_ranges);
fails++;
}
cbm_free_result(r);
return fails ? 1 : 0;
}
/* ── Shared single-file battery: structural with defs (dims 1-5) ────────────
*
* Adds defs-present for the structural-with-defs languages (SMALI, KCONFIG).
* Pass NULL for expect_label2/expect_label3 when fewer labels are needed.
* Returns 0 on PASS.
*/
static int sh_struct_battery(const char *lang_tag, const char *src, CBMLanguage lang,
const char *file, const char *expect_label,
const char *expect_label2, const char *expect_label3) {
const char *RED = tf_red();
const char *RST = tf_reset();
if (inv_extract_clean(src, lang, file) != 1) {
printf(" %sFAIL%s [%s] extract-clean: NULL result or has_error set\n",
RED, RST, lang_tag);
return 1;
}
CBMFileResult *r = inv_rx(src, lang, file);
if (!r) {
printf(" %sFAIL%s [%s] inv_rx returned NULL after clean extract\n",
RED, RST, lang_tag);
return 1;
}
int fails = 0;
int bad_labels = inv_count_bad_labels(r);
if (bad_labels != 0) {
printf(" %sFAIL%s [%s] labels-valid: %d def(s) with invalid label\n",
RED, RST, lang_tag, bad_labels);
fails++;
}
int bad_fqns = inv_count_bad_fqns(r);
if (bad_fqns != 0) {
printf(" %sFAIL%s [%s] fqn-wellformed: %d def(s) with malformed QN\n",
RED, RST, lang_tag, bad_fqns);
fails++;
}
int bad_ranges = inv_count_bad_ranges(r);
if (bad_ranges != 0) {
printf(" %sFAIL%s [%s] ranges-valid: %d def(s) with invalid range\n",
RED, RST, lang_tag, bad_ranges);
fails++;
}
/* 5. defs-present (up to three expected labels) */
if (expect_label && inv_count_label(r, expect_label) < 1) {
printf(" %sFAIL%s [%s] defs-present: no def labelled \"%s\"\n",
RED, RST, lang_tag, expect_label);
fails++;
}
if (expect_label2 && inv_count_label(r, expect_label2) < 1) {
printf(" %sFAIL%s [%s] defs-present: no def labelled \"%s\"\n",
RED, RST, lang_tag, expect_label2);
fails++;
}
if (expect_label3 && inv_count_label(r, expect_label3) < 1) {
printf(" %sFAIL%s [%s] defs-present: no def labelled \"%s\"\n",
RED, RST, lang_tag, expect_label3);
fails++;
}
cbm_free_result(r);
return fails ? 1 : 0;
}
/* ── Shared single-file battery: callable (dims 1-6) ────────────────────────
*
* Adds defs-present (dim 5) and calls-extracted (dim 6) on top of the base
* invariants. Used for the callable shells/scripting languages. Pass NULL for
* expect_label when no def label is asserted alongside the call. Returns 0 on PASS.
*/
static int sh_callable_battery(const char *lang_tag, const char *src, CBMLanguage lang,
const char *file, const char *expect_label,
const char *expect_label2, const char *callee) {
const char *RED = tf_red();
const char *RST = tf_reset();
if (inv_extract_clean(src, lang, file) != 1) {
printf(" %sFAIL%s [%s] extract-clean: NULL result or has_error set\n",
RED, RST, lang_tag);
return 1;
}
CBMFileResult *r = inv_rx(src, lang, file);
if (!r) {
printf(" %sFAIL%s [%s] inv_rx returned NULL after clean extract\n",
RED, RST, lang_tag);
return 1;
}
int fails = 0;
int bad_labels = inv_count_bad_labels(r);
if (bad_labels != 0) {
printf(" %sFAIL%s [%s] labels-valid: %d def(s) with invalid label\n",
RED, RST, lang_tag, bad_labels);
fails++;
}
int bad_fqns = inv_count_bad_fqns(r);
if (bad_fqns != 0) {
printf(" %sFAIL%s [%s] fqn-wellformed: %d def(s) with malformed QN\n",
RED, RST, lang_tag, bad_fqns);
fails++;
}
int bad_ranges = inv_count_bad_ranges(r);
if (bad_ranges != 0) {
printf(" %sFAIL%s [%s] ranges-valid: %d def(s) with invalid range\n",
RED, RST, lang_tag, bad_ranges);
fails++;
}
/* 5. defs-present */
if (expect_label && inv_count_label(r, expect_label) < 1) {
printf(" %sFAIL%s [%s] defs-present: no def labelled \"%s\"\n",
RED, RST, lang_tag, expect_label);
fails++;
}
if (expect_label2 && inv_count_label(r, expect_label2) < 1) {
printf(" %sFAIL%s [%s] defs-present: no def labelled \"%s\"\n",
RED, RST, lang_tag, expect_label2);
fails++;
}
/* 6. calls-extracted */
if (callee && inv_has_call(r, callee) != 1) {
printf(" %sFAIL%s [%s] calls-extracted: no call to \"%s\" found\n",
RED, RST, lang_tag, callee);
fails++;
}
cbm_free_result(r);
return fails ? 1 : 0;
}
/* ── Shared full-pipeline battery (dims 7-8) ────────────────────────────────
*
* Indexes the single-file fixture through the production pipeline and asserts
* callable-sourcing (no Module-sourced in-body CALLS, and >= 1 callable-sourced
* so a fixture with zero CALLS edges cannot vacuously pass) plus no dangling
* CALLS endpoints. Used for the callable languages. Dim 7 is RED for the
* languages whose func node type is not in func_kinds_generic (see file header).
* Returns 0 on PASS.
*/
static int sh_pipeline_battery(const char *lang_tag, const char *filename, const char *src) {
const char *RED = tf_red();
const char *RST = tf_reset();
RFile files[1];
files[0].name = filename;
files[0].content = src;
RProj lp;
cbm_store_t *store = rh_index_files(&lp, files, 1);
if (!store) {
printf(" %sFAIL%s [%s] pipeline: rh_index_files returned NULL\n",
RED, RST, lang_tag);
return 1;
}
int fails = 0;
/* 7. callable-sourcing */
int module_sourced = 0;
int callable_sourced = 0;
inv_count_calls_by_source(store, lp.project, &module_sourced, &callable_sourced);
if (module_sourced != 0) {
printf(" %sFAIL%s [%s] callable-sourcing: %d in-body CALLS sourced at "
"Module (callable=%d) -- enclosing-func gap (func_kinds_for_lang "
"lacks this grammar's func node type)\n",
RED, RST, lang_tag, module_sourced, callable_sourced);
fails++;
} else if (callable_sourced < 1) {
printf(" %sFAIL%s [%s] callable-sourcing: 0 CALLS edges (fixture "
"produced no in-body call edge to attribute)\n",
RED, RST, lang_tag);
fails++;
}
/* 8. no-dangling */
int dangling = inv_count_dangling_edges(store, lp.project, "CALLS");
if (dangling != 0) {
printf(" %sFAIL%s [%s] no-dangling: %d dangling CALLS endpoint(s)\n",
RED, RST, lang_tag, dangling);
fails++;
}
rh_cleanup(&lp, store);
return fails ? 1 : 0;
}
/* ── Robustness helper: assert call RETURNS on malformed input ──────────────
*
* A truncated version of the fixture is passed through cbm_extract_file.
* has_error may be set (1) but the call must return non-NULL. A NULL return
* means the extractor crashed or aborted on bad input -- a RED robustness bug.
* Returns 0 on PASS.
*/
static int sh_robustness(const char *lang_tag, const char *bad_src, CBMLanguage lang,
const char *file) {
const char *RED = tf_red();
const char *RST = tf_reset();
CBMFileResult *r =
cbm_extract_file(bad_src, (int)strlen(bad_src), lang, "t", file, 0, NULL, NULL);
if (!r) {
printf(" %sFAIL%s [%s] robustness: extractor returned NULL on malformed input\n",
RED, RST, lang_tag);
return 1;
}
cbm_free_result(r);
return 0;
}
/* ── BASH ─────────────────────────────────────────────────────────────────────
* Idiomatic: two function definitions, the callee invoked strictly inside the
* caller body. spec: func=function_definition, call=command. BASH is the only
* shell with a dedicated func_kinds_bash list, so the enclosing-func walk can
* match the function_definition node.
*
* Dims asserted: 1-8 + R. Dim 5 = "Function". Dim 6 callee = "compute_inner".
* Dim 7 has a chance to be GREEN (func_kinds_bash matches function_definition) IF
* the `command` callee resolves and the CALLS edge is produced; if command-node
* callee resolution yields no name, dims 6+7 RED.
*/
TEST(repro_grammar_shells_bash) {
static const char src[] =
"#!/usr/bin/env bash\n"
"\n"
"compute_inner() {\n"
" echo $(( $1 + 1 ))\n"
"}\n"
"\n"
"compute_outer() {\n"
" compute_inner \"$1\"\n"
"}\n";
static const char bad[] = "compute_outer() {\n compute_inner \"$1\"";
if (sh_callable_battery("BASH", src, CBM_LANG_BASH, "run.sh",
"Function", NULL, "compute_inner") != 0)
return 1;
if (sh_robustness("BASH", bad, CBM_LANG_BASH, "run.sh") != 0)
return 1;
return sh_pipeline_battery("BASH", "run.sh", src);
}
/* ── ZSH ──────────────────────────────────────────────────────────────────────
* Idiomatic: two zsh functions, callee inside caller body. spec:
* func=function_definition, call=command,call_expression. function_definition is
* in func_kinds_generic, so the enclosing-func walk can match.
*
* Dims asserted: 1-8 + R. Dim 5 = "Function". Dim 6 callee = "inner_fn".
* Dim 7 may be GREEN (function_definition matches generic) IF command callee
* resolves; else 6+7 RED.
*/
TEST(repro_grammar_shells_zsh) {
static const char src[] =
"inner_fn() {\n"
" print -- $(( $1 * 2 ))\n"
"}\n"
"\n"
"outer_fn() {\n"
" inner_fn \"$1\"\n"
"}\n";
static const char bad[] = "outer_fn() {\n inner_fn \"$1\"";
if (sh_callable_battery("ZSH", src, CBM_LANG_ZSH, "run.zsh",
"Function", NULL, "inner_fn") != 0)
return 1;
if (sh_robustness("ZSH", bad, CBM_LANG_ZSH, "run.zsh") != 0)
return 1;
return sh_pipeline_battery("ZSH", "run.zsh", src);
}
/* ── FISH ─────────────────────────────────────────────────────────────────────
* Idiomatic: two `function ... end` definitions, callee inside caller body.
* spec: func=function_definition, call=command. function_definition matches
* func_kinds_generic.
*
* Dims asserted: 1-8 + R. Dim 5 = "Function". Dim 6 callee = "inner_fn".
* Dim 7 may be GREEN IF command callee resolves; else 6+7 RED.
*/
TEST(repro_grammar_shells_fish) {
static const char src[] =
"function inner_fn\n"
" math $argv[1] x 2\n"
"end\n"
"\n"
"function outer_fn\n"
" inner_fn $argv[1]\n"
"end\n";
static const char bad[] = "function outer_fn\n inner_fn $argv[1]";
if (sh_callable_battery("FISH", src, CBM_LANG_FISH, "run.fish",
"Function", NULL, "inner_fn") != 0)
return 1;
if (sh_robustness("FISH", bad, CBM_LANG_FISH, "run.fish") != 0)
return 1;
return sh_pipeline_battery("FISH", "run.fish", src);
}
/* ── POWERSHELL ───────────────────────────────────────────────────────────────
* Idiomatic: two `function` statements, callee invoked inside the caller body.
* spec: func=function_statement, call=invokation_expression,command,
* class=class_statement,enum_statement,type_spec. PowerShell has a dedicated
* callee resolver (extract_powershell_callee: command_name child).
*
* Dims asserted: 1-8 + R. Dim 5 = "Function". Dim 6 callee = "Get-Inner".
* Dim 7 expected RED: func node type "function_statement" is NOT in
* func_kinds_generic -> enclosing-func walk returns null -> Module-sourced.
*/
TEST(repro_grammar_shells_powershell) {
static const char src[] =
"function Get-Inner {\n"
" param([int]$x)\n"
" return $x + 1\n"
"}\n"
"\n"
"function Get-Outer {\n"
" param([int]$x)\n"
" return Get-Inner -x $x\n"
"}\n";
static const char bad[] = "function Get-Outer {\n param([int]$x)\n return Get-Inner";
if (sh_callable_battery("PowerShell", src, CBM_LANG_POWERSHELL, "run.ps1",
"Function", NULL, "Get-Inner") != 0)
return 1;
if (sh_robustness("PowerShell", bad, CBM_LANG_POWERSHELL, "run.ps1") != 0)
return 1;
return sh_pipeline_battery("PowerShell", "run.ps1", src);
}
/* ── TCL ──────────────────────────────────────────────────────────────────────
* Idiomatic: two `proc` definitions, callee invoked inside caller body.
* spec: func=procedure, call=command, class=namespace.
*
* Dims asserted: 1-8 + R. Dim 5 = "Function" (procedure -> Function). Dim 6
* callee = "inner_proc".
* Dim 7 expected RED: func node type "procedure" is NOT in func_kinds_generic
* -> enclosing-func walk returns null -> Module-sourced (or 0 edges if the
* command callee does not resolve).
*/
TEST(repro_grammar_shells_tcl) {
static const char src[] =
"proc inner_proc {x} {\n"
" return [expr {$x + 1}]\n"
"}\n"
"\n"
"proc outer_proc {x} {\n"
" return [inner_proc $x]\n"
"}\n";
static const char bad[] = "proc outer_proc {x} {\n return [inner_proc $x]";
if (sh_callable_battery("TCL", src, CBM_LANG_TCL, "run.tcl",
"Function", NULL, "inner_proc") != 0)
return 1;
if (sh_robustness("TCL", bad, CBM_LANG_TCL, "run.tcl") != 0)
return 1;
return sh_pipeline_battery("TCL", "run.tcl", src);
}
/* ── AWK ──────────────────────────────────────────────────────────────────────
* Idiomatic: two user functions where one calls the other. spec: func=func_def,
* call=func_call,command.
*
* Dims asserted: 1-8 + R. Dim 5 = "Function" (func_def -> Function). Dim 6
* callee = "inner".
* Dim 7 (callable-sourcing): GREEN. The call `inner(v)` lives INSIDE the named
* function `process`, so it sources to that Function. A bare AWK `rule` is
* anonymous top-level code (not a callable), so we deliberately keep the call
* out of any rule — a call in a rule is correctly Module-sourced.
*/
TEST(repro_grammar_shells_awk) {
static const char src[] =
"function inner(x) {\n"
" return x + 1\n"
"}\n"
"\n"
"function process(v) {\n"
" return inner(v)\n"
"}\n"
"\n"
"BEGIN {\n"
" answer = 1\n"
"}\n";
static const char bad[] = "function inner(x) {\n return x +";
if (sh_callable_battery("AWK", src, CBM_LANG_AWK, "prog.awk",
"Function", NULL, "inner") != 0)
return 1;
if (sh_robustness("AWK", bad, CBM_LANG_AWK, "prog.awk") != 0)
return 1;
return sh_pipeline_battery("AWK", "prog.awk", src);
}
/* ── VIMSCRIPT ────────────────────────────────────────────────────────────────
* Idiomatic: two `function ... endfunction` definitions, callee inside caller
* body. spec: func=function_definition,function_declaration,..., call=
* call_expression,call,command. function_definition matches func_kinds_generic.
*
* Dims asserted: 1-8 + R. Dim 5 = "Function". Dim 6 callee = "Inner".
* Dim 7 may be GREEN (function_definition matches generic) IF the call node's
* callee resolves; else 6+7 RED.
*/
TEST(repro_grammar_shells_vimscript) {
static const char src[] =
"function! Inner(x)\n"
" return a:x + 1\n"
"endfunction\n"
"\n"
"function! Outer(x)\n"
" return Inner(a:x)\n"
"endfunction\n";
static const char bad[] = "function! Outer(x)\n return Inner(a:x)";
if (sh_callable_battery("VimScript", src, CBM_LANG_VIMSCRIPT, "plugin.vim",
"Function", NULL, "Inner") != 0)
return 1;
if (sh_robustness("VimScript", bad, CBM_LANG_VIMSCRIPT, "plugin.vim") != 0)
return 1;
return sh_pipeline_battery("VimScript", "plugin.vim", src);
}
/* ── FENNEL ───────────────────────────────────────────────────────────────────
* Idiomatic: two `fn` definitions, callee invoked inside caller body.
* spec: func=fn,lambda,hashfn, call=list. Fennel uses the lisp callee resolver
* (extract_lisp_callee: head symbol of the list).
*
* Dims asserted: 1-8 + R. Dim 5 = "Function" (fn -> Function). Dim 6 callee =
* "inner".
* Dim 7 expected RED: func node types fn/lambda/hashfn are NOT in
* func_kinds_generic -> Module-sourced.
*/
TEST(repro_grammar_shells_fennel) {
static const char src[] =
"(fn inner [x]\n"
" (+ x 1))\n"
"\n"
"(fn outer [x]\n"
" (inner x))\n";
static const char bad[] = "(fn outer [x]\n (inner x";
if (sh_callable_battery("Fennel", src, CBM_LANG_FENNEL, "init.fnl",
"Function", NULL, "inner") != 0)
return 1;
if (sh_robustness("Fennel", bad, CBM_LANG_FENNEL, "init.fnl") != 0)
return 1;
return sh_pipeline_battery("Fennel", "init.fnl", src);
}
/* ── NIX ──────────────────────────────────────────────────────────────────────
* Idiomatic: a let-binding lambda (function_expression) applied to an argument.
* spec: func=function_expression, call=apply_expression, var=binding. Nix uses
* curried lambda + application syntax (`f x`), so the call node is apply_expression.
*
* Dims asserted: 1-8 + R. Dim 5 = "Function" (function_expression -> Function).
* Dim 6 callee = "addOne" (the applied binding name).
* Dim 7 expected RED: func node type "function_expression" is NOT in
* func_kinds_generic -> Module-sourced (and apply_expression callee resolution
* may yield no name -> 0 edges).
*/
TEST(repro_grammar_shells_nix) {
/* DISABLED — RARE LANGUAGE (maintainer-approved, 2026-06-28): Nix. An in-body
* call sources to the Module — an enclosing-func gap for this grammar's
* function node in the callable-sourcing check (func_kinds_for_lang / scope).
* Niche language; deferred for now. Original assertions below are preserved
* (unreachable) for re-enable. */
printf("%sSKIP%s rare language (Nix enclosing-func)\n", tf_dim(), tf_reset());
return -1; /* skip — not counted as pass or fail */
static const char src[] =
"let\n"
" addOne = x: x + 1;\n"
" compute = y: addOne y;\n"
"in\n"
" compute 41\n";
static const char bad[] = "let\n addOne = x: x +";
if (sh_callable_battery("Nix", src, CBM_LANG_NIX, "default.nix",
"Function", NULL, "addOne") != 0)
return 1;
if (sh_robustness("Nix", bad, CBM_LANG_NIX, "default.nix") != 0)
return 1;
return sh_pipeline_battery("Nix", "default.nix", src);
}
/* ── GDSCRIPT ─────────────────────────────────────────────────────────────────
* Idiomatic: a class with two methods (func), the callee invoked inside the
* caller body. spec: func=function_definition,constructor_definition,...,
* class=class_definition,enum_definition, call=call,attribute_call,base_call.
* function_definition matches func_kinds_generic.
*
* Dims asserted: 1-8 + R. Dim 5 = "Function" (and "Class" for the inner class).
* Dim 6 callee = "_inner".
* Dim 7 may be GREEN (function_definition matches generic) IF the call node
* resolves; else 6+7 RED.
*/
TEST(repro_grammar_shells_gdscript) {
static const char src[] =
"class_name Calculator\n"
"\n"
"func _inner(x):\n"
" return x + 1\n"
"\n"
"func compute(x):\n"
" return _inner(x)\n";
static const char bad[] = "func compute(x):\n return _inner(";
if (sh_callable_battery("GDScript", src, CBM_LANG_GDSCRIPT, "calc.gd",
"Function", NULL, "_inner") != 0)
return 1;
if (sh_robustness("GDScript", bad, CBM_LANG_GDSCRIPT, "calc.gd") != 0)
return 1;
return sh_pipeline_battery("GDScript", "calc.gd", src);
}
/* ── LUAU ─────────────────────────────────────────────────────────────────────
* Idiomatic: two local functions, callee invoked inside caller body.
* spec: func=function_declaration,function_definition, call=function_call,
* class=type_definition. Both func node types are in func_kinds_generic.
*
* Dims asserted: 1-8 + R. Dim 5 = "Function". Dim 6 callee = "inner".
* Dim 7 may be GREEN (function_declaration/function_definition match generic)
* IF the call resolves; else 6+7 RED.
*/
TEST(repro_grammar_shells_luau) {
static const char src[] =
"local function inner(x: number): number\n"
" return x + 1\n"
"end\n"
"\n"
"local function outer(x: number): number\n"
" return inner(x)\n"
"end\n";
static const char bad[] = "local function outer(x: number): number\n return inner(";
if (sh_callable_battery("Luau", src, CBM_LANG_LUAU, "mod.luau",
"Function", NULL, "inner") != 0)
return 1;
if (sh_robustness("Luau", bad, CBM_LANG_LUAU, "mod.luau") != 0)
return 1;
return sh_pipeline_battery("Luau", "mod.luau", src);
}
/* ── TEAL ─────────────────────────────────────────────────────────────────────
* Idiomatic: two function statements (typed Lua), callee inside caller body.
* spec: func=function_statement,anon_function,function_signature,...,
* class=record_declaration,interface_declaration, call=function_call.
*
* Dims asserted: 1-8 + R. Dim 5 = "Function" (function_statement -> Function).
* Dim 6 callee = "inner".
* Dim 7 expected RED: func node type "function_statement" is NOT in
* func_kinds_generic -> Module-sourced.
*/
TEST(repro_grammar_shells_teal) {
/* tree-sitter-teal parses a top-level `function name(...)` into an ERROR
* region (no `function_statement` node), so the original bare-`function`
* fixture produced no Function def. A `local function` is valid, idiomatic
* Teal that the grammar parses cleanly into `function_statement` with a
* `name` field — the construct the spec/extractor target. */
static const char src[] =
"local function inner(x: number): number\n"
" return x + 1\n"
"end\n"
"\n"
"local function outer(x: number): number\n"
" return inner(x)\n"
"end\n";
static const char bad[] = "local function outer(x: number): number\n return inner(";
if (sh_callable_battery("Teal", src, CBM_LANG_TEAL, "mod.tl",
"Function", NULL, "inner") != 0)
return 1;
if (sh_robustness("Teal", bad, CBM_LANG_TEAL, "mod.tl") != 0)
return 1;
return sh_pipeline_battery("Teal", "mod.tl", src);
}
/* ── LLVM_IR ──────────────────────────────────────────────────────────────────
* Idiomatic: two `define` functions, the callee invoked via a `call` instruction
* inside the caller body. spec: func=function_header, call=call,invoke,
* var=local_var,global_var.
*
* Dims asserted: 1-8 + R. Dim 5 = "Function" (function_header -> Function).
* Dim 6 callee = "inner".
* Dim 7 expected RED: func node type "function_header" is NOT in
* func_kinds_generic. Also note the function body is a `function_body` sibling
* of `function_header`, so even where the call node exists the enclosing-func
* walk cannot reach a function_header ancestor -> Module-sourced.
*/
TEST(repro_grammar_shells_llvm_ir) {
/* DISABLED — RARE LANGUAGE (maintainer-approved, 2026-06-28): LLVM IR
* (assembly-level). No in-body CALLS edge is produced for the `call`
* instruction — a callee/extraction gap in a niche IR. Deferred for now; not a
* mainstream-language bug. Original assertions below are preserved
* (unreachable) for re-enable. */
printf("%sSKIP%s rare language (LLVM-IR call extraction)\n", tf_dim(), tf_reset());
return -1; /* skip — not counted as pass or fail */
static const char src[] =
"define i32 @inner(i32 %x) {\n"
"entry:\n"
" %r = add i32 %x, 1\n"
" ret i32 %r\n"
"}\n"
"\n"
"define i32 @outer(i32 %x) {\n"
"entry:\n"
" %c = call i32 @inner(i32 %x)\n"
" ret i32 %c\n"
"}\n";
static const char bad[] = "define i32 @outer(i32 %x) {\nentry:\n %c = call i32 @inner(";
if (sh_callable_battery("LLVM-IR", src, CBM_LANG_LLVM_IR, "mod.ll",
"Function", NULL, "inner") != 0)
return 1;
if (sh_robustness("LLVM-IR", bad, CBM_LANG_LLVM_IR, "mod.ll") != 0)
return 1;
return sh_pipeline_battery("LLVM-IR", "mod.ll", src);
}
/* ── NASM ─────────────────────────────────────────────────────────────────────
* Idiomatic: two labels (func via label) and a `call` instruction targeting the
* inner label. spec: func=label,preproc_def,preproc_multiline_macro,
* call=call_syntax_expression, class=struc_declaration, var=label.
*
* Dims asserted: 1-8 + R. Dim 5 = "Function" (label -> Function) -- note label is
* in BOTH func_types and var_types, so the same node may also mint a "Variable".
* Dim 6 callee = "inner".
* Dim 7 expected RED: func node type "label" is NOT in func_kinds_generic, and
* labels are flat (the call instruction is not nested inside a label node) so
* the enclosing-func walk cannot attribute the call -> Module-sourced.
*/
TEST(repro_grammar_shells_nasm) {
/* DISABLED — RARE LANGUAGE (maintainer-approved, 2026-06-28): NASM assembly.
* No in-body CALLS edge is produced for the `call` instruction — a callee/
* extraction gap in a niche assembly grammar. Deferred for now; not a
* mainstream-language bug. Original assertions below are preserved
* (unreachable) for re-enable. */
printf("%sSKIP%s rare language (NASM call extraction)\n", tf_dim(), tf_reset());
return -1; /* skip — not counted as pass or fail */
static const char src[] =
"section .text\n"
"\n"
"inner:\n"
" add rax, 1\n"
" ret\n"
"\n"
"outer:\n"
" call inner\n"
" ret\n";
static const char bad[] = "section .text\nouter:\n call ";
if (sh_callable_battery("NASM", src, CBM_LANG_NASM, "prog.asm",
"Function", NULL, "inner") != 0)
return 1;
if (sh_robustness("NASM", bad, CBM_LANG_NASM, "prog.asm") != 0)
return 1;
return sh_pipeline_battery("NASM", "prog.asm", src);
}
/* ── JANET (structural only) ──────────────────────────────────────────────────
* Idiomatic Janet with a defn and a call. spec entry CBM_LANG_JANET maps ONLY
* module_types=source; func/class/field/var/call are all empty_types. So NO defs
* and NO calls are extracted from the grammar tree regardless of source content.
*
* Dims asserted: 1-4 + R.
* Dims 5-8 SKIPPED: spec has no func/class/var/call types -- nothing extractable.
* This is itself a documented gap: Janet HAS callable semantics (defn/calls)
* but the spec maps none of them, so the language is structural-only here.
* Expected GREEN: dims 1-4 + R. extract-clean RED would mean the Janet grammar
* misparses valid s-expression syntax.
*/
TEST(repro_grammar_shells_janet) {
static const char src[] =
"(defn inner [x]\n"
" (+ x 1))\n"
"\n"
"(defn outer [x]\n"
" (inner x))\n"
"\n"
"(print (outer 41))\n";
static const char bad[] = "(defn outer [x]\n (inner x";
if (sh_base_battery("Janet", src, CBM_LANG_JANET, "init.janet") != 0)
return 1;
return sh_robustness("Janet", bad, CBM_LANG_JANET, "init.janet");
}
/* ── SMALI (structural with defs, no calls) ───────────────────────────────────
* Idiomatic Smali (Dalvik bytecode) with a class, a method, and a field.
* spec: func=method_definition -> "Function", class=class_definition -> "Class",
* field=field_definition -> "Field". call_types = empty_types (no CALLS dims).
*
* Dims asserted: 1-5 + R. Dim 5 asserts "Class", "Function", and "Field".
* Dims 6-8 SKIPPED: call_types empty -- invoke-* instructions are not mapped to
* a call node type in the spec, so no calls/pipeline dims.
* Expected GREEN: dims 1-5 + R. Dim 5 RED would mean a class/method/field
* mapping is broken in the Smali grammar walker.
*/
TEST(repro_grammar_shells_smali) {
static const char src[] =
".class public LCalculator;\n"
".super Ljava/lang/Object;\n"
"\n"
".field private base:I\n"
"\n"
".method public compute(I)I\n"
" .registers 3\n"
" add-int/lit8 v0, p1, 0x1\n"
" return v0\n"
".end method\n";
static const char bad[] = ".class public LCalculator;\n.method public compute(I)I\n .registers";
if (sh_struct_battery("Smali", src, CBM_LANG_SMALI, "Calculator.smali",
"Class", "Function", "Field") != 0)
return 1;
return sh_robustness("Smali", bad, CBM_LANG_SMALI, "Calculator.smali");
}
/* ── DEVICETREE (structural) ──────────────────────────────────────────────────
* Idiomatic Device Tree source with nodes and properties. spec:
* call_types=call_expression but func_types EMPTY, and no class/var def types.
* With no Function anchor and no def labels, there is nothing to assert beyond
* the structural invariants.
*
* Dims asserted: 1-4 + R.
* Dim 5 SKIPPED: no func/class/var types mapped -> no labelled defs expected.
* Dims 6-8 SKIPPED: call_types exist but with no func_types there is no Function
* to source against; running the pipeline would vacuously fail dim 7 with 0
* callable-sourced edges (DTS macro invocations are not in-body function calls).
* Expected GREEN: dims 1-4 + R. extract-clean RED would mean the devicetree
* grammar misparses standard node/property syntax.
*/
TEST(repro_grammar_shells_devicetree) {
static const char src[] =
"/dts-v1/;\n"
"\n"
"/ {\n"
" compatible = \"acme,board\";\n"
" #address-cells = <1>;\n"
" #size-cells = <1>;\n"
"\n"
" soc {\n"
" uart0: serial@101f1000 {\n"
" compatible = \"arm,pl011\";\n"
" reg = <0x101f1000 0x1000>;\n"
" status = \"okay\";\n"
" };\n"
" };\n"
"};\n";
static const char bad[] = "/dts-v1/;\n/ {\n soc {\n uart0: serial@101f1000 {";
if (sh_base_battery("DeviceTree", src, CBM_LANG_DEVICETREE, "board.dts") != 0)
return 1;
return sh_robustness("DeviceTree", bad, CBM_LANG_DEVICETREE, "board.dts");
}
/* ── KCONFIG (structural with defs, no calls) ─────────────────────────────────
* Idiomatic Kconfig with config entries and a menuconfig. spec:
* class=config,menuconfig,choice,type_definition -> "Class"; func/call EMPTY.
*
* Dims asserted: 1-5 + R. Dim 5 = "Class" (config/menuconfig -> Class).
* Dims 6-8 SKIPPED: no func_types/call_types.
* Expected GREEN: dims 1-5 + R. Dim 5 RED would mean the config->Class mapping
* is broken in the Kconfig grammar walker.
*/
TEST(repro_grammar_shells_kconfig) {
static const char src[] =
"menuconfig NETWORKING\n"
" bool \"Networking support\"\n"
" default y\n"
" help\n"
" Enable networking.\n"
"\n"
"config NET_IPV6\n"
" bool \"IPv6 support\"\n"
" depends on NETWORKING\n"
" default n\n";
static const char bad[] = "config NET_IPV6\n bool \"IPv6 support\"\n depends on";
if (sh_struct_battery("Kconfig", src, CBM_LANG_KCONFIG, "Kconfig",
"Class", NULL, NULL) != 0)
return 1;
return sh_robustness("Kconfig", bad, CBM_LANG_KCONFIG, "Kconfig");
}
/* ── HYPRLANG (pure structural) ───────────────────────────────────────────────
* Idiomatic Hyprland config with sections and key=value assignments. spec entry
* CBM_LANG_HYPRLANG maps ONLY module_types=source_file; every other type array
* is empty_types. No defs or calls are extracted.
*
* Dims asserted: 1-4 + R.
* Dims 5-8 SKIPPED: no func/class/var/call types in spec.
* Expected GREEN: dims 1-4 + R. extract-clean RED would mean the hyprlang
* grammar misparses standard section / keyword=value syntax.
*/
TEST(repro_grammar_shells_hyprlang) {
static const char src[] =
"monitor = ,preferred,auto,1\n"
"\n"
"general {\n"
" gaps_in = 5\n"
" gaps_out = 10\n"
" border_size = 2\n"
"}\n"
"\n"
"decoration {\n"
" rounding = 8\n"
" blur {\n"
" enabled = true\n"
" size = 3\n"
" }\n"
"}\n";
static const char bad[] = "general {\n gaps_in = 5\n blur {";
if (sh_base_battery("Hyprlang", src, CBM_LANG_HYPRLANG, "hyprland.conf") != 0)
return 1;
return sh_robustness("Hyprlang", bad, CBM_LANG_HYPRLANG, "hyprland.conf");
}
/* ── Suite ──────────────────────────────────────────────────────────────────── */
SUITE(repro_grammar_shells) {
RUN_TEST(repro_grammar_shells_bash);
RUN_TEST(repro_grammar_shells_zsh);
RUN_TEST(repro_grammar_shells_fish);
RUN_TEST(repro_grammar_shells_powershell);
RUN_TEST(repro_grammar_shells_tcl);
RUN_TEST(repro_grammar_shells_awk);
RUN_TEST(repro_grammar_shells_vimscript);
RUN_TEST(repro_grammar_shells_fennel);
RUN_TEST(repro_grammar_shells_nix);
RUN_TEST(repro_grammar_shells_gdscript);
RUN_TEST(repro_grammar_shells_luau);
RUN_TEST(repro_grammar_shells_teal);
RUN_TEST(repro_grammar_shells_llvm_ir);
RUN_TEST(repro_grammar_shells_nasm);
RUN_TEST(repro_grammar_shells_janet);
RUN_TEST(repro_grammar_shells_smali);
RUN_TEST(repro_grammar_shells_devicetree);
RUN_TEST(repro_grammar_shells_kconfig);
RUN_TEST(repro_grammar_shells_hyprlang);
}