/*
* test_grammar_regression.c — Per-language extraction regression net.
*
* Guards against silent extraction breakage when vendored tree-sitter grammars
* are refreshed. Every CBM_LANG_* enum is exercised: a minimal sample is
* extracted and checked for (a) no crash (non-NULL result) and (b) a
* catastrophic-break floor (defs >= min_defs) plus, for confident cases,
* expected definition names. A future grammar upgrade that renames/removes the
* node types extraction depends on (e.g. the fwcd kotlin `name`-field drop that
* produced 0 defs) fails this suite loudly.
*
* min_defs convention: code languages use >=1 (a sample with a function/type
* must yield at least one def); data/config/markup languages use 0 (the check
* is "parses without crashing"). Tighten a row's min_defs to lock in a count.
*/
#include "test_framework.h"
#include "cbm.h"
#include "grammar_cases.h"
static int reg_has_def_any(CBMFileResult *r, const char *name) {
for (int i = 0; i < r->defs.count; i++) {
if (strcmp(r->defs.items[i].name, name) == 0)
return 1;
}
return 0;
}
static CBMFileResult *extract(const char *src, CBMLanguage lang, const char *proj,
const char *path) {
return cbm_extract_file(src, (int)strlen(src), lang, proj, path, 0, NULL, NULL);
}
const GrammarCase CBM_GRAMMAR_CASES[] = {
/* ── LSP-backed / mainstream code languages (expect named defs) ── */
{"go",
CBM_LANG_GO,
"a.go",
"package m\nfunc Foo() {}\nfunc Bar() int { return 0 }\n",
2,
{"Foo", "Bar", NULL}},
{"c",
CBM_LANG_C,
"a.c",
"int foo(void){return 0;}\nint bar(void){return 1;}\n",
2,
{"foo", "bar", NULL}},
{"cpp", CBM_LANG_CPP, "a.cpp", "struct A {};\nint foo(){return 0;}\n", 2, {"A", "foo", NULL}},
{"cuda",
CBM_LANG_CUDA,
"a.cu",
"__global__ void foo(){}\nint bar(){return 0;}\n",
1,
{"bar", NULL}},
{"python",
CBM_LANG_PYTHON,
"a.py",
"def foo():\n pass\nclass A:\n pass\n",
2,
{"foo", "A", NULL}},
{"javascript",
CBM_LANG_JAVASCRIPT,
"a.js",
"function foo(){}\nclass A{}\n",
2,
{"foo", "A", NULL}},
{"typescript",
CBM_LANG_TYPESCRIPT,
"a.ts",
"function foo(): number { return 1; }\nclass A {}\n",
2,
{"foo", "A", NULL}},
{"tsx", CBM_LANG_TSX, "a.tsx", "function foo(): number { return 1; }\n", 1, {"foo", NULL}},
{"java", CBM_LANG_JAVA, "A.java", "class A {\n void foo() {}\n}\n", 2, {"A", "foo", NULL}},
{"kotlin", CBM_LANG_KOTLIN, "a.kt", "fun foo() {}\nclass A\n", 2, {"foo", "A", NULL}},
{"rust", CBM_LANG_RUST, "a.rs", "fn foo() {}\nstruct A;\n", 2, {"foo", "A", NULL}},
{"ruby", CBM_LANG_RUBY, "a.rb", "def foo\nend\nclass A\nend\n", 2, {"foo", "A", NULL}},
{"php", CBM_LANG_PHP, "a.php", "=1), names vary by grammar ── */
{"ada", CBM_LANG_ADA, "a.adb", "procedure Foo is\nbegin\n null;\nend Foo;\n", 1, {NULL}},
{"agda", CBM_LANG_AGDA, "a.agda", "module M where\nfoo : Set\nfoo = Set\n", 0, {NULL}},
{"apex", CBM_LANG_APEX, "A.cls", "public class A {\n void foo() {}\n}\n", 1, {NULL}},
{"awk",
CBM_LANG_AWK,
"a.awk",
"function foo() { print 1 }\nfunction bar() { print 2 }\n",
1,
{NULL}},
{"cairo", CBM_LANG_CAIRO, "a.cairo", "fn foo() {}\nfn bar() {}\n", 1, {NULL}},
{"clojure", CBM_LANG_CLOJURE, "a.clj", "(defn foo [] 1)\n(defn bar [] 2)\n", 1, {NULL}},
{"commonlisp",
CBM_LANG_COMMONLISP,
"a.lisp",
"(defun foo () 1)\n(defun bar () 2)\n",
1,
{NULL}},
{"emacslisp", CBM_LANG_EMACSLISP, "a.el", "(defun foo () 1)\n(defun bar () 2)\n", 1, {NULL}},
{"crystal", CBM_LANG_CRYSTAL, "a.cr", "def foo\nend\nclass A\nend\n", 1, {NULL}},
{"d", CBM_LANG_DLANG, "a.d", "void foo() {}\nvoid bar() {}\n", 1, {NULL}},
{"elixir", CBM_LANG_ELIXIR, "a.ex", "defmodule A do\n def foo do\n end\nend\n", 1, {NULL}},
{"erlang", CBM_LANG_ERLANG, "a.erl", "-module(a).\nfoo() -> ok.\nbar() -> ok.\n", 1, {NULL}},
{"fennel", CBM_LANG_FENNEL, "a.fnl", "(fn foo [] 1)\n(fn bar [] 2)\n", 1, {NULL}},
{"fish", CBM_LANG_FISH, "a.fish", "function foo\n echo hi\nend\n", 1, {NULL}},
{"fortran", CBM_LANG_FORTRAN, "a.f90", "subroutine foo()\nend subroutine\n", 1, {NULL}},
{"fsharp", CBM_LANG_FSHARP, "a.fs", "let foo () = 1\nlet bar () = 2\n", 1, {NULL}},
{"gleam", CBM_LANG_GLEAM, "a.gleam", "pub fn foo() { 1 }\npub fn bar() { 2 }\n", 1, {NULL}},
{"glsl", CBM_LANG_GLSL, "a.glsl", "void foo() {}\nvoid main() {}\n", 1, {NULL}},
{"hare", CBM_LANG_HARE, "a.ha", "fn foo() void = void;\nfn bar() void = void;\n", 1, {NULL}},
{"haskell", CBM_LANG_HASKELL, "a.hs", "foo :: Int\nfoo = 1\n", 1, {NULL}},
{"hlsl", CBM_LANG_HLSL, "a.hlsl", "void foo() {}\nvoid bar() {}\n", 1, {NULL}},
{"ispc", CBM_LANG_ISPC, "a.ispc", "void foo() {}\nvoid bar() {}\n", 1, {NULL}},
{"objc", CBM_LANG_OBJC, "a.m", "@implementation A\n- (void)foo {}\n@end\n", 0, {NULL}},
{"ocaml", CBM_LANG_OCAML, "a.ml", "let foo () = 1\nlet bar () = 2\n", 1, {NULL}},
{"odin", CBM_LANG_ODIN, "a.odin", "foo :: proc() {}\nbar :: proc() {}\n", 1, {NULL}},
{"pascal", CBM_LANG_PASCAL, "a.pas", "procedure Foo;\nbegin\nend;\n", 1, {NULL}},
{"pony",
CBM_LANG_PONY,
"a.pony",
"actor Main\n fun foo(): U32 => 1\n",
2,
{"Main", "foo", NULL}},
{"purescript", CBM_LANG_PURESCRIPT, "a.purs", "foo :: Int\nfoo = 1\n", 0, {NULL}},
{"racket", CBM_LANG_RACKET, "a.rkt", "(define (foo) 1)\n(define (bar) 2)\n", 1, {NULL}},
{"rescript", CBM_LANG_RESCRIPT, "a.res", "let foo = () => 1\nlet bar = () => 2\n", 1, {NULL}},
{"scheme", CBM_LANG_SCHEME, "a.scm", "(define (foo) 1)\n(define (bar) 2)\n", 1, {NULL}},
{"slang", CBM_LANG_SLANG, "a.slang", "void foo() {}\nvoid bar() {}\n", 1, {NULL}},
{"squirrel", CBM_LANG_SQUIRREL, "a.nut", "function foo() {}\nfunction bar() {}\n", 1, {NULL}},
{"starlark",
CBM_LANG_STARLARK,
"a.bzl",
"def foo():\n pass\ndef bar():\n pass\n",
1,
{NULL}},
{"sway", CBM_LANG_SWAY, "a.sw", "fn foo() {}\nfn bar() {}\n", 1, {NULL}},
{"teal",
CBM_LANG_TEAL,
"a.tl",
"local function foo() end\nlocal function bar() end\n",
1,
{NULL}},
{"vimscript", CBM_LANG_VIMSCRIPT, "a.vim", "function! Foo()\nendfunction\n", 1, {NULL}},
{"elm", CBM_LANG_ELM, "a.elm", "module M exposing (..)\nfoo = 1\n", 0, {NULL}},
{"func", CBM_LANG_FUNC, "a.fc", "() foo() {\n}\n", 0, {NULL}},
{"lean", CBM_LANG_LEAN, "a.lean", "def foo : Nat := 1\ndef bar : Nat := 2\n", 0, {NULL}},
{"move",
CBM_LANG_MOVE,
"a.move",
"module 0x1::m {\n public fun foo() {}\n}\n",
1,
{"foo", NULL}},
{"mojo",
CBM_LANG_MOJO,
"a.mojo",
"fn foo() -> Int:\n return 1\n\nstruct A:\n fn bar(self) -> Int:\n return foo()\n",
2,
{"foo", "A", NULL}},
{"smali",
CBM_LANG_SMALI,
"A.smali",
".class public LA;\n.super Ljava/lang/Object;\n.method public foo()V\n.end method\n",
2,
{"foo", NULL}},
{"systemverilog",
CBM_LANG_SYSTEMVERILOG,
"a.sv",
"module m;\n function int foo(); return 0; endfunction\nendmodule\n",
0,
{NULL}},
{"verilog", CBM_LANG_VERILOG, "a.v", "module m;\nendmodule\n", 0, {NULL}},
{"vhdl", CBM_LANG_VHDL, "a.vhd", "entity foo is\nend foo;\n", 0, {NULL}},
{"wgsl", CBM_LANG_WGSL, "a.wgsl", "fn foo() {}\nfn bar() {}\n", 1, {NULL}},
{"tlaplus", CBM_LANG_TLAPLUS, "a.tla", "---- MODULE M ----\nFoo == 1\n====\n", 0, {NULL}},
{"llvm", CBM_LANG_LLVM_IR, "a.ll", "define void @foo() {\n ret void\n}\n", 0, {NULL}},
{"tablegen", CBM_LANG_TABLEGEN, "a.td", "def Foo {}\n", 0, {NULL}},
{"puppet", CBM_LANG_PUPPET, "a.pp", "class foo {\n}\n", 0, {NULL}},
/* ── first-party / self-maintained grammars ── */
{"assembly", CBM_LANG_ASSEMBLY, "a.s", ".global foo\nfoo:\n ret\n", 0, {NULL}},
{"nasm", CBM_LANG_NASM, "a.asm", "global foo\nfoo:\n ret\n", 0, {NULL}},
{"cfml", CBM_LANG_CFML, "a.cfm", "
hi
\n", 0, {NULL}}, {"css", CBM_LANG_CSS, "a.css", "a { color: red; }\n.x { width: 1px; }\n", 0, {NULL}}, {"scss", CBM_LANG_SCSS, "a.scss", "$c: red;\na { color: $c; }\n", 0, {NULL}}, {"markdown", CBM_LANG_MARKDOWN, "a.md", "# Title\n\nSome text.\n", 0, {NULL}}, {"rst", CBM_LANG_RST, "a.rst", "Title\n=====\n\ntext\n", 0, {NULL}}, {"dockerfile", CBM_LANG_DOCKERFILE, "Dockerfile", "FROM alpine\nRUN echo hi\n", 0, {NULL}}, {"makefile", CBM_LANG_MAKEFILE, "Makefile", "all:\n\techo hi\n", 0, {NULL}}, {"cmake", CBM_LANG_CMAKE, "CMakeLists.txt", "function(foo)\nendfunction()\n", 0, {NULL}}, {"meson", CBM_LANG_MESON, "meson.build", "project('x', 'c')\n", 0, {NULL}}, {"gn", CBM_LANG_GN, "a.gn", "executable(\"foo\") {\n}\n", 0, {NULL}}, {"just", CBM_LANG_JUST, "justfile", "foo:\n\techo hi\n", 0, {NULL}}, {"hcl", CBM_LANG_HCL, "a.hcl", "resource \"x\" \"y\" {\n a = 1\n}\n", 0, {NULL}}, {"nix", CBM_LANG_NIX, "a.nix", "{ foo = 1; bar = 2; }\n", 0, {NULL}}, {"gomod", CBM_LANG_GOMOD, "go.mod", "module example.com/x\n\ngo 1.21\n", 0, {NULL}}, {"gotemplate", CBM_LANG_GOTEMPLATE, "a.tmpl", "{{ if .X }}{{ .Y }}{{ end }}\n", 0, {NULL}}, {"graphql", CBM_LANG_GRAPHQL, "a.graphql", "type Foo {\n id: ID\n}\n", 0, {NULL}}, {"prisma", CBM_LANG_PRISMA, "a.prisma", "model Foo {\n id Int @id\n}\n", 0, {NULL}}, {"thrift", CBM_LANG_THRIFT, "a.thrift", "service Foo {\n void ping()\n}\n", 0, {NULL}}, {"capnp", CBM_LANG_CAPNP, "a.capnp", "struct Foo {\n id @0 :Int32;\n}\n", 0, {NULL}}, {"smithy", CBM_LANG_SMITHY, "a.smithy", "namespace example\n\nstructure Foo {\n id: Integer\n}\n", 1, {"Foo", NULL}}, {"wit", CBM_LANG_WIT, "a.wit", "interface i {\n record r { x: u32 }\n enum e { a, b }\n f: func() -> u32;\n}\n", 3, {"r", "e", "f", NULL}}, {"kdl", CBM_LANG_KDL, "a.kdl", "node \"x\" {\n}\n", 0, {NULL}}, {"ron", CBM_LANG_RON, "a.ron", "(a: 1, b: 2)\n", 0, {NULL}}, {"nickel", CBM_LANG_NICKEL, "a.ncl", "{ foo = 1, bar = 2 }\n", 0, {NULL}}, {"pkl", CBM_LANG_PKL, "a.pkl", "foo = 1\nbar = 2\n", 0, {NULL}}, {"bicep", CBM_LANG_BICEP, "a.bicep", "param foo string\n", 0, {NULL}}, {"bitbake", CBM_LANG_BITBAKE, "a.bb", "DESCRIPTION = \"x\"\n", 0, {NULL}}, {"beancount", CBM_LANG_BEANCOUNT, "a.beancount", "2020-01-01 open Assets:Cash\n", 0, {NULL}}, {"bibtex", CBM_LANG_BIBTEX, "a.bib", "@article{key,\n title = {X}\n}\n", 0, {NULL}}, {"po", CBM_LANG_PO, "a.po", "msgid \"x\"\nmsgstr \"y\"\n", 0, {NULL}}, {"diff", CBM_LANG_DIFF, "a.diff", "--- a\n+++ b\n@@ -1 +1 @@\n-x\n+y\n", 0, {NULL}}, {"regex", CBM_LANG_REGEX, "a.re", "(foo|bar)+\n", 0, {NULL}}, {"requirements", CBM_LANG_REQUIREMENTS, "requirements.txt", "flask==1.0\nrequests>=2\n", 0, {NULL}}, {"properties", CBM_LANG_PROPERTIES, "a.properties", "foo=1\nbar=2\n", 0, {NULL}}, {"gitignore", CBM_LANG_GITIGNORE, ".gitignore", "*.o\nbuild/\n", 0, {NULL}}, {"gitattributes", CBM_LANG_GITATTRIBUTES, ".gitattributes", "*.c text\n", 0, {NULL}}, {"sshconfig", CBM_LANG_SSHCONFIG, "config", "Host x\n HostName y\n", 0, {NULL}}, {"hyprlang", CBM_LANG_HYPRLANG, "a.conf", "general {\n gaps_in = 5\n}\n", 0, {NULL}}, {"kconfig", CBM_LANG_KCONFIG, "Kconfig", "config FOO\n bool \"foo\"\n", 0, {NULL}}, {"linkerscript", CBM_LANG_LINKERSCRIPT, "a.ld", "SECTIONS {\n .text : {}\n}\n", 0, {NULL}}, {"devicetree", CBM_LANG_DEVICETREE, "a.dts", "/dts-v1/;\n/ {\n};\n", 0, {NULL}}, {"jinja2", CBM_LANG_JINJA2, "a.j2", "{% if x %}{{ y }}{% endif %}\n", 0, {NULL}}, {"liquid", CBM_LANG_LIQUID, "a.liquid", "{% if x %}{{ y }}{% endif %}\n", 0, {NULL}}, {"blade", CBM_LANG_BLADE, "a.blade.php", "@if($x)\n{{ $y }}\n@endif\n", 0, {NULL}}, {"vue", CBM_LANG_VUE, "a.vue", "\n", 0, {NULL}}, {"svelte", CBM_LANG_SVELTE, "a.svelte", "\n{x}
\n", 0, {NULL}}, {"astro", CBM_LANG_ASTRO, "a.astro", "---\nconst x = 1;\n---\n