| ... | @@ -1,29 +1,10 @@ | ... | @@ -1,29 +1,10 @@ |
| 1 | const std = @import("std"); | 1 | const std = @import("std"); |
| 2 | const link = @import("link.zig"); | 2 | const link = @import("link.zig"); |
| 3 | const Module = @import("Module.zig"); | 3 | const Module = @import("Module.zig"); |
| 4 | const ErrorMsg = Module.ErrorMsg; | | |
| 5 | const Allocator = std.mem.Allocator; | 4 | const Allocator = std.mem.Allocator; |
| 6 | const zir = @import("zir.zig"); | 5 | const zir = @import("zir.zig"); |
| 7 | const Package = @import("Package.zig"); | 6 | const Package = @import("Package.zig"); |
| 8 | | 7 | |
| 9 | test "find-offset" { | | |
| 10 | std.testing.expectEqual(findOffset("hello123", 1, 8), 7); | | |
| 11 | const testmsg = | | |
| 12 | \\@noreturn = primitive(noreturn) | | |
| 13 | \\ | | |
| 14 | \\@start_fnty = fntype([], @noreturn, cc=Naked) | | |
| 15 | \\@start = fn(@start_fnty, { | | |
| 16 | \\ %0 = call(@notafunc, []) | | |
| 17 | \\}) | | |
| 18 | ; | | |
| 19 | std.testing.expectEqual(findOffset(testmsg, 2, 1), 32); | | |
| 20 | std.testing.expectEqual(findOffset(testmsg, 3, 1), 33); | | |
| 21 | std.testing.expectEqual(findOffset(testmsg, 3, 10), 42); | | |
| 22 | std.testing.expectEqual(findOffset(testmsg, 4, 1), 79); | | |
| 23 | std.testing.expectEqual(findOffset(testmsg, 5, 1), 106); | | |
| 24 | std.testing.expectEqual(findOffset(testmsg, 5, 13), 118); | | |
| 25 | } | | |
| 26 | | | |
| 27 | test "self-hosted" { | 8 | test "self-hosted" { |
| 28 | var ctx: TestContext = undefined; | 9 | var ctx: TestContext = undefined; |
| 29 | try ctx.init(); | 10 | try ctx.init(); |
| ... | @@ -34,26 +15,11 @@ test "self-hosted" { | ... | @@ -34,26 +15,11 @@ test "self-hosted" { |
| 34 | try ctx.run(); | 15 | try ctx.run(); |
| 35 | } | 16 | } |
| 36 | | 17 | |
| 37 | /// Finds the raw byte offset of line:column in src. This is not a performant implementation, | 18 | const ErrorMsg = struct { |
| 38 | /// as it should only ever be called rarely and it is better to focus on readability. | 19 | msg: []const u8, |
| 39 | fn findOffset(src: []const u8, line: usize, column: usize) ?usize { | 20 | line: u32, |
| 40 | // "0000000001" | 21 | column: u32, |
| 41 | // 1:10 | 22 | }; |
| 42 | // | | |
| 43 | var current_line: usize = 1; | | |
| 44 | var current_column: usize = 1; | | |
| 45 | for (src) |char, index| { | | |
| 46 | if (current_line == line and current_column == column) { | | |
| 47 | return index; | | |
| 48 | } | | |
| 49 | if (char == '\n') { | | |
| 50 | current_line += 1; | | |
| 51 | current_column = 0; | | |
| 52 | } | | |
| 53 | current_column += 1; | | |
| 54 | } | | |
| 55 | return null; | | |
| 56 | } | | |
| 57 | | 23 | |
| 58 | pub const TestContext = struct { | 24 | pub const TestContext = struct { |
| 59 | // TODO: remove these. They are deprecated. | 25 | // TODO: remove these. They are deprecated. |
| ... | @@ -115,7 +81,7 @@ pub const TestContext = struct { | ... | @@ -115,7 +81,7 @@ pub const TestContext = struct { |
| 115 | cross_target: std.zig.CrossTarget, | 81 | cross_target: std.zig.CrossTarget, |
| 116 | }; | 82 | }; |
| 117 | | 83 | |
| 118 | pub const ZIRStageType = enum { | 84 | pub const ZIRUpdateType = enum { |
| 119 | /// A transformation stage transforms the input ZIR and tests against | 85 | /// A transformation stage transforms the input ZIR and tests against |
| 120 | /// the expected output | 86 | /// the expected output |
| 121 | Transformation, | 87 | Transformation, |
| ... | @@ -129,7 +95,7 @@ pub const TestContext = struct { | ... | @@ -129,7 +95,7 @@ pub const TestContext = struct { |
| 129 | Compiles, | 95 | Compiles, |
| 130 | }; | 96 | }; |
| 131 | | 97 | |
| 132 | pub const ZIRStage = struct { | 98 | pub const ZIRUpdate = struct { |
| 133 | /// The input to the current stage. We simulate an incremental update | 99 | /// The input to the current stage. We simulate an incremental update |
| 134 | /// with the file's contents changed to this value each stage. | 100 | /// with the file's contents changed to this value each stage. |
| 135 | /// | 101 | /// |
| ... | @@ -138,7 +104,7 @@ pub const TestContext = struct { | ... | @@ -138,7 +104,7 @@ pub const TestContext = struct { |
| 138 | /// you can keep it mostly consistent, with small changes, testing the | 104 | /// you can keep it mostly consistent, with small changes, testing the |
| 139 | /// effects of the incremental compilation. | 105 | /// effects of the incremental compilation. |
| 140 | src: [:0]const u8, | 106 | src: [:0]const u8, |
| 141 | case: union(ZIRStageType) { | 107 | case: union(ZIRUpdateType) { |
| 142 | /// The expected output ZIR | 108 | /// The expected output ZIR |
| 143 | Transformation: []const u8, | 109 | Transformation: []const u8, |
| 144 | /// A slice containing the expected errors *in sequential order*. | 110 | /// A slice containing the expected errors *in sequential order*. |
| ... | @@ -175,14 +141,14 @@ pub const TestContext = struct { | ... | @@ -175,14 +141,14 @@ pub const TestContext = struct { |
| 175 | /// such as QEMU is required for tests to complete. | 141 | /// such as QEMU is required for tests to complete. |
| 176 | /// | 142 | /// |
| 177 | target: std.zig.CrossTarget, | 143 | target: std.zig.CrossTarget, |
| 178 | stages: []ZIRStage, | 144 | stages: []ZIRUpdate, |
| 179 | }; | 145 | }; |
| 180 | | 146 | |
| 181 | pub fn addZIRCase( | 147 | pub fn addZIRCase( |
| 182 | ctx: *TestContext, | 148 | ctx: *TestContext, |
| 183 | name: []const u8, | 149 | name: []const u8, |
| 184 | target: std.zig.CrossTarget, | 150 | target: std.zig.CrossTarget, |
| 185 | stages: []ZIRStage, | 151 | stages: []ZIRUpdate, |
| 186 | ) !void { | 152 | ) !void { |
| 187 | const case = .{ | 153 | const case = .{ |
| 188 | .name = name, | 154 | .name = name, |
| ... | @@ -233,21 +199,34 @@ pub const TestContext = struct { | ... | @@ -233,21 +199,34 @@ pub const TestContext = struct { |
| 233 | ) void { | 199 | ) void { |
| 234 | var array = std.ArrayList(ErrorMsg).init(ctx.zir_error_cases.allocator); | 200 | var array = std.ArrayList(ErrorMsg).init(ctx.zir_error_cases.allocator); |
| 235 | for (expected_errors) |e| { | 201 | for (expected_errors) |e| { |
| 236 | const line_index = std.mem.indexOf(u8, e, ":"); | 202 | var cur = e; |
| | 203 | const err = cur[0..7]; |
| | 204 | if (!std.mem.eql(u8, err, "error: ")) { |
| | 205 | std.debug.panic("Only error messages are currently supported, received {}\n", .{e}); |
| | 206 | } |
| | 207 | cur = cur[7..]; |
| | 208 | var line_index = std.mem.indexOf(u8, cur, ":"); |
| 237 | if (line_index == null) { | 209 | if (line_index == null) { |
| 238 | std.debug.panic("Invalid test: error must be specified as 'line:column:msg', found '{}'", .{e}); | 210 | std.debug.panic("Invalid test: error must be specified as 'error: line:column: msg', found '{}'", .{e}); |
| 239 | } | 211 | } |
| 240 | const column_index = std.mem.indexOf(u8, e[line_index.? + 1 ..], ":"); | 212 | const line = std.fmt.parseInt(u32, cur[0..line_index.?], 10) catch @panic("Unable to parse line number"); |
| | 213 | cur = cur[line_index.? + 1 ..]; |
| | 214 | const column_index = std.mem.indexOf(u8, cur, ":"); |
| 241 | if (column_index == null) { | 215 | if (column_index == null) { |
| 242 | std.debug.panic("Invalid test: error must be specified as 'line:column:msg', found '{}'", .{e}); | 216 | std.debug.panic("Invalid test: error must be specified as 'error: line:column: msg', found '{}'", .{e}); |
| | 217 | } |
| | 218 | const column = std.fmt.parseInt(u32, cur[0..column_index.?], 10) catch @panic("Unable to parse column number"); |
| | 219 | std.debug.assert(cur[column_index.? + 1] == ' '); |
| | 220 | const msg = cur[column_index.? + 2 ..]; |
| | 221 | |
| | 222 | if (line == 0 or column == 0) { |
| | 223 | @panic("Invalid test: error line and column must be specified starting at one!"); |
| 243 | } | 224 | } |
| 244 | const line = std.fmt.parseInt(usize, e[0..line_index.?], 10) catch @panic("Unable to parse line number"); | 225 | |
| 245 | const column = std.fmt.parseInt(usize, e[line_index.? + 1 ..][0..column_index.?], 10) catch @panic("Unable to parse column number"); | 226 | array.append(.{ |
| 246 | const msg = e[line_index.? + 1 ..][column_index.? + 1 ..]; | | |
| 247 | const offset = findOffset(src, line, column) orelse std.debug.panic("Unable to match {}:{} to byte offset!", .{ line, column }); | | |
| 248 | array.append(ErrorMsg{ | | |
| 249 | .byte_offset = offset, | | |
| 250 | .msg = msg, | 227 | .msg = msg, |
| | 228 | .line = line - 1, |
| | 229 | .column = column - 1, |
| 251 | }) catch unreachable; | 230 | }) catch unreachable; |
| 252 | } | 231 | } |
| 253 | ctx.zir_error_cases.append(.{ | 232 | ctx.zir_error_cases.append(.{ |
| ... | @@ -264,6 +243,7 @@ pub const TestContext = struct { | ... | @@ -264,6 +243,7 @@ pub const TestContext = struct { |
| 264 | .zir_cmp_output_cases = std.ArrayList(ZIRCompareOutputCase).init(allocator), | 243 | .zir_cmp_output_cases = std.ArrayList(ZIRCompareOutputCase).init(allocator), |
| 265 | .zir_transform_cases = std.ArrayList(ZIRTransformCase).init(allocator), | 244 | .zir_transform_cases = std.ArrayList(ZIRTransformCase).init(allocator), |
| 266 | .zir_error_cases = std.ArrayList(ZIRErrorCase).init(allocator), | 245 | .zir_error_cases = std.ArrayList(ZIRErrorCase).init(allocator), |
| | 246 | .zir_cases = std.ArrayList(ZIRCase).init(allocator), |
| 267 | }; | 247 | }; |
| 268 | } | 248 | } |
| 269 | | 249 | |
| ... | @@ -274,6 +254,7 @@ pub const TestContext = struct { | ... | @@ -274,6 +254,7 @@ pub const TestContext = struct { |
| 274 | self.zir_error_cases.allocator.free(e.expected_errors); | 254 | self.zir_error_cases.allocator.free(e.expected_errors); |
| 275 | } | 255 | } |
| 276 | self.zir_error_cases.deinit(); | 256 | self.zir_error_cases.deinit(); |
| | 257 | self.zir_cases.deinit(); |
| 277 | self.* = undefined; | 258 | self.* = undefined; |
| 278 | } | 259 | } |
| 279 | | 260 | |
| ... | @@ -343,9 +324,9 @@ pub const TestContext = struct { | ... | @@ -343,9 +324,9 @@ pub const TestContext = struct { |
| 343 | | 324 | |
| 344 | for (case.stages) |s| { | 325 | for (case.stages) |s| { |
| 345 | // TODO: remove before committing. This is for ZLS ;) | 326 | // TODO: remove before committing. This is for ZLS ;) |
| 346 | const stage: ZIRStage = s; | 327 | const stage: ZIRUpdate = s; |
| 347 | | 328 | |
| 348 | var stage_node = prg_node.start("stage", 4); | 329 | var stage_node = prg_node.start("update", 4); |
| 349 | stage_node.activate(); | 330 | stage_node.activate(); |
| 350 | defer stage_node.end(); | 331 | defer stage_node.end(); |
| 351 | | 332 | |
| ... | @@ -592,74 +573,33 @@ pub const TestContext = struct { | ... | @@ -592,74 +573,33 @@ pub const TestContext = struct { |
| 592 | e.* = false; | 573 | e.* = false; |
| 593 | } | 574 | } |
| 594 | | 575 | |
| 595 | // TODO: check the input error list in sequential order, manually | 576 | var all_errors = try module.getAllErrorsAlloc(); |
| 596 | // incrementing indices when needed. This would allow deduplicating the | 577 | defer all_errors.deinit(allocator); |
| 597 | // following three blocks into one, and the restriction it imposes on | 578 | for (all_errors.list) |e| { |
| 598 | // test writers is one that naturally flows anyways. | 579 | var handled = false; |
| 599 | { | 580 | for (case.expected_errors) |ex, i| { |
| 600 | var i = module.failed_files.iterator(); | 581 | if (e.line == ex.line and e.column == ex.column and std.mem.eql(u8, ex.msg, e.msg)) { |
| 601 | while (i.next()) |pair| { | 582 | if (handled_errors[i]) { |
| 602 | const v1 = pair.value.*; | 583 | err = error.ErrorReceivedMultipleTimes; |
| 603 | var handled = false; | 584 | std.debug.warn("Received error multiple times: {}\n", .{e.msg}); |
| 604 | for (case.expected_errors) |e, index| { | 585 | } else { |
| 605 | if (!handled_errors[index]) { | 586 | handled_errors[i] = true; |
| 606 | if (v1.byte_offset == e.byte_offset and std.mem.eql(u8, v1.msg, e.msg)) { | 587 | handled = true; |
| 607 | handled_errors[index] = true; | | |
| 608 | handled = true; | | |
| 609 | break; | | |
| 610 | } | | |
| 611 | } | 588 | } |
| 612 | } | 589 | break; |
| 613 | if (!handled) { | | |
| 614 | err = error.UnexpectedError; | | |
| 615 | std.debug.warn("Unexpected file error: {}\n", .{v1}); | | |
| 616 | } | 590 | } |
| 617 | } | 591 | } |
| 618 | } | 592 | if (!handled) { |
| 619 | { | 593 | err = error.ErrorNotExpected; |
| 620 | var i = module.failed_decls.iterator(); | 594 | std.debug.warn("Received an unexpected error: {}:{}: {}\n", .{ e.line, e.column, e.msg }); |
| 621 | while (i.next()) |pair| { | | |
| 622 | const v1 = pair.value.*; | | |
| 623 | var handled = false; | | |
| 624 | for (case.expected_errors) |e, index| { | | |
| 625 | if (!handled_errors[index]) { | | |
| 626 | if (v1.byte_offset == e.byte_offset and std.mem.eql(u8, v1.msg, e.msg)) { | | |
| 627 | handled_errors[index] = true; | | |
| 628 | handled = true; | | |
| 629 | break; | | |
| 630 | } | | |
| 631 | } | | |
| 632 | } | | |
| 633 | if (!handled) { | | |
| 634 | err = error.UnexpectedError; | | |
| 635 | std.debug.warn("Unexpected decl error: {}\n", .{v1}); | | |
| 636 | } | | |
| 637 | } | | |
| 638 | } | | |
| 639 | { | | |
| 640 | var i = module.failed_exports.iterator(); | | |
| 641 | while (i.next()) |pair| { | | |
| 642 | const v1 = pair.value.*; | | |
| 643 | var handled = false; | | |
| 644 | for (case.expected_errors) |e, index| { | | |
| 645 | if (!handled_errors[index]) { | | |
| 646 | if (v1.byte_offset == e.byte_offset and std.mem.eql(u8, v1.msg, e.msg)) { | | |
| 647 | handled_errors[index] = true; | | |
| 648 | handled = true; | | |
| 649 | break; | | |
| 650 | } | | |
| 651 | } | | |
| 652 | } | | |
| 653 | if (!handled) { | | |
| 654 | err = error.UnexpectedError; | | |
| 655 | std.debug.warn("Unexpected export error: {}\n", .{v1}); | | |
| 656 | } | | |
| 657 | } | 595 | } |
| 658 | } | 596 | } |
| | 597 | |
| 659 | for (handled_errors) |e, i| { | 598 | for (handled_errors) |e, i| { |
| 660 | if (!e) { | 599 | if (!e) { |
| 661 | err = error.MissingExpectedError; | 600 | err = error.MissingExpectedError; |
| 662 | std.debug.warn("Did not receive error: {}\n", .{case.expected_errors[i].msg}); | 601 | const er = case.expected_errors[i]; |
| | 602 | std.debug.warn("Did not receive error: {}:{}: {}\n", .{ er.line, er.column, er.msg }); |
| 663 | } | 603 | } |
| 664 | } | 604 | } |
| 665 | | 605 | |