From c92816fbefe9d789d3f3c13f319c949c8b97ad01 Mon Sep 17 00:00:00 2001 From: Noam Preil Date: Mon, 15 Jun 2020 17:51:29 -0400 Subject: [PATCH] [Stage2/Testing] ZIR tests for expected errors --- src-self-hosted/test.zig | 142 ++++++++++++++++++++++++++++++--- test/stage2/compile_errors.zig | 21 +++++ 2 files changed, 154 insertions(+), 9 deletions(-) diff --git a/src-self-hosted/test.zig b/src-self-hosted/test.zig index 605c973bb..21d357208 100644 --- a/src-self-hosted/test.zig +++ b/src-self-hosted/test.zig @@ -1,6 +1,7 @@ const std = @import("std"); const link = @import("link.zig"); const Module = @import("Module.zig"); +const ErrorMsg = Module.ErrorMsg; const Allocator = std.mem.Allocator; const zir = @import("zir.zig"); const Package = @import("Package.zig"); @@ -18,6 +19,7 @@ test "self-hosted" { pub const TestContext = struct { zir_cmp_output_cases: std.ArrayList(ZIRCompareOutputCase), zir_transform_cases: std.ArrayList(ZIRTransformCase), + zir_error_cases: std.ArrayList(ZIRErrorCase), pub const ZIRCompareOutputCase = struct { name: []const u8, @@ -55,6 +57,15 @@ pub const TestContext = struct { } }; + pub const ZIRErrorCase = struct { + name: []const u8, + src: [:0]const u8, + expected_file_errors: []const ErrorMsg, + expected_decl_errors: []const ErrorMsg, + expected_export_errors: []const ErrorMsg, + cross_target: std.zig.CrossTarget, + }; + pub fn addZIRCompareOutput( ctx: *TestContext, name: []const u8, @@ -87,30 +98,38 @@ pub const TestContext = struct { }) catch unreachable; } - pub fn addZIRMulti( + pub fn addZIRError( ctx: *TestContext, name: []const u8, cross_target: std.zig.CrossTarget, - ) *ZIRTransformCase { - const case = ctx.zir_transform_cases.addOne() catch unreachable; - case.* = .{ + src: [:0]const u8, + expected_file_errors: []const ErrorMsg, + expected_decl_errors: []const ErrorMsg, + expected_export_errors: []const ErrorMsg, + ) void { + ctx.zir_error_cases.append(.{ .name = name, + .src = src, + .expected_file_errors = expected_file_errors, + .expected_decl_errors = expected_decl_errors, + .expected_export_errors = expected_export_errors, .cross_target = cross_target, - .updates = std.ArrayList(ZIRTransformCase.Update).init(std.heap.page_allocator), - }; - return case; + }) catch unreachable; } fn init(self: *TestContext) !void { + const allocator = std.heap.page_allocator; self.* = .{ - .zir_cmp_output_cases = std.ArrayList(ZIRCompareOutputCase).init(std.heap.page_allocator), - .zir_transform_cases = std.ArrayList(ZIRTransformCase).init(std.heap.page_allocator), + .zir_cmp_output_cases = std.ArrayList(ZIRCompareOutputCase).init(allocator), + .zir_transform_cases = std.ArrayList(ZIRTransformCase).init(allocator), + .zir_error_cases = std.ArrayList(ZIRErrorCase).init(allocator), }; } fn deinit(self: *TestContext) void { self.zir_cmp_output_cases.deinit(); self.zir_transform_cases.deinit(); + self.zir_error_cases.deinit(); self.* = undefined; } @@ -133,6 +152,12 @@ pub const TestContext = struct { try self.runOneZIRTransformCase(std.testing.allocator, root_node, case, info.target); try std.testing.allocator_instance.validate(); } + for (self.zir_error_cases.items) |case| { + std.testing.base_allocator_instance.reset(); + const info = try std.zig.system.NativeTargetInfo.detect(std.testing.allocator, case.cross_target); + try self.runOneZIRErrorCase(std.testing.allocator, root_node, case, info.target); + try std.testing.allocator_instance.validate(); + } } fn runOneZIRCmpOutputCase( @@ -300,6 +325,105 @@ pub const TestContext = struct { } } } + + fn runOneZIRErrorCase( + self: *TestContext, + allocator: *Allocator, + root_node: *std.Progress.Node, + case: ZIRErrorCase, + target: std.Target, + ) !void { + var tmp = std.testing.tmpDir(.{}); + defer tmp.cleanup(); + + var prg_node = root_node.start(case.name, 1); + prg_node.activate(); + defer prg_node.end(); + + const tmp_src_path = "test-case.zir"; + try tmp.dir.writeFile(tmp_src_path, case.src); + + const root_pkg = try Package.create(allocator, tmp.dir, ".", tmp_src_path); + defer root_pkg.destroy(); + + var module = try Module.init(allocator, .{ + .target = target, + .output_mode = .Obj, + .optimize_mode = .Debug, + .bin_file_dir = tmp.dir, + .bin_file_path = "test-case.o", + .root_pkg = root_pkg, + }); + defer module.deinit(); + + var module_node = prg_node.start("parse/analysis/codegen", null); + module_node.activate(); + const failed = f: { + module.update() catch break :f true; + break :f false; + }; + if (!failed) { + return error.DidNotFail; + } + module_node.end(); + { + var i = module.failed_files.iterator(); + var index: usize = 0; + while (i.next()) |pair| : (index += 1) { + if (index == case.expected_file_errors.len) { + return error.UnexpectedError; + } + const v1 = pair.value.*; + const v2 = case.expected_file_errors[index]; + if (v1.byte_offset != v2.byte_offset) { + std.debug.warn("Expected error at {}, found it at {}\n", .{ v2.byte_offset, v1.byte_offset }); + return error.ExpectedErrorElsewhere; + } + if (!std.mem.eql(u8, v1.msg, v2.msg)) { + std.debug.warn("Expected '{}', found '{}'\n", .{ v2.msg, v1.msg }); + return error.ExpectedOtherError; + } + } + } + { + var i = module.failed_decls.iterator(); + var index: usize = 0; + while (i.next()) |pair| : (index += 1) { + if (index == case.expected_decl_errors.len) { + return error.UnexpectedError; + } + const v1 = pair.value.*; + const v2 = case.expected_decl_errors[index]; + if (v1.byte_offset != v2.byte_offset) { + std.debug.warn("Expected error at {}, found it at {}\n", .{ v2.byte_offset, v1.byte_offset }); + return error.ExpectedErrorElsewhere; + } + if (!std.mem.eql(u8, v1.msg, v2.msg)) { + std.debug.warn("Expected '{}', found '{}'\n", .{ v2.msg, v1.msg }); + return error.ExpectedOtherError; + } + } + } + { + var i = module.failed_exports.iterator(); + var index: usize = 0; + while (i.next()) |pair| : (index += 1) { + if (index == case.expected_export_errors.len) { + return error.UnexpectedError; + } + const v1 = pair.value.*; + const v2 = case.expected_export_errors[index]; + if (v1.byte_offset != v2.byte_offset) { + std.debug.warn("Expected error at {}, found it at {}\n", .{ v2.byte_offset, v1.byte_offset }); + return error.ExpectedErrorElsewhere; + } + if (!std.mem.eql(u8, v1.msg, v2.msg)) { + std.debug.warn("Expected '{}', found '{}'\n", .{ v2.msg, v1.msg }); + return error.ExpectedOtherError; + } + } + } + } }; fn debugPrintErrors(src: []const u8, errors: var) void { diff --git a/test/stage2/compile_errors.zig b/test/stage2/compile_errors.zig index 9b8dcd91c..e3c009eee 100644 --- a/test/stage2/compile_errors.zig +++ b/test/stage2/compile_errors.zig @@ -1,8 +1,29 @@ const TestContext = @import("../../src-self-hosted/test.zig").TestContext; +const std = @import("std"); + +const ErrorMsg = @import("../../src-self-hosted/Module.zig").ErrorMsg; + +const linux_x64 = std.zig.CrossTarget{ + .cpu_arch = .x86_64, + .os_tag = .linux, +}; pub fn addCases(ctx: *TestContext) !void { // TODO: re-enable these tests. // https://github.com/ziglang/zig/issues/1364 + ctx.addZIRError("test", linux_x64, + \\@noreturn = primitive(noreturn) + \\@void = primitive(void) + \\@usize = primitive(usize) + \\ + \\@start_fnty = fntype([], @noreturn, cc=Naked) + \\@start = fn(@start_fnty, { + \\ %0 = call(%test, []) + \\}) + , &[_]ErrorMsg{.{ + .byte_offset = 168, + .msg = "unrecognized identifier: %test", + }}, &[_]ErrorMsg{}, &[_]ErrorMsg{}); //try ctx.testCompileError( // \\export fn entry() void {}