From b4fc9fed0c2d10cd8c1b3b99246b39c262ac5ade Mon Sep 17 00:00:00 2001 From: Gabriel Schneider Date: Thu, 24 Sep 2026 13:04:20 -0300 Subject: Allocate tree-sitter's external scanners from the same Zig allocator as its runtime syntax.zig points the tree-sitter runtime at a Zig allocator, but the grammars' external scanners did not follow it. A grammar's tree_sitter/alloc.h maps ts_malloc to the runtime's current allocator only when TREE_SITTER_REUSE_ALLOCATOR is defined, and plain malloc otherwise, so every scanner's state and its arrays came from libc. Five scanners (bash, markdown, markdown_inline, python, typst) also call malloc, calloc, realloc and free by name. build.zig now compiles every grammar with TREE_SITTER_REUSE_ALLOCATOR and forces in src/tree_sitter_heap.h, which includes stdlib.h and then defines the four names as calls through ts_current_malloc and friends. A header forced in from the build rather than a patch to the grammars keeps zig-pkg pristine. No grammar archive refers to libc's allocator any more; only the runtime does, for its defaults. A scanner frees only what it allocated itself, and the syntax tests that create and destroy a parser for every grammar under a leak-checking allocator pass. Co-Authored-By: Claude Opus 5.5 (1M context) --- build.zig | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'build.zig') diff --git a/build.zig b/build.zig index c70f52b2..f0675a70 100644 --- a/build.zig +++ b/build.zig @@ -390,7 +390,17 @@ pub fn build(b: *std.Build) void { .root_module = b.createModule(.{ .target = target, .optimize = c_optimize, .link_libc = !is_web }), .linkage = .static, }); - const ts_cflags = [_][]const u8{"-fno-sanitize=function"}; + // Every allocation a grammar's external scanner makes goes to the + // runtime's allocator, which syntax.zig points at a Zig one: + // TREE_SITTER_REUSE_ALLOCATOR for the scanners that use + // ts_malloc, and src/tree_sitter_heap.h for the ones that call + // malloc themselves. + const ts_cflags = [_][]const u8{ + "-fno-sanitize=function", + "-DTREE_SITTER_REUSE_ALLOCATOR", + "-include", + b.path("src/tree_sitter_heap.h").getPath(b), + }; lib.root_module.addCSourceFile(.{ .file = dep.path(g.src ++ "/parser.c"), .flags = &ts_cflags }); if (g.scanner) lib.root_module.addCSourceFile(.{ .file = dep.path(g.src ++ "/scanner.c"), .flags = &ts_cflags }); lib.root_module.addIncludePath(dep.path(g.src)); -- cgit v1.3