/* * repro_grammar_misc.c -- FINAL per-grammar INVARIANT battery covering the * remaining MISCELLANEOUS language family (hardware-description, CFML dialects, * niche scripting, structural assembly/linker/tablegen/ledger/IaC). This file * completes the all-159-grammar reproduce-first coverage: every CBM_LANG_* now * has a per-language RED/GREEN row on the bug-repro board. * * One TEST() per language so per-language RED/GREEN shows on the board. Each * test runs the battery dimension appropriate to what the language's lang_spec * actually models (verified against internal/cbm/lang_specs.c and the * *_func_types / *_class_types / *_call_types arrays): * * CALLABLE family (func_types AND call_types both non-empty) -> FULL battery * (dims 1-8) + robustness: * VERILOG -> CBM_LANG_VERILOG (func: function_declaration/task; * call: system_tf_call/subroutine_call) * SYSTEMVERILOG -> CBM_LANG_SYSTEMVERILOG (func: function_declaration/task; * call: function_subroutine_call) * VHDL -> CBM_LANG_VHDL (func: subprogram_declaration/def; * call: function_call/procedure_call) * CFML -> CBM_LANG_CFML (func: function_declaration; * call: call_expression) * CFSCRIPT -> CBM_LANG_CFSCRIPT (func: function_declaration; call: * js_call_types = call_expression) * RESCRIPT -> CBM_LANG_RESCRIPT (func: function; call: call_expression) * SQUIRREL -> CBM_LANG_SQUIRREL (func: function_declaration; call: * call_expression) * PINE -> CBM_LANG_PINE (func: function_declaration_statement; * call: call) * TEMPL -> CBM_LANG_TEMPL (func: function_declaration/method; * call: call_expression) * SQL -> CBM_LANG_SQL (func: create_function; call: * function_call/invocation/command) * * STRUCTURAL family (asm / linker / data / IaC) -> extract-clean + * labels/fqn/ranges valid + defs-present (the entities each should extract) + * robustness; NO call / pipeline dims: * ASSEMBLY -> CBM_LANG_ASSEMBLY (func_types = {"label"}; defs are * labels routed through the func-def * path -> "Function"). defs-present * asserts "Function". * LINKERSCRIPT -> CBM_LANG_LINKERSCRIPT (only module_types + call_types; no * func/class/var defs in spec). NO * defs-present assertion -- dims 1-4 * + robustness only. * TABLEGEN -> CBM_LANG_TABLEGEN (func: def/multiclass/defm -> * "Function"; class: class -> "Class"). * defs-present asserts "Function" and * "Class". No call_types -> no call dim. * BEANCOUNT -> CBM_LANG_BEANCOUNT (only module_types + import_types; no * func/class/var/call defs in spec). * NO defs-present -- dims 1-4 + * robustness only. * BICEP -> CBM_LANG_BICEP (func: user_defined_function -> * "Function"; class: resource/type/ * module_declaration -> "Class"). * defs-present asserts "Class" for the * resource declaration. Treated as * structural per the family split (no * call/pipeline dim asserted). * * BATTERY DIMENSIONS * ------------------ * SINGLE-FILE (cbm_extract_file, via inv_rx + inv_count_* helpers): * 1. extract-clean : inv_extract_clean(src,lang,file) == 1 * (parser returned a result and did not set has_error; a * hard crash would not return at all). * 2. labels-valid : inv_count_bad_labels(r) == 0 * (every extracted def label is in the known label set). * 3. fqn-wellformed : inv_count_bad_fqns(r) == 0 * (no empty / ".." / leading or trailing '.' / whitespace QNs). * 4. ranges-valid : inv_count_bad_ranges(r) == 0 * (start_line >= 1 and start_line <= end_line for every def). * 5. defs-present : at least one def with each expected label is extracted. * 6. calls-extracted : inv_has_call(r, callee) == 1 (the in-body call was * captured). CALLABLE family only. * * FULL-PIPELINE (rh_index_files -> cbm_store_t*, via inv_count_* store helpers): * 7. callable-sourcing : inv_count_calls_by_source(store,project,&mod,&call); * assert mod == 0 AND call >= 1 -- every in-body call must * be sourced at a Function/Method node, NEVER at a Module * node. CALLABLE family only. * 8. no-dangling : inv_count_dangling_edges(store,project,"CALLS") == 0 * (every CALLS edge resolves both endpoints). CALLABLE * family only. * * ROBUSTNESS (every language): * R. extract-on-malformed: the extractor must RETURN (not crash/hang) on a * deliberately truncated/broken version of the fixture. cbm_extract_file may * set has_error but must not return NULL. * * HONEST RED CONTRACT (the point of this file): dimension 7 (callable-sourcing) is * expected RED for the non-LSP callable languages here. None of VERILOG / * SYSTEMVERILOG / VHDL / CFML / CFSCRIPT / RESCRIPT / SQUIRREL / PINE / TEMPL / SQL * has a dedicated cross-LSP rescue, so attribution depends solely on the * tree-sitter enclosing-func walk (cbm_find_enclosing_func + func_kinds_for_lang in * helpers.c). When that mapping does not match the grammar's emitted func node * types, the in-body call falls back to the Module QN -- exactly the enclosing-func * drift documented for the compiled/OOP family in repro_grammar_core.c. Some of * these languages may additionally fail dim 6 (calls-extracted) if the grammar's * call node carries the callee on a child shape the call-extractor does not read, * or even dim 7 vacuously (0 CALLS edges). RED rows here ARE the deliverable: they * document the per-language attribution / extraction gaps precisely. * * Coding rule: inline comments are line comments only (no block comments inside * block comments). */ #include "test_framework.h" #include "repro_invariant_lib.h" #include #include #include /* ── Shared single-file battery (dims 1-6) ─────────────────────────────────── * * Runs the base invariants (1-4), the defs-present checks (5) for each non-NULL * expected label, and the calls-extracted check (6) when callee is non-NULL. * Pass NULL for expect_label2 / callee to skip those dimensions (structural * languages pass NULL for callee; languages with no asserted def pass NULL for * expect_label). Returns 0 on PASS, 1 on FAIL. */ static int misc_single_file_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(); /* 1. extract-clean -- must hold before anything else is meaningful. */ 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; /* nothing else can be trusted */ } 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++; } /* 5. defs-present (per non-NULL expected label) */ 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 (CALLABLE family only) */ 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 * edge so a fixture that produced zero CALLS edges cannot vacuously pass) and no * dangling CALLS edges. Dim 7 is expected RED for the non-LSP callable languages * here -- that is the intended signal. Returns 0 on PASS, 1 on FAIL. */ static int misc_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) -- known enclosing-func gap\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. If it returns NULL * the extractor crashed or aborted on bad input -- that is a RED robustness bug. * Returns 0 on PASS, 1 on FAIL. */ static int misc_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; } /* ── ASSEMBLY (structural) ─────────────────────────────────────────────────── * Idiomatic x86-64 GAS snippet: a global function label, a local label, and a * call to a labelled routine. assembly_func_types = {"label"} so labels are * routed through the func-def path and minted as "Function" defs. * assembly spec has no call_types -> no calls/pipeline dims. * * Dims asserted: 1-5 ("Function" for the labels) + R. * Expected: dims 1-4 + R GREEN; dim 5 GREEN if label -> "Function" mints (the * `add:`/`main:` labels). Dim 5 RED would document that the assembly label * def-path does not fire for GAS-style labels. */ TEST(repro_grammar_misc_assembly) { static const char src[] = ".text\n" ".globl main\n" "add:\n" " addl %esi, %edi\n" " movl %edi, %eax\n" " ret\n" "main:\n" " movl $1, %edi\n" " movl $2, %esi\n" " call add\n" " ret\n"; static const char bad[] = ".globl main\nmain:\n call "; if (misc_single_file_battery("ASSEMBLY", src, CBM_LANG_ASSEMBLY, "f.s", "Function", NULL, NULL) != 0) return 1; return misc_robustness("ASSEMBLY", bad, CBM_LANG_ASSEMBLY, "f.s"); } /* ── BEANCOUNT (structural) ────────────────────────────────────────────────── * Idiomatic Beancount ledger: an option directive, an open directive for an * account, and a transaction with two postings. The Beancount spec has only * beancount_module_types = {"file"} + beancount_import_types; no func/class/var/ * call types are mapped, so no labelled defs are minted from the grammar tree. * * Dims asserted: 1-4 + R (no defs-present, no calls/pipeline). * Expected GREEN: dims 1-4 + R. extract-clean RED would indicate the Beancount * grammar misparses standard directive / transaction syntax. */ TEST(repro_grammar_misc_beancount) { static const char src[] = "option \"title\" \"CBM Ledger\"\n" "\n" "2026-01-01 open Assets:Cash USD\n" "2026-01-01 open Expenses:Food USD\n" "\n" "2026-06-26 * \"Lunch\" \"Sandwich shop\"\n" " Expenses:Food 12.50 USD\n" " Assets:Cash -12.50 USD\n"; static const char bad[] = "2026-06-26 * \"Lunch\"\n Expenses:Food 12.50"; if (misc_single_file_battery("BEANCOUNT", src, CBM_LANG_BEANCOUNT, "main.beancount", NULL, NULL, NULL) != 0) return 1; return misc_robustness("BEANCOUNT", bad, CBM_LANG_BEANCOUNT, "main.beancount"); } /* ── BICEP (structural) ────────────────────────────────────────────────────── * Idiomatic Azure Bicep: a parameter, a variable, and a resource_declaration. * bicep_class_types = {"resource_declaration", "type_declaration", * "module_declaration"} -> "Class"; bicep_func_types = {"user_defined_function", * "lambda_expression"} -> "Function". The resource declaration is the primary * structural entity. call_types exist (call_expression) but Bicep is treated as * structural here -- the call/pipeline dims are not asserted. * * Dims asserted: 1-5 ("Class" for the resource) + R. * Expected: dims 1-4 + R GREEN; dim 5 GREEN if resource_declaration -> "Class". * Dim 5 RED would document that the Bicep resource def-path does not fire. */ TEST(repro_grammar_misc_bicep) { static const char src[] = "param location string = resourceGroup().location\n" "var storageName = 'cbmstore'\n" "\n" "resource sa 'Microsoft.Storage/storageAccounts@2023-01-01' = {\n" " name: storageName\n" " location: location\n" " sku: {\n" " name: 'Standard_LRS'\n" " }\n" " kind: 'StorageV2'\n" "}\n"; static const char bad[] = "resource sa 'Microsoft.Storage@2023' = {\n name:"; if (misc_single_file_battery("BICEP", src, CBM_LANG_BICEP, "main.bicep", "Class", NULL, NULL) != 0) return 1; return misc_robustness("BICEP", bad, CBM_LANG_BICEP, "main.bicep"); } /* ── CFML (callable) ───────────────────────────────────────────────────────── * Idiomatic CFML tag-dialect template (.cfm): a cffunction defining `add`, and a * second cffunction `compute` that invokes `add()` strictly inside its body. * cfml_func_types = {"function_declaration", "function_expression"} -> "Function"; * cfml_call_types = {"call_expression"} -> call extraction. * * Dims asserted: 1-8 + R. * Dim 5 expected GREEN: "Function" for the cffunction defs. * Dim 6 expected GREEN: call to "add" inside compute. * Dim 7 expected GREEN: cf_function_tag is in cfml_func_types and compute_func_qn * resolves its name from the cf_attribute (name="..."), so the add() call inside * compute's cffunction body sources to the compute Function. (Previously the * def-extractor minted a "Function" for cf_function_tag but the scope-tracking * func_types list only had function_declaration/_expression, so the in-body call * mis-sourced to Module: a production sync bug, not a rescue gap -- now fixed.) * Dim 8 expected GREEN: no dangling CALLS endpoints. */ TEST(repro_grammar_misc_cfml) { static const char src[] = "\n" " \n" " \n" " \n" "\n" "\n" "\n" " \n" " \n" "\n"; static const char bad[] = "\n "Function"; the CFSCRIPT spec reuses js_call_types * (call_expression) for call extraction. * * Dims asserted: 1-8 + R. * Dim 5 expected GREEN: "Function" for the function defs. * Dim 6 expected GREEN: call to "add" inside compute. * Dim 7 expected RED: no cross-LSP rescue for CFScript; the enclosing-func walk * may attribute the in-body call at Module. * Dim 8 expected GREEN: no dangling CALLS endpoints. */ TEST(repro_grammar_misc_cfscript) { static const char src[] = "component {\n" " function add(a, b) {\n" " return a + b;\n" " }\n" "\n" " function compute(x) {\n" " return add(x, 1);\n" " }\n" "}\n"; static const char bad[] = "component {\n function add(a, b) {\n return add("; if (misc_single_file_battery("CFSCRIPT", src, CBM_LANG_CFSCRIPT, "Calc.cfc", "Function", NULL, "add") != 0) return 1; if (misc_robustness("CFSCRIPT", bad, CBM_LANG_CFSCRIPT, "Calc.cfc") != 0) return 1; return misc_pipeline_battery("CFSCRIPT", "Calc.cfc", src); } /* ── LINKERSCRIPT (structural) ─────────────────────────────────────────────── * Idiomatic GNU ld linker script: a MEMORY block, an ENTRY directive, and a * SECTIONS block. The Linkerscript spec has only linkerscript_module_types = * {"source_file"} + linkerscript_call_types = {"call_expression"}; there are NO * func_types/class_types/var_types, so no labelled defs are minted. Because * func_types is empty there is no Function node to source a call against, so the * call/pipeline dims are not asserted (they would vacuously fail dim 7). * * Dims asserted: 1-4 + R (no defs-present, no calls/pipeline). * Expected GREEN: dims 1-4 + R. extract-clean RED would indicate the linker-script * grammar misparses standard MEMORY/SECTIONS syntax. */ TEST(repro_grammar_misc_linkerscript) { static const char src[] = "ENTRY(_start)\n" "\n" "MEMORY\n" "{\n" " FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 256K\n" " RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 64K\n" "}\n" "\n" "SECTIONS\n" "{\n" " .text : { *(.text*) } > FLASH\n" " .data : { *(.data*) } > RAM\n" "}\n"; static const char bad[] = "SECTIONS\n{\n .text : { *(.text*) } > "; if (misc_single_file_battery("LINKERSCRIPT", src, CBM_LANG_LINKERSCRIPT, "link.ld", NULL, NULL, NULL) != 0) return 1; return misc_robustness("LINKERSCRIPT", bad, CBM_LANG_LINKERSCRIPT, "link.ld"); } /* ── PINE (callable) ───────────────────────────────────────────────────────── * Idiomatic Pine Script v5 indicator: a user function `ema2` defined with * function_declaration_statement, and a call to the built-in `plot()` plus an * application of `ema2`. pine_func_types = {"function_declaration_statement"} -> * "Function"; pine_call_types = {"call"} -> call extraction. * * Dims asserted: 1-8 + R. * Dim 5 expected GREEN: "Function" for ema2 and wrap. * Dim 6 expected GREEN: call to "ema2" inside wrap. * Dim 7 expected GREEN: wrap's body calls the same-file ema2, so a * callable-sourced CALLS edge is emitted from the wrap Function node. The * top-level indicator() call targets a Pine built-in (no same-file def), so it * yields no edge -- no Module-sourced edge remains. (The earlier fixture's only * same-file calls -- out = ema2(...) and plot(out) -- sat at script top level * and were legitimately Module-sourced: a broken fixture, not a prod gap.) * Dim 8 expected GREEN: no dangling CALLS endpoints. */ TEST(repro_grammar_misc_pine) { static const char src[] = "//@version=5\n" "indicator(\"CBM EMA\", overlay=true)\n" "\n" "ema2(src, len) =>\n" " a = src + len\n" " a\n" "\n" "wrap(src, len) =>\n" " b = ema2(src, len)\n" " b\n"; static const char bad[] = "//@version=5\nema2(src, len) =>\n a = ta.ema("; if (misc_single_file_battery("PINE", src, CBM_LANG_PINE, "ind.pine", "Function", NULL, "ema2") != 0) return 1; if (misc_robustness("PINE", bad, CBM_LANG_PINE, "ind.pine") != 0) return 1; return misc_pipeline_battery("PINE", "ind.pine", src); } /* ── RESCRIPT (callable) ───────────────────────────────────────────────────── * Idiomatic ReScript module: a let-bound function `add` and a let-bound function * `compute` that calls `add` inside its body. rescript_func_types = {"function"} * -> "Function"; rescript_call_types = {"call_expression"} -> call extraction; * rescript_class_types = {"module_declaration", "type_declaration"}. * * Dims asserted: 1-8 + R. * Dim 5 expected GREEN: "Function" for the let-bound functions. * Dim 6 expected GREEN: call to "add" inside compute. * Dim 7 expected RED: ReScript has no cross-LSP rescue; the enclosing-func walk * for the `function` node may fall back to Module for the in-body call. * Dim 8 expected GREEN: no dangling CALLS endpoints. */ TEST(repro_grammar_misc_rescript) { static const char src[] = "let add = (a, b) => a + b\n" "\n" "let compute = x => {\n" " let result = add(x, 1)\n" " result\n" "}\n"; static const char bad[] = "let compute = x => {\n let result = add("; if (misc_single_file_battery("RESCRIPT", src, CBM_LANG_RESCRIPT, "Calc.res", "Function", NULL, "add") != 0) return 1; if (misc_robustness("RESCRIPT", bad, CBM_LANG_RESCRIPT, "Calc.res") != 0) return 1; return misc_pipeline_battery("RESCRIPT", "Calc.res", src); } /* ── SQL (callable) ────────────────────────────────────────────────────────── * Idiomatic PostgreSQL PL/pgSQL: a create_function defining `add`, and a second * create_function `compute` whose body invokes `add(...)`. sql_func_types = * {"create_function", "function_declaration"} -> "Function"; sql_call_types = * {"function_call", "invocation", "command"} -> call extraction. * * Dims asserted: 1-8 + R. * Dim 5 expected GREEN: "Function" for the create_function defs. * Dim 6 expected GREEN: call to "add" inside compute (function_call / invocation). * Dim 7 expected RED: SQL has no cross-LSP rescue; calls inside the function body * string may not resolve to the enclosing create_function via the tree-sitter * walk, falling back to Module. Dim 7 may also fail vacuously if the call is not * captured as a CALLS edge. RED documents the gap. * Dim 8 expected GREEN: no dangling CALLS endpoints. */ TEST(repro_grammar_misc_sql) { static const char src[] = "CREATE FUNCTION add(a integer, b integer) RETURNS integer AS $$\n" "BEGIN\n" " RETURN a + b;\n" "END;\n" "$$ LANGUAGE plpgsql;\n" "\n" "CREATE FUNCTION compute(x integer) RETURNS integer AS $$\n" "BEGIN\n" " RETURN add(x, 1);\n" "END;\n" "$$ LANGUAGE plpgsql;\n"; static const char bad[] = "CREATE FUNCTION add(a integer) RETURNS integer AS $$\nBEGIN\n RETURN add("; if (misc_single_file_battery("SQL", src, CBM_LANG_SQL, "fn.sql", "Function", NULL, "add") != 0) return 1; if (misc_robustness("SQL", bad, CBM_LANG_SQL, "fn.sql") != 0) return 1; return misc_pipeline_battery("SQL", "fn.sql", src); } /* ── SQUIRREL (callable) ───────────────────────────────────────────────────── * Idiomatic Squirrel: a free function `add` and a free function `compute` that * calls `add()` inside its body. squirrel_func_types = {"function_declaration", * "anonymous_function", "lambda_expression"} -> "Function"; * squirrel_call_types = {"call_expression"} -> call extraction; * squirrel_class_types = {"class_declaration", "enum_declaration"} -> "Class". * * Dims asserted: 1-8 + R. * Dim 5 expected GREEN: "Function" for the function defs. * Dim 6 expected GREEN: call to "add" inside compute. * Dim 7 expected RED: Squirrel has no cross-LSP rescue; the enclosing-func walk * for the function_declaration node may fall back to Module for the in-body call. * Dim 8 expected GREEN: no dangling CALLS endpoints. */ TEST(repro_grammar_misc_squirrel) { static const char src[] = "function add(a, b) {\n" " return a + b;\n" "}\n" "\n" "function compute(x) {\n" " return add(x, 1);\n" "}\n"; static const char bad[] = "function add(a, b) {\n return add("; if (misc_single_file_battery("SQUIRREL", src, CBM_LANG_SQUIRREL, "calc.nut", "Function", NULL, "add") != 0) return 1; if (misc_robustness("SQUIRREL", bad, CBM_LANG_SQUIRREL, "calc.nut") != 0) return 1; return misc_pipeline_battery("SQUIRREL", "calc.nut", src); } /* ── SYSTEMVERILOG (callable) ──────────────────────────────────────────────── * Idiomatic SystemVerilog module: a function `add` (function_declaration) and an * initial block / always block that invokes `add(...)` and a system task. * systemverilog_func_types = {"function_declaration", "task_declaration", * "function_body_declaration", "function_statement"} -> "Function"; * systemverilog_call_types = {"function_subroutine_call", "system_tf_call", * "method_call"} -> call extraction; systemverilog_class_types includes * module_declaration / class_declaration. * * Dims asserted: 1-8 + R. * Dim 5 expected GREEN: "Function" for the function `add`. * Dim 6 expected GREEN: call to "add" (function_subroutine_call) inside the block. * Dim 7 expected RED: SystemVerilog has no cross-LSP rescue; the enclosing-func * walk may attribute the in-body call at Module (or at the enclosing * module/class node, which is not a Function/Method). RED documents the gap. * Dim 8 expected GREEN: no dangling CALLS endpoints. */ TEST(repro_grammar_misc_systemverilog) { static const char src[] = "module calc;\n" " function automatic int add(int a, int b);\n" " return a + b;\n" " endfunction\n" "\n" " function automatic int compute(int x);\n" " return add(x, 1);\n" " endfunction\n" "endmodule\n"; static const char bad[] = "module calc;\n function automatic int add(int a);\n return add("; if (misc_single_file_battery("SYSTEMVERILOG", src, CBM_LANG_SYSTEMVERILOG, "calc.sv", "Function", NULL, "add") != 0) return 1; if (misc_robustness("SYSTEMVERILOG", bad, CBM_LANG_SYSTEMVERILOG, "calc.sv") != 0) return 1; return misc_pipeline_battery("SYSTEMVERILOG", "calc.sv", src); } /* ── TABLEGEN (structural) ─────────────────────────────────────────────────── * Idiomatic LLVM TableGen: a class definition and a def (record) that inherits * from it. tablegen_func_types = {"def", "multiclass", "defm"} -> "Function"; * tablegen_class_types = {"class"} -> "Class". TableGen has no call_types -> no * calls/pipeline dims. * * Dims asserted: 1-5 ("Function" for the def, "Class" for the class) + R. * Expected: dims 1-4 + R GREEN; dim 5 GREEN if def -> "Function" and class -> * "Class" both mint. Dim 5 RED would document the TableGen def/class path gap. */ TEST(repro_grammar_misc_tablegen) { static const char src[] = "class Instruction {\n" " string Namespace = \"CBM\";\n" " bits<8> Opcode = 0;\n" "}\n" "\n" "def ADD : Instruction {\n" " let Opcode = 1;\n" "}\n" "\n" "def SUB : Instruction {\n" " let Opcode = 2;\n" "}\n"; static const char bad[] = "class Instruction {\n string Namespace = "; if (misc_single_file_battery("TABLEGEN", src, CBM_LANG_TABLEGEN, "instr.td", "Function", "Class", NULL) != 0) return 1; return misc_robustness("TABLEGEN", bad, CBM_LANG_TABLEGEN, "instr.td"); } /* ── TEMPL (callable) ──────────────────────────────────────────────────────── * Idiomatic templ (a-h/templ) file: a Go helper `greeting` (function_declaration) * and a Go function `compute` that calls `greeting(...)` inside its body. The * templ spec maps templ_func_types = {"function_declaration", "method_declaration", * "method_elem"} -> "Function"; templ_call_types = {"call_expression"} -> call * extraction; templ_class_types include component_declaration / type defs. * * Dims asserted: 1-8 + R. * Dim 5 expected GREEN: "Function" for the Go function defs. * Dim 6 expected GREEN: call to "greeting" inside compute. * Dim 7 expected RED: templ has no cross-LSP rescue; the enclosing-func walk for * the function_declaration node may fall back to Module for the in-body call. * Dim 8 expected GREEN: no dangling CALLS endpoints. */ TEST(repro_grammar_misc_templ) { static const char src[] = "package main\n" "\n" "func greeting(name string) string {\n" " return \"Hello, \" + name\n" "}\n" "\n" "func compute(name string) string {\n" " return greeting(name)\n" "}\n"; static const char bad[] = "package main\nfunc greeting(name string) string {\n return greeting("; if (misc_single_file_battery("TEMPL", src, CBM_LANG_TEMPL, "page.templ", "Function", NULL, "greeting") != 0) return 1; if (misc_robustness("TEMPL", bad, CBM_LANG_TEMPL, "page.templ") != 0) return 1; return misc_pipeline_battery("TEMPL", "page.templ", src); } /* ── VERILOG (callable) ────────────────────────────────────────────────────── * Idiomatic Verilog module: a function `add` (function_declaration) and a second * function `compute` whose body invokes `add(...)`. verilog_func_types = * {"function_declaration", "task_declaration", "function_body_declaration", * "function_statement"} -> "Function"; verilog_call_types = {"system_tf_call", * "subroutine_call", "function_subroutine_call", "method_call"} -> call * extraction; verilog_class_types include module_declaration / class_declaration. * * Dims asserted: 1-8 + R. * Dim 5 expected GREEN: "Function" for the function `add`. * Dim 6 expected GREEN: call to "add" (subroutine_call / function_subroutine_call). * Dim 7 expected RED: Verilog has no cross-LSP rescue; the in-body call may be * sourced at Module (or at the non-callable enclosing module_declaration node). * RED documents the attribution gap. * Dim 8 expected GREEN: no dangling CALLS endpoints. */ TEST(repro_grammar_misc_verilog) { static const char src[] = "module calc;\n" " function integer add(input integer a, input integer b);\n" " add = a + b;\n" " endfunction\n" "\n" " function integer compute(input integer x);\n" " compute = add(x, 1);\n" " endfunction\n" "endmodule\n"; static const char bad[] = "module calc;\n function integer add(input integer a);\n add = add("; if (misc_single_file_battery("VERILOG", src, CBM_LANG_VERILOG, "calc.v", "Function", NULL, "add") != 0) return 1; if (misc_robustness("VERILOG", bad, CBM_LANG_VERILOG, "calc.v") != 0) return 1; return misc_pipeline_battery("VERILOG", "calc.v", src); } /* ── VHDL (callable) ───────────────────────────────────────────────────────── * Idiomatic VHDL package body: a function `add` (subprogram_definition) and a * function `compute` whose body calls `add(...)`. vhdl_func_types = * {"subprogram_declaration", "subprogram_definition"} -> "Function"; * vhdl_call_types = {"function_call", "procedure_call_statement", * "component_instantiation_statement"} -> call extraction; vhdl_class_types * include entity/architecture/package declarations. * * Dims asserted: 1-8 + R. * Dim 5 expected GREEN: "Function" for the subprogram defs. * Dim 6 expected GREEN: call to "add" (function_call) inside compute. * Dim 7 expected RED: VHDL has no cross-LSP rescue; the enclosing-func walk for * the subprogram_definition node may fall back to Module for the in-body call. * Dim 8 expected GREEN: no dangling CALLS endpoints. */ TEST(repro_grammar_misc_vhdl) { static const char src[] = "package body calc is\n" " function add(a : integer; b : integer) return integer is\n" " begin\n" " return a + b;\n" " end function;\n" "\n" " function compute(x : integer) return integer is\n" " begin\n" " return add(x, 1);\n" " end function;\n" "end package body;\n"; static const char bad[] = "package body calc is\n function add(a : integer) return integer is\n begin\n return add("; if (misc_single_file_battery("VHDL", src, CBM_LANG_VHDL, "calc.vhd", "Function", NULL, "add") != 0) return 1; if (misc_robustness("VHDL", bad, CBM_LANG_VHDL, "calc.vhd") != 0) return 1; return misc_pipeline_battery("VHDL", "calc.vhd", src); } /* ── Suite ──────────────────────────────────────────────────────────────────── */ SUITE(repro_grammar_misc) { RUN_TEST(repro_grammar_misc_assembly); RUN_TEST(repro_grammar_misc_beancount); RUN_TEST(repro_grammar_misc_bicep); RUN_TEST(repro_grammar_misc_cfml); RUN_TEST(repro_grammar_misc_cfscript); RUN_TEST(repro_grammar_misc_linkerscript); RUN_TEST(repro_grammar_misc_pine); RUN_TEST(repro_grammar_misc_rescript); RUN_TEST(repro_grammar_misc_sql); RUN_TEST(repro_grammar_misc_squirrel); RUN_TEST(repro_grammar_misc_systemverilog); RUN_TEST(repro_grammar_misc_tablegen); RUN_TEST(repro_grammar_misc_templ); RUN_TEST(repro_grammar_misc_verilog); RUN_TEST(repro_grammar_misc_vhdl); }