mymarkdown

My markdown
git clone https://git.grace.moe/mymarkdown
Log | Files | Refs

commit 71831825ee34cefa215e26379e45c96d9b9f4e14
parent 2c4a3e2b7a887a254b827cc4fef5e8f5a17a530d
Author: gracefu <81774659+gracefuu@users.noreply.github.com>
Date:   Thu, 22 May 2025 04:49:33 +0800

fix leak when allocation fails on final dupe

Diffstat:
Msrc/AstGen3.zig | 24++++++++++++++++++------
Msrc/test.zig | 65+++++++++++++++++++++++++++++++++++++++++++++++------------------
2 files changed, 65 insertions(+), 24 deletions(-)

diff --git a/src/AstGen3.zig b/src/AstGen3.zig @@ -324,16 +324,28 @@ pub fn parse( } if (output_gpa) |gpa2| { + const nodes = try gpa2.dupe(Node, ast.nodes.items); + errdefer gpa2.free(nodes); + const errors = try gpa2.dupe(Error, ast.errors.items); + errdefer gpa2.free(errors); + const extra = try gpa2.dupe(u32, ast.extra.items); + errdefer gpa2.free(extra); return .{ - .nodes = try gpa2.dupe(Node, ast.nodes.items), - .errors = try gpa2.dupe(Error, ast.errors.items), - .extra = try gpa2.dupe(u32, ast.extra.items), + .nodes = nodes, + .errors = errors, + .extra = extra, }; } else { + const nodes = try ast.nodes.toOwnedSlice(gpa); + errdefer gpa.free(nodes); + const errors = try ast.errors.toOwnedSlice(gpa); + errdefer gpa.free(errors); + const extra = try ast.extra.toOwnedSlice(gpa); + errdefer gpa.free(extra); return .{ - .nodes = try ast.nodes.toOwnedSlice(gpa), - .errors = try ast.errors.toOwnedSlice(gpa), - .extra = try ast.extra.toOwnedSlice(gpa), + .nodes = nodes, + .errors = errors, + .extra = extra, }; } } diff --git a/src/test.zig b/src/test.zig @@ -6,8 +6,8 @@ const PADDING = @import("padded_str.zig").PADDING; const GeneralPurposeAllocator = std.heap.GeneralPurposeAllocator(.{}); const ArenaAllocator = std.heap.ArenaAllocator; -fn readFile(path: []const u8) !std.ArrayList(u8) { - var input_instance: std.ArrayList(u8) = .init(std.testing.allocator); +fn readFile(gpa: std.mem.Allocator, path: []const u8) !std.ArrayList(u8) { + var input_instance: std.ArrayList(u8) = .init(gpa); errdefer input_instance.deinit(); const input_file = try std.fs.cwd().openFile(path, .{}); defer input_file.close(); @@ -17,22 +17,51 @@ fn readFile(path: []const u8) !std.ArrayList(u8) { return input_instance; } -fn testSnapshot(comptime path: []const u8) !void { - const snapshot_path = path ++ ".ast"; - const wrong_path = path ++ ".wrong.ast"; +fn testSnapshot(gpa: std.mem.Allocator, path: []const u8) !void { + return testSnapshotImpl(gpa, path); + // std.testing.checkAllAllocationFailures( + // gpa, + // testSnapshotImpl, + // .{path}, + // ) catch |err| if (err != error.SwallowedOutOfMemoryError) + // return err; +} +fn testSnapshotImpl(gpa: std.mem.Allocator, path: []const u8) !void { + const snapshot_path = try std.mem.concat(gpa, u8, &.{ path, ".ast" }); + defer gpa.free(snapshot_path); + const wrong_path = try std.mem.concat(gpa, u8, &.{ path, ".wrong.ast" }); + defer gpa.free(wrong_path); - var input_instance = try readFile(path); + var input_instance = try readFile(gpa, path); defer input_instance.deinit(); try input_instance.appendNTimes('\n', 128); - const ast = try parse3(std.testing.allocator, null, input_instance.items); - defer ast.deinit(std.testing.allocator); + const ast = try parse3(gpa, null, input_instance.items); + defer ast.deinit(gpa); + // const ast2 = try parse3(gpa, gpa, input_instance.items); + // defer ast2.deinit(gpa); - var output_instance: std.ArrayList(u8) = .init(std.testing.allocator); + var output_instance: std.ArrayList(u8) = .init(gpa); defer output_instance.deinit(); try ast.renderAst(output_instance.writer(), input_instance.items); - var snapshot_instance: std.ArrayList(u8) = readFile(snapshot_path) catch .init(std.testing.allocator); + var snapshot_instance: std.ArrayList(u8) = readFile(gpa, snapshot_path) catch |err| { + const wrong_file = try std.fs.cwd().createFile(wrong_path, .{}); + defer wrong_file.close(); + try wrong_file.writer().writeAll(output_instance.items); + std.debug.print( + \\Snapshot could not be loaded for {[path]s} due to error {[err]}. + \\ + \\Generated output written to {[wrong_path]s}. + \\ + \\If generated output looks good: + \\ + \\ mv {[wrong_path]s} {[snapshot_path]s} + \\ + \\ + , .{ .path = path, .wrong_path = wrong_path, .snapshot_path = snapshot_path, .err = err }); + return err; + }; defer snapshot_instance.deinit(); if (!std.mem.eql(u8, snapshot_instance.items, output_instance.items)) { @@ -61,35 +90,35 @@ fn testSnapshot(comptime path: []const u8) !void { } test "empty" { - try testSnapshot("src/test/empty.my"); + try testSnapshot(std.testing.allocator, "src/test/empty.my"); } test "Happy path paragraph" { - try testSnapshot("src/test/paragraph.my"); + try testSnapshot(std.testing.allocator, "src/test/paragraph.my"); } test "Happy path headings" { - try testSnapshot("src/test/heading.my"); + try testSnapshot(std.testing.allocator, "src/test/heading.my"); } test "Happy path quote" { - try testSnapshot("src/test/quote.my"); + try testSnapshot(std.testing.allocator, "src/test/quote.my"); } test "Happy path list" { - try testSnapshot("src/test/list.my"); + try testSnapshot(std.testing.allocator, "src/test/list.my"); } test "Happy path list elaboration" { - try testSnapshot("src/test/elaboration.my"); + try testSnapshot(std.testing.allocator, "src/test/elaboration.my"); } test "Thematic break" { - try testSnapshot("src/test/thematic_break.my"); + try testSnapshot(std.testing.allocator, "src/test/thematic_break.my"); } test "Sample" { - try testSnapshot("src/test/sample.my"); + try testSnapshot(std.testing.allocator, "src/test/sample.my"); } test "Super long line" {