authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-12-04 17:21:55-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-12-04 17:21:55-07:00
log2ed1ed9b32ae588f6a8997248d69817b5d89a133
tree795a5929a71352c469fcd6018bb57ca5a3ebb20c
parent1c5606af9fdbfa18fb312a35a73c68515967c94d

stage2: introduce Module.failed_root_source_file

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 {
226226 arena: std.heap.ArenaAllocator.State,
227227 list: []const Message,
228228
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 },
235240
236241 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 }
243255 }
244256 };
245257
......@@ -256,13 +268,23 @@ pub const AllErrors = struct {
256268 ) !void {
257269 const loc = std.zig.findLineColumn(source, simple_err_msg.byte_offset);
258270 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 },
264278 });
265279 }
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 }
266288};
267289
268290pub const Directory = struct {
......@@ -1169,11 +1191,15 @@ pub fn update(self: *Compilation) !void {
11691191 // to force a refresh we unload now.
11701192 if (module.root_scope.cast(Module.Scope.File)) |zig_file| {
11711193 zig_file.unload(module.gpa);
1194 module.failed_root_src_file = null;
11721195 module.analyzeContainer(&zig_file.root_container) catch |err| switch (err) {
11731196 error.AnalysisFail => {
11741197 assert(self.totalErrorCount() != 0);
11751198 },
1176 else => |e| return e,
1199 error.OutOfMemory => return error.OutOfMemory,
1200 else => |e| {
1201 module.failed_root_src_file = e;
1202 },
11771203 };
11781204 } else if (module.root_scope.cast(Module.Scope.ZIRModule)) |zir_module| {
11791205 zir_module.unload(module.gpa);
......@@ -1251,7 +1277,8 @@ pub fn totalErrorCount(self: *Compilation) usize {
12511277 if (self.bin_file.options.module) |module| {
12521278 total += module.failed_decls.items().len +
12531279 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);
12551282 }
12561283
12571284 // The "no entry point found" error only counts if there are no other errors.
......@@ -1293,21 +1320,22 @@ pub fn getAllErrorsAlloc(self: *Compilation) !AllErrors {
12931320 const source = try decl.scope.getSource(module);
12941321 try AllErrors.add(&arena, &errors, decl.scope.subFilePath(), source, err_msg.*);
12951322 }
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 }
12961332 }
12971333
12981334 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 };
13051335 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 },
13111339 });
13121340 }
13131341
......@@ -2644,12 +2672,19 @@ pub fn updateSubCompilation(sub_compilation: *Compilation) !void {
26442672
26452673 if (errors.list.len != 0) {
26462674 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 }
26532688 }
26542689 return error.BuildingLibCObjectFailed;
26552690 }
src/Module.zig+3
......@@ -78,6 +78,9 @@ import_table: std.StringArrayHashMapUnmanaged(*Scope.File) = .{},
7878/// previous analysis.
7979generation: u32 = 0,
8080
81/// When populated it means there was an error opening/reading the root source file.
82failed_root_src_file: ?anyerror = null,
83
8184stage1_flags: packed struct {
8285 have_winmain: bool = false,
8386 have_wwinmain: bool = false,
src/test.zig+94-15
......@@ -22,10 +22,52 @@ test "self-hosted" {
2222 try ctx.run();
2323}
2424
25const ErrorMsg = struct {
26 msg: []const u8,
27 line: u32,
28 column: u32,
25const 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 }
2971};
3072
3173pub const TestContext = struct {
......@@ -112,7 +154,8 @@ pub const TestContext = struct {
112154 var array = self.updates.allocator.alloc(ErrorMsg, errors.len) catch unreachable;
113155 for (errors) |e, i| {
114156 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;
116159 }
117160 var cur = e[1..];
118161 var line_index = std.mem.indexOf(u8, cur, ":");
......@@ -137,9 +180,11 @@ pub const TestContext = struct {
137180 }
138181
139182 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 },
143188 };
144189 }
145190 self.updates.append(.{ .src = src, .case = .{ .Error = array } }) catch unreachable;
......@@ -544,8 +589,17 @@ pub const TestContext = struct {
544589 defer all_errors.deinit(allocator);
545590 if (all_errors.list.len != 0) {
546591 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 }
549603 }
550604 if (case.cbe) {
551605 const C = comp.bin_file.cast(link.File.C).?;
......@@ -618,12 +672,34 @@ pub const TestContext = struct {
618672 defer all_errors.deinit(allocator);
619673 for (all_errors.list) |a| {
620674 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 },
624697 }
625698 } 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 );
627703 std.process.exit(1);
628704 }
629705 }
......@@ -631,7 +707,10 @@ pub const TestContext = struct {
631707 for (handled_errors) |h, i| {
632708 if (!h) {
633709 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 );
635714 std.process.exit(1);
636715 }
637716 }
test/stage2/test.zig+2-2
......@@ -36,7 +36,7 @@ pub fn addCases(ctx: *TestContext) !void {
3636 {
3737 var case = ctx.exe("hello world with updates", linux_x64);
3838
39 case.addError("", &[_][]const u8{":1:1: error: no entry point found"});
39 case.addError("", &[_][]const u8{"no entry point found"});
4040
4141 // Incorrect return type
4242 case.addError(
......@@ -147,7 +147,7 @@ pub fn addCases(ctx: *TestContext) !void {
147147
148148 {
149149 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"});
151151
152152 // Incorrect return type
153153 case.addError(