| 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,20 +226,32 @@ pub const AllErrors = struct { |
| 226 | arena: std.heap.ArenaAllocator.State, | 226 | arena: std.heap.ArenaAllocator.State, |
| 227 | list: []const Message, | 227 | list: []const Message, |
| 228 | 228 | ||
| 229 | pub const Message = struct { | 229 | pub const Message = union(enum) { |
| 230 | src_path: []const u8, | 230 | src: struct { |
| 231 | line: usize, | 231 | src_path: []const u8, |
| 232 | column: usize, | 232 | line: usize, |
| 233 | byte_offset: usize, | 233 | column: usize, |
| 234 | msg: []const u8, | 234 | byte_offset: usize, |
| 235 | msg: []const u8, | ||
| 236 | }, | ||
| 237 | plain: struct { | ||
| 238 | msg: []const u8, | ||
| 239 | }, | ||
| 235 | 240 | ||
| 236 | pub fn renderToStdErr(self: Message) void { | 241 | pub fn renderToStdErr(self: Message) void { |
| 237 | std.debug.print("{}:{}:{}: error: {}\n", .{ | 242 | switch (self) { |
| 238 | self.src_path, | 243 | .src => |src| { |
| 239 | self.line + 1, | 244 | std.debug.print("{s}:{d}:{d}: error: {s}\n", .{ |
| 240 | self.column + 1, | 245 | src.src_path, |
| 241 | self.msg, | 246 | src.line + 1, |
| 242 | }); | 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,13 +268,23 @@ pub const AllErrors = struct { |
| 256 | ) !void { | 268 | ) !void { |
| 257 | const loc = std.zig.findLineColumn(source, simple_err_msg.byte_offset); | 269 | const loc = std.zig.findLineColumn(source, simple_err_msg.byte_offset); |
| 258 | try errors.append(.{ | 270 | try errors.append(.{ |
| 259 | .src_path = try arena.allocator.dupe(u8, sub_file_path), | 271 | .src = .{ |
| 260 | .msg = try arena.allocator.dupe(u8, simple_err_msg.msg), | 272 | .src_path = try arena.allocator.dupe(u8, sub_file_path), |
| 261 | .byte_offset = simple_err_msg.byte_offset, | 273 | .msg = try arena.allocator.dupe(u8, simple_err_msg.msg), |
| 262 | .line = loc.line, | 274 | .byte_offset = simple_err_msg.byte_offset, |
| 263 | .column = loc.column, | 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 | pub const Directory = struct { | 290 | pub const Directory = struct { |
| ... | @@ -1169,11 +1191,15 @@ pub fn update(self: *Compilation) !void { | ... | @@ -1169,11 +1191,15 @@ pub fn update(self: *Compilation) !void { |
| 1169 | // to force a refresh we unload now. | 1191 | // to force a refresh we unload now. |
| 1170 | if (module.root_scope.cast(Module.Scope.File)) |zig_file| { | 1192 | if (module.root_scope.cast(Module.Scope.File)) |zig_file| { |
| 1171 | zig_file.unload(module.gpa); | 1193 | zig_file.unload(module.gpa); |
| 1194 | module.failed_root_src_file = null; | ||
| 1172 | module.analyzeContainer(&zig_file.root_container) catch |err| switch (err) { | 1195 | module.analyzeContainer(&zig_file.root_container) catch |err| switch (err) { |
| 1173 | error.AnalysisFail => { | 1196 | error.AnalysisFail => { |
| 1174 | assert(self.totalErrorCount() != 0); | 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 | } else if (module.root_scope.cast(Module.Scope.ZIRModule)) |zir_module| { | 1204 | } else if (module.root_scope.cast(Module.Scope.ZIRModule)) |zir_module| { |
| 1179 | zir_module.unload(module.gpa); | 1205 | zir_module.unload(module.gpa); |
| ... | @@ -1251,7 +1277,8 @@ pub fn totalErrorCount(self: *Compilation) usize { | ... | @@ -1251,7 +1277,8 @@ pub fn totalErrorCount(self: *Compilation) usize { |
| 1251 | if (self.bin_file.options.module) |module| { | 1277 | if (self.bin_file.options.module) |module| { |
| 1252 | total += module.failed_decls.items().len + | 1278 | total += module.failed_decls.items().len + |
| 1253 | module.failed_exports.items().len + | 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 | // The "no entry point found" error only counts if there are no other errors. | 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,21 +1320,22 @@ pub fn getAllErrorsAlloc(self: *Compilation) !AllErrors { |
| 1293 | const source = try decl.scope.getSource(module); | 1320 | const source = try decl.scope.getSource(module); |
| 1294 | try AllErrors.add(&arena, &errors, decl.scope.subFilePath(), source, err_msg.*); | 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 | if (errors.items.len == 0 and self.link_error_flags.no_entry_point_found) { | 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 | try errors.append(.{ | 1335 | try errors.append(.{ |
| 1306 | .src_path = global_err_src_path, | 1336 | .plain = .{ |
| 1307 | .line = 0, | 1337 | .msg = try std.fmt.allocPrint(&arena.allocator, "no entry point found", .{}), |
| 1308 | .column = 0, | 1338 | }, |
| 1309 | .byte_offset = 0, | ||
| 1310 | .msg = try std.fmt.allocPrint(&arena.allocator, "no entry point found", .{}), | ||
| 1311 | }); | 1339 | }); |
| 1312 | } | 1340 | } |
| 1313 | 1341 | ||
| ... | @@ -2644,12 +2672,19 @@ pub fn updateSubCompilation(sub_compilation: *Compilation) !void { | ... | @@ -2644,12 +2672,19 @@ pub fn updateSubCompilation(sub_compilation: *Compilation) !void { |
| 2644 | 2672 | ||
| 2645 | if (errors.list.len != 0) { | 2673 | if (errors.list.len != 0) { |
| 2646 | for (errors.list) |full_err_msg| { | 2674 | for (errors.list) |full_err_msg| { |
| 2647 | log.err("{}:{}:{}: {}\n", .{ | 2675 | switch (full_err_msg) { |
| 2648 | full_err_msg.src_path, | 2676 | .src => |src| { |
| 2649 | full_err_msg.line + 1, | 2677 | log.err("{s}:{d}:{d}: {s}\n", .{ |
| 2650 | full_err_msg.column + 1, | 2678 | src.src_path, |
| 2651 | full_err_msg.msg, | 2679 | src.line + 1, |
| 2652 | }); | 2680 | src.column + 1, |
| 2681 | src.msg, | ||
| 2682 | }); | ||
| 2683 | }, | ||
| 2684 | .plain => |plain| { | ||
| 2685 | log.err("{s}", .{plain.msg}); | ||
| 2686 | }, | ||
| 2687 | } | ||
| 2653 | } | 2688 | } |
| 2654 | return error.BuildingLibCObjectFailed; | 2689 | return error.BuildingLibCObjectFailed; |
| 2655 | } | 2690 | } |
src/Module.zig+3| ... | @@ -78,6 +78,9 @@ import_table: std.StringArrayHashMapUnmanaged(*Scope.File) = .{}, | ... | @@ -78,6 +78,9 @@ import_table: std.StringArrayHashMapUnmanaged(*Scope.File) = .{}, |
| 78 | /// previous analysis. | 78 | /// previous analysis. |
| 79 | generation: u32 = 0, | 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 | stage1_flags: packed struct { | 84 | stage1_flags: packed struct { |
| 82 | have_winmain: bool = false, | 85 | have_winmain: bool = false, |
| 83 | have_wwinmain: bool = false, | 86 | have_wwinmain: bool = false, |
src/test.zig+94-15| ... | @@ -22,10 +22,52 @@ test "self-hosted" { | ... | @@ -22,10 +22,52 @@ test "self-hosted" { |
| 22 | try ctx.run(); | 22 | try ctx.run(); |
| 23 | } | 23 | } |
| 24 | 24 | ||
| 25 | const ErrorMsg = struct { | 25 | const ErrorMsg = union(enum) { |
| 26 | msg: []const u8, | 26 | src: struct { |
| 27 | line: u32, | 27 | msg: []const u8, |
| 28 | column: u32, | 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 | pub const TestContext = struct { | 73 | pub const TestContext = struct { |
| ... | @@ -112,7 +154,8 @@ pub const TestContext = struct { | ... | @@ -112,7 +154,8 @@ pub const TestContext = struct { |
| 112 | var array = self.updates.allocator.alloc(ErrorMsg, errors.len) catch unreachable; | 154 | var array = self.updates.allocator.alloc(ErrorMsg, errors.len) catch unreachable; |
| 113 | for (errors) |e, i| { | 155 | for (errors) |e, i| { |
| 114 | if (e[0] != ':') { | 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 | var cur = e[1..]; | 160 | var cur = e[1..]; |
| 118 | var line_index = std.mem.indexOf(u8, cur, ":"); | 161 | var line_index = std.mem.indexOf(u8, cur, ":"); |
| ... | @@ -137,9 +180,11 @@ pub const TestContext = struct { | ... | @@ -137,9 +180,11 @@ pub const TestContext = struct { |
| 137 | } | 180 | } |
| 138 | 181 | ||
| 139 | array[i] = .{ | 182 | array[i] = .{ |
| 140 | .msg = msg, | 183 | .src = .{ |
| 141 | .line = line - 1, | 184 | .msg = msg, |
| 142 | .column = column - 1, | 185 | .line = line - 1, |
| 186 | .column = column - 1, | ||
| 187 | }, | ||
| 143 | }; | 188 | }; |
| 144 | } | 189 | } |
| 145 | self.updates.append(.{ .src = src, .case = .{ .Error = array } }) catch unreachable; | 190 | self.updates.append(.{ .src = src, .case = .{ .Error = array } }) catch unreachable; |
| ... | @@ -544,8 +589,17 @@ pub const TestContext = struct { | ... | @@ -544,8 +589,17 @@ pub const TestContext = struct { |
| 544 | defer all_errors.deinit(allocator); | 589 | defer all_errors.deinit(allocator); |
| 545 | if (all_errors.list.len != 0) { | 590 | if (all_errors.list.len != 0) { |
| 546 | std.debug.print("\nErrors occurred updating the compilation:\n================\n", .{}); | 591 | std.debug.print("\nErrors occurred updating the compilation:\n================\n", .{}); |
| 547 | for (all_errors.list) |err| { | 592 | for (all_errors.list) |err_msg| { |
| 548 | std.debug.print(":{}:{}: error: {}\n================\n", .{ err.line + 1, err.column + 1, 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 | if (case.cbe) { | 604 | if (case.cbe) { |
| 551 | const C = comp.bin_file.cast(link.File.C).?; | 605 | const C = comp.bin_file.cast(link.File.C).?; |
| ... | @@ -618,12 +672,34 @@ pub const TestContext = struct { | ... | @@ -618,12 +672,34 @@ pub const TestContext = struct { |
| 618 | defer all_errors.deinit(allocator); | 672 | defer all_errors.deinit(allocator); |
| 619 | for (all_errors.list) |a| { | 673 | for (all_errors.list) |a| { |
| 620 | for (e) |ex, i| { | 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)) { | 675 | const a_tag: @TagType(@TypeOf(a)) = a; |
| 622 | handled_errors[i] = true; | 676 | const ex_tag: @TagType(@TypeOf(ex)) = ex; |
| 623 | break; | 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 | } else { | 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 | std.process.exit(1); | 703 | std.process.exit(1); |
| 628 | } | 704 | } |
| 629 | } | 705 | } |
| ... | @@ -631,7 +707,10 @@ pub const TestContext = struct { | ... | @@ -631,7 +707,10 @@ pub const TestContext = struct { |
| 631 | for (handled_errors) |h, i| { | 707 | for (handled_errors) |h, i| { |
| 632 | if (!h) { | 708 | if (!h) { |
| 633 | const er = e[i]; | 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 | std.process.exit(1); | 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,7 +36,7 @@ pub fn addCases(ctx: *TestContext) !void { |
| 36 | { | 36 | { |
| 37 | var case = ctx.exe("hello world with updates", linux_x64); | 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 | // Incorrect return type | 41 | // Incorrect return type |
| 42 | case.addError( | 42 | case.addError( |
| ... | @@ -147,7 +147,7 @@ pub fn addCases(ctx: *TestContext) !void { | ... | @@ -147,7 +147,7 @@ pub fn addCases(ctx: *TestContext) !void { |
| 147 | 147 | ||
| 148 | { | 148 | { |
| 149 | var case = ctx.exe("hello world with updates", macosx_x64); | 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 | // Incorrect return type | 152 | // Incorrect return type |
| 153 | case.addError( | 153 | case.addError( |