| author | |
| committer | |
| log | 2ed1ed9b32ae588f6a8997248d69817b5d89a133 |
| tree | 795a5929a71352c469fcd6018bb57ca5a3ebb20c |
| parent | 1c5606af9fdbfa18fb312a35a73c68515967c94d |
Use case:
zig build-exe non_existent_file.zig
Previous behavior:
error.FileNotFound, followed by an error return trace
Behavior after this commit:
error: unable to read non_existent_file.zig: FileNotFound
(end of stderr, exit code 1)
This turns AllErrors.Message into a tagged union which now has the
capability to represent both "plain" errors as well as source-based
errors (with file, line, column, byte offset). The "no entry point found"
error has moved to be a plain error message.4 files changed, 170 insertions(+), 53 deletions(-)
src/Compilation.zig+71-36| ... | ... | @@ -226,20 +226,32 @@ pub const AllErrors = struct { |
| 226 | 226 | arena: std.heap.ArenaAllocator.State, |
| 227 | 227 | list: []const Message, |
| 228 | 228 | |
| 229 | pub const Message = struct { | |
| 230 | src_path: []const u8, | |
| 231 | line: usize, | |
| 232 | column: usize, | |
| 233 | byte_offset: usize, | |
| 234 | msg: []const u8, | |
| 229 | pub const Message = union(enum) { | |
| 230 | src: struct { | |
| 231 | src_path: []const u8, | |
| 232 | line: usize, | |
| 233 | column: usize, | |
| 234 | byte_offset: usize, | |
| 235 | msg: []const u8, | |
| 236 | }, | |
| 237 | plain: struct { | |
| 238 | msg: []const u8, | |
| 239 | }, | |
| 235 | 240 | |
| 236 | 241 | pub fn renderToStdErr(self: Message) void { |
| 237 | std.debug.print("{}:{}:{}: error: {}\n", .{ | |
| 238 | self.src_path, | |
| 239 | self.line + 1, | |
| 240 | self.column + 1, | |
| 241 | self.msg, | |
| 242 | }); | |
| 242 | switch (self) { | |
| 243 | .src => |src| { | |
| 244 | std.debug.print("{s}:{d}:{d}: error: {s}\n", .{ | |
| 245 | src.src_path, | |
| 246 | src.line + 1, | |
| 247 | src.column + 1, | |
| 248 | src.msg, | |
| 249 | }); | |
| 250 | }, | |
| 251 | .plain => |plain| { | |
| 252 | std.debug.print("error: {s}\n", .{plain.msg}); | |
| 253 | }, | |
| 254 | } | |
| 243 | 255 | } |
| 244 | 256 | }; |
| 245 | 257 | |
| ... | ... | @@ -256,13 +268,23 @@ pub const AllErrors = struct { |
| 256 | 268 | ) !void { |
| 257 | 269 | const loc = std.zig.findLineColumn(source, simple_err_msg.byte_offset); |
| 258 | 270 | try errors.append(.{ |
| 259 | .src_path = try arena.allocator.dupe(u8, sub_file_path), | |
| 260 | .msg = try arena.allocator.dupe(u8, simple_err_msg.msg), | |
| 261 | .byte_offset = simple_err_msg.byte_offset, | |
| 262 | .line = loc.line, | |
| 263 | .column = loc.column, | |
| 271 | .src = .{ | |
| 272 | .src_path = try arena.allocator.dupe(u8, sub_file_path), | |
| 273 | .msg = try arena.allocator.dupe(u8, simple_err_msg.msg), | |
| 274 | .byte_offset = simple_err_msg.byte_offset, | |
| 275 | .line = loc.line, | |
| 276 | .column = loc.column, | |
| 277 | }, | |
| 264 | 278 | }); |
| 265 | 279 | } |
| 280 | ||
| 281 | fn addPlain( | |
| 282 | arena: *std.heap.ArenaAllocator, | |
| 283 | errors: *std.ArrayList(Message), | |
| 284 | msg: []const u8, | |
| 285 | ) !void { | |
| 286 | try errors.append(.{ .plain = .{ .msg = msg } }); | |
| 287 | } | |
| 266 | 288 | }; |
| 267 | 289 | |
| 268 | 290 | pub const Directory = struct { |
| ... | ... | @@ -1169,11 +1191,15 @@ pub fn update(self: *Compilation) !void { |
| 1169 | 1191 | // to force a refresh we unload now. |
| 1170 | 1192 | if (module.root_scope.cast(Module.Scope.File)) |zig_file| { |
| 1171 | 1193 | zig_file.unload(module.gpa); |
| 1194 | module.failed_root_src_file = null; | |
| 1172 | 1195 | module.analyzeContainer(&zig_file.root_container) catch |err| switch (err) { |
| 1173 | 1196 | error.AnalysisFail => { |
| 1174 | 1197 | assert(self.totalErrorCount() != 0); |
| 1175 | 1198 | }, |
| 1176 | else => |e| return e, | |
| 1199 | error.OutOfMemory => return error.OutOfMemory, | |
| 1200 | else => |e| { | |
| 1201 | module.failed_root_src_file = e; | |
| 1202 | }, | |
| 1177 | 1203 | }; |
| 1178 | 1204 | } else if (module.root_scope.cast(Module.Scope.ZIRModule)) |zir_module| { |
| 1179 | 1205 | zir_module.unload(module.gpa); |
| ... | ... | @@ -1251,7 +1277,8 @@ pub fn totalErrorCount(self: *Compilation) usize { |
| 1251 | 1277 | if (self.bin_file.options.module) |module| { |
| 1252 | 1278 | total += module.failed_decls.items().len + |
| 1253 | 1279 | module.failed_exports.items().len + |
| 1254 | module.failed_files.items().len; | |
| 1280 | module.failed_files.items().len + | |
| 1281 | @boolToInt(module.failed_root_src_file != null); | |
| 1255 | 1282 | } |
| 1256 | 1283 | |
| 1257 | 1284 | // The "no entry point found" error only counts if there are no other errors. |
| ... | ... | @@ -1293,21 +1320,22 @@ pub fn getAllErrorsAlloc(self: *Compilation) !AllErrors { |
| 1293 | 1320 | const source = try decl.scope.getSource(module); |
| 1294 | 1321 | try AllErrors.add(&arena, &errors, decl.scope.subFilePath(), source, err_msg.*); |
| 1295 | 1322 | } |
| 1323 | if (module.failed_root_src_file) |err| { | |
| 1324 | const file_path = try module.root_pkg.root_src_directory.join(&arena.allocator, &[_][]const u8{ | |
| 1325 | module.root_pkg.root_src_path, | |
| 1326 | }); | |
| 1327 | const msg = try std.fmt.allocPrint(&arena.allocator, "unable to read {s}: {s}", .{ | |
| 1328 | file_path, @errorName(err), | |
| 1329 | }); | |
| 1330 | try AllErrors.addPlain(&arena, &errors, msg); | |
| 1331 | } | |
| 1296 | 1332 | } |
| 1297 | 1333 | |
| 1298 | 1334 | if (errors.items.len == 0 and self.link_error_flags.no_entry_point_found) { |
| 1299 | const global_err_src_path = blk: { | |
| 1300 | if (self.bin_file.options.module) |module| break :blk module.root_pkg.root_src_path; | |
| 1301 | if (self.c_source_files.len != 0) break :blk self.c_source_files[0].src_path; | |
| 1302 | if (self.bin_file.options.objects.len != 0) break :blk self.bin_file.options.objects[0]; | |
| 1303 | break :blk "(no file)"; | |
| 1304 | }; | |
| 1305 | 1335 | try errors.append(.{ |
| 1306 | .src_path = global_err_src_path, | |
| 1307 | .line = 0, | |
| 1308 | .column = 0, | |
| 1309 | .byte_offset = 0, | |
| 1310 | .msg = try std.fmt.allocPrint(&arena.allocator, "no entry point found", .{}), | |
| 1336 | .plain = .{ | |
| 1337 | .msg = try std.fmt.allocPrint(&arena.allocator, "no entry point found", .{}), | |
| 1338 | }, | |
| 1311 | 1339 | }); |
| 1312 | 1340 | } |
| 1313 | 1341 | |
| ... | ... | @@ -2644,12 +2672,19 @@ pub fn updateSubCompilation(sub_compilation: *Compilation) !void { |
| 2644 | 2672 | |
| 2645 | 2673 | if (errors.list.len != 0) { |
| 2646 | 2674 | for (errors.list) |full_err_msg| { |
| 2647 | log.err("{}:{}:{}: {}\n", .{ | |
| 2648 | full_err_msg.src_path, | |
| 2649 | full_err_msg.line + 1, | |
| 2650 | full_err_msg.column + 1, | |
| 2651 | full_err_msg.msg, | |
| 2652 | }); | |
| 2675 | switch (full_err_msg) { | |
| 2676 | .src => |src| { | |
| 2677 | log.err("{s}:{d}:{d}: {s}\n", .{ | |
| 2678 | src.src_path, | |
| 2679 | src.line + 1, | |
| 2680 | src.column + 1, | |
| 2681 | src.msg, | |
| 2682 | }); | |
| 2683 | }, | |
| 2684 | .plain => |plain| { | |
| 2685 | log.err("{s}", .{plain.msg}); | |
| 2686 | }, | |
| 2687 | } | |
| 2653 | 2688 | } |
| 2654 | 2689 | return error.BuildingLibCObjectFailed; |
| 2655 | 2690 | } |
src/Module.zig+3| ... | ... | @@ -78,6 +78,9 @@ import_table: std.StringArrayHashMapUnmanaged(*Scope.File) = .{}, |
| 78 | 78 | /// previous analysis. |
| 79 | 79 | generation: u32 = 0, |
| 80 | 80 | |
| 81 | /// When populated it means there was an error opening/reading the root source file. | |
| 82 | failed_root_src_file: ?anyerror = null, | |
| 83 | ||
| 81 | 84 | stage1_flags: packed struct { |
| 82 | 85 | have_winmain: bool = false, |
| 83 | 86 | have_wwinmain: bool = false, |
src/test.zig+94-15| ... | ... | @@ -22,10 +22,52 @@ test "self-hosted" { |
| 22 | 22 | try ctx.run(); |
| 23 | 23 | } |
| 24 | 24 | |
| 25 | const ErrorMsg = struct { | |
| 26 | msg: []const u8, | |
| 27 | line: u32, | |
| 28 | column: u32, | |
| 25 | const ErrorMsg = union(enum) { | |
| 26 | src: struct { | |
| 27 | msg: []const u8, | |
| 28 | line: u32, | |
| 29 | column: u32, | |
| 30 | }, | |
| 31 | plain: struct { | |
| 32 | msg: []const u8, | |
| 33 | }, | |
| 34 | ||
| 35 | fn init(other: Compilation.AllErrors.Message) ErrorMsg { | |
| 36 | switch (other) { | |
| 37 | .src => |src| return .{ | |
| 38 | .src = .{ | |
| 39 | .msg = src.msg, | |
| 40 | .line = @intCast(u32, src.line), | |
| 41 | .column = @intCast(u32, src.column), | |
| 42 | }, | |
| 43 | }, | |
| 44 | .plain => |plain| return .{ | |
| 45 | .plain = .{ | |
| 46 | .msg = plain.msg, | |
| 47 | }, | |
| 48 | }, | |
| 49 | } | |
| 50 | } | |
| 51 | ||
| 52 | pub fn format( | |
| 53 | self: ErrorMsg, | |
| 54 | comptime fmt: []const u8, | |
| 55 | options: std.fmt.FormatOptions, | |
| 56 | writer: anytype, | |
| 57 | ) !void { | |
| 58 | switch (self) { | |
| 59 | .src => |src| { | |
| 60 | return writer.print(":{d}:{d}: error: {s}", .{ | |
| 61 | src.line + 1, | |
| 62 | src.column + 1, | |
| 63 | src.msg, | |
| 64 | }); | |
| 65 | }, | |
| 66 | .plain => |plain| { | |
| 67 | return writer.print("error: {s}", .{plain.msg}); | |
| 68 | }, | |
| 69 | } | |
| 70 | } | |
| 29 | 71 | }; |
| 30 | 72 | |
| 31 | 73 | pub const TestContext = struct { |
| ... | ... | @@ -112,7 +154,8 @@ pub const TestContext = struct { |
| 112 | 154 | var array = self.updates.allocator.alloc(ErrorMsg, errors.len) catch unreachable; |
| 113 | 155 | for (errors) |e, i| { |
| 114 | 156 | if (e[0] != ':') { |
| 115 | @panic("Invalid test: error must be specified as follows:\n:line:column: error: message\n=========\n"); | |
| 157 | array[i] = .{ .plain = .{ .msg = e } }; | |
| 158 | continue; | |
| 116 | 159 | } |
| 117 | 160 | var cur = e[1..]; |
| 118 | 161 | var line_index = std.mem.indexOf(u8, cur, ":"); |
| ... | ... | @@ -137,9 +180,11 @@ pub const TestContext = struct { |
| 137 | 180 | } |
| 138 | 181 | |
| 139 | 182 | array[i] = .{ |
| 140 | .msg = msg, | |
| 141 | .line = line - 1, | |
| 142 | .column = column - 1, | |
| 183 | .src = .{ | |
| 184 | .msg = msg, | |
| 185 | .line = line - 1, | |
| 186 | .column = column - 1, | |
| 187 | }, | |
| 143 | 188 | }; |
| 144 | 189 | } |
| 145 | 190 | self.updates.append(.{ .src = src, .case = .{ .Error = array } }) catch unreachable; |
| ... | ... | @@ -544,8 +589,17 @@ pub const TestContext = struct { |
| 544 | 589 | defer all_errors.deinit(allocator); |
| 545 | 590 | if (all_errors.list.len != 0) { |
| 546 | 591 | std.debug.print("\nErrors occurred updating the compilation:\n================\n", .{}); |
| 547 | for (all_errors.list) |err| { | |
| 548 | std.debug.print(":{}:{}: error: {}\n================\n", .{ err.line + 1, err.column + 1, err.msg }); | |
| 592 | for (all_errors.list) |err_msg| { | |
| 593 | switch (err_msg) { | |
| 594 | .src => |src| { | |
| 595 | std.debug.print(":{d}:{d}: error: {s}\n================\n", .{ | |
| 596 | src.line + 1, src.column + 1, src.msg, | |
| 597 | }); | |
| 598 | }, | |
| 599 | .plain => |plain| { | |
| 600 | std.debug.print("error: {s}\n================\n", .{plain.msg}); | |
| 601 | }, | |
| 602 | } | |
| 549 | 603 | } |
| 550 | 604 | if (case.cbe) { |
| 551 | 605 | const C = comp.bin_file.cast(link.File.C).?; |
| ... | ... | @@ -618,12 +672,34 @@ pub const TestContext = struct { |
| 618 | 672 | defer all_errors.deinit(allocator); |
| 619 | 673 | for (all_errors.list) |a| { |
| 620 | 674 | for (e) |ex, i| { |
| 621 | if (a.line == ex.line and a.column == ex.column and std.mem.eql(u8, ex.msg, a.msg)) { | |
| 622 | handled_errors[i] = true; | |
| 623 | break; | |
| 675 | const a_tag: @TagType(@TypeOf(a)) = a; | |
| 676 | const ex_tag: @TagType(@TypeOf(ex)) = ex; | |
| 677 | switch (a) { | |
| 678 | .src => |src| { | |
| 679 | if (ex_tag != .src) continue; | |
| 680 | ||
| 681 | if (src.line == ex.src.line and | |
| 682 | src.column == ex.src.column and | |
| 683 | std.mem.eql(u8, ex.src.msg, src.msg)) | |
| 684 | { | |
| 685 | handled_errors[i] = true; | |
| 686 | break; | |
| 687 | } | |
| 688 | }, | |
| 689 | .plain => |plain| { | |
| 690 | if (ex_tag != .plain) continue; | |
| 691 | ||
| 692 | if (std.mem.eql(u8, ex.plain.msg, plain.msg)) { | |
| 693 | handled_errors[i] = true; | |
| 694 | break; | |
| 695 | } | |
| 696 | }, | |
| 624 | 697 | } |
| 625 | 698 | } else { |
| 626 | std.debug.print("{}\nUnexpected error:\n================\n:{}:{}: error: {}\n================\nTest failed.\n", .{ case.name, a.line + 1, a.column + 1, a.msg }); | |
| 699 | std.debug.print( | |
| 700 | "{s}\nUnexpected error:\n================\n{}\n================\nTest failed.\n", | |
| 701 | .{ case.name, ErrorMsg.init(a) }, | |
| 702 | ); | |
| 627 | 703 | std.process.exit(1); |
| 628 | 704 | } |
| 629 | 705 | } |
| ... | ... | @@ -631,7 +707,10 @@ pub const TestContext = struct { |
| 631 | 707 | for (handled_errors) |h, i| { |
| 632 | 708 | if (!h) { |
| 633 | 709 | const er = e[i]; |
| 634 | std.debug.print("{}\nDid not receive error:\n================\n{}:{}: {}\n================\nTest failed.\n", .{ case.name, er.line, er.column, er.msg }); | |
| 710 | std.debug.print( | |
| 711 | "{s}\nDid not receive error:\n================\n{}\n================\nTest failed.\n", | |
| 712 | .{ case.name, er }, | |
| 713 | ); | |
| 635 | 714 | std.process.exit(1); |
| 636 | 715 | } |
| 637 | 716 | } |
test/stage2/test.zig+2-2| ... | ... | @@ -36,7 +36,7 @@ pub fn addCases(ctx: *TestContext) !void { |
| 36 | 36 | { |
| 37 | 37 | var case = ctx.exe("hello world with updates", linux_x64); |
| 38 | 38 | |
| 39 | case.addError("", &[_][]const u8{":1:1: error: no entry point found"}); | |
| 39 | case.addError("", &[_][]const u8{"no entry point found"}); | |
| 40 | 40 | |
| 41 | 41 | // Incorrect return type |
| 42 | 42 | case.addError( |
| ... | ... | @@ -147,7 +147,7 @@ pub fn addCases(ctx: *TestContext) !void { |
| 147 | 147 | |
| 148 | 148 | { |
| 149 | 149 | var case = ctx.exe("hello world with updates", macosx_x64); |
| 150 | case.addError("", &[_][]const u8{":1:1: error: no entry point found"}); | |
| 150 | case.addError("", &[_][]const u8{"no entry point found"}); | |
| 151 | 151 | |
| 152 | 152 | // Incorrect return type |
| 153 | 153 | case.addError( |