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:
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" {