From 507b1207b788f61ab88d50ad8373a38f5376bf93 Mon Sep 17 00:00:00 2001 From: CJ van den Berg Date: Thu, 9 Apr 2026 18:28:38 +0200 Subject: [PATCH 1/3] test: add trivial test to make sure we actually build --- build.zig | 18 ++++++++++++++++++ src/syntax.zig | 8 ++++++++ 2 files changed, 26 insertions(+) diff --git a/build.zig b/build.zig index 245c497..a069e72 100644 --- a/build.zig +++ b/build.zig @@ -182,11 +182,29 @@ pub fn build(b: *std.Build) void { }, }); + const tests = b.addTest(.{ + .root_module = b.createModule(.{ + .root_source_file = b.path("src/syntax.zig"), + .target = target, + .optimize = optimize, + .imports = &.{ + .{ .name = "build_options", .module = options_mod }, + .{ .name = "cbor", .module = cbor_dep.module("cbor") }, + .{ .name = "treez", .module = tree_sitter_dep.module("treez") }, + }, + }), + }); + if (use_tree_sitter) { const ts_bin_query_gen_step = b.addRunArtifact(ts_bin_query_gen); const output = ts_bin_query_gen_step.addOutputFileArg("bin_queries.cbor"); syntax_mod.addAnonymousImport("syntax_bin_queries", .{ .root_source_file = output }); + tests.root_module.addAnonymousImport("syntax_bin_queries", .{ .root_source_file = output }); } + + const test_run_cmd = b.addRunArtifact(tests); + const test_step = b.step("test", "Run unit tests"); + test_step.dependOn(&test_run_cmd.step); } fn ts_queryfile(b: *std.Build, dep: *std.Build.Dependency, bin_gen: *std.Build.Step.Compile, comptime sub_path: []const u8) void { diff --git a/src/syntax.zig b/src/syntax.zig index 455a278..cc45eb8 100644 --- a/src/syntax.zig +++ b/src/syntax.zig @@ -482,3 +482,11 @@ pub fn count_error_nodes(self: *const Self) usize { }; return error_count; } + +test "simple build and link test" { + const zig_file_type = @import("file_type.zig").get_by_name_static("zig") orelse return error.TestFailed; + const query_cache = try QueryCache.create(std.testing.allocator, .{}); + defer query_cache.deinit(); + const syntax = try create(zig_file_type, std.testing.allocator, query_cache); + _ = syntax; +} From 5a32cf3ffb8fd5e2bd922155ba46df048a2c629d Mon Sep 17 00:00:00 2001 From: CJ van den Berg Date: Thu, 9 Apr 2026 18:41:20 +0200 Subject: [PATCH 2/3] fix: prevent leak in testcase --- src/syntax.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/syntax.zig b/src/syntax.zig index cc45eb8..6aae106 100644 --- a/src/syntax.zig +++ b/src/syntax.zig @@ -488,5 +488,5 @@ test "simple build and link test" { const query_cache = try QueryCache.create(std.testing.allocator, .{}); defer query_cache.deinit(); const syntax = try create(zig_file_type, std.testing.allocator, query_cache); - _ = syntax; + syntax.destroy(); } From 5bc4bcba1552a597d1a6a1f3bf8433ba710b6a90 Mon Sep 17 00:00:00 2001 From: Ryan Mehri Date: Sat, 11 Apr 2026 12:55:38 -0400 Subject: [PATCH 3/3] fix: memory leak with injections query backported to zig-0.15 branch --- src/QueryCache.zig | 2 +- src/syntax.zig | 20 +++++++++++++++----- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/src/QueryCache.zig b/src/QueryCache.zig index 24f6436..024f50b 100644 --- a/src/QueryCache.zig +++ b/src/QueryCache.zig @@ -171,7 +171,7 @@ fn ReturnType(comptime query_type: QueryType) type { pub fn get(self: *Self, file_type: FileType, comptime query_type: QueryType) Error!ReturnType(query_type) { const query = try self.get_cached_query(try self.get_cache_entry(file_type, query_type)); - self.add_ref_locked(); + if (query != null) self.add_ref_locked(); return switch (@typeInfo(ReturnType(query_type))) { .optional => |_| query, else => query.?, diff --git a/src/syntax.zig b/src/syntax.zig index 6aae106..e461603 100644 --- a/src/syntax.zig +++ b/src/syntax.zig @@ -49,7 +49,7 @@ pub fn create(file_type: FileType, allocator: std.mem.Allocator, query_cache: *Q const query = try query_cache.get(file_type, .highlights); errdefer query_cache.release(query, .highlights); const errors_query = try query_cache.get(file_type, .errors); - errdefer query_cache.release(errors_query, .highlights); + errdefer query_cache.release(errors_query, .errors); const injections = try query_cache.get(file_type, .injections); errdefer if (injections) |injections_| query_cache.release(injections_, .injections); const self = try allocator.create(Self); @@ -85,7 +85,7 @@ pub fn destroy(self: *Self) void { if (self.content) |c| self.allocator.free(c); if (self.tree) |tree| tree.destroy(); self.query_cache.release(self.query, .highlights); - self.query_cache.release(self.errors_query, .highlights); + self.query_cache.release(self.errors_query, .errors); if (self.injections) |injections| self.query_cache.release(injections, .injections); self.parser.destroy(); self.allocator.destroy(self); @@ -484,9 +484,19 @@ pub fn count_error_nodes(self: *const Self) usize { } test "simple build and link test" { + const gpa = std.testing.allocator; + const zig_file_type = @import("file_type.zig").get_by_name_static("zig") orelse return error.TestFailed; - const query_cache = try QueryCache.create(std.testing.allocator, .{}); + const query_cache = try QueryCache.create(gpa, .{}); defer query_cache.deinit(); - const syntax = try create(zig_file_type, std.testing.allocator, query_cache); - syntax.destroy(); + const syntax = try create(zig_file_type, gpa, query_cache); + defer syntax.destroy(); + + const content = try std.fs.cwd().readFileAlloc(gpa, "src/syntax.zig", std.math.maxInt(usize)); + defer gpa.free(content); + try syntax.refresh_full(content); + + try syntax.render({}, struct { + fn cb(_: void, _: Range, _: []const u8, _: u32, _: usize, _: *const Node) error{Stop}!void {} + }.cb, null); }