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 {...@@ -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,
228228
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 },
235240
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 };
245257
...@@ -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};
267289
268pub const Directory = struct {290pub 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 }
12561283
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 }
12971333
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 }
13131341
...@@ -2644,12 +2672,19 @@ pub fn updateSubCompilation(sub_compilation: *Compilation) !void {...@@ -2644,12 +2672,19 @@ pub fn updateSubCompilation(sub_compilation: *Compilation) !void {
26442672
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.
79generation: u32 = 0,79generation: 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
81stage1_flags: packed struct {84stage1_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}
2424
25const ErrorMsg = struct {25const 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};
3072
31pub const TestContext = struct {73pub 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 }
138181
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);
3838
39 case.addError("", &[_][]const u8{":1:1: error: no entry point found"});39 case.addError("", &[_][]const u8{"no entry point found"});
4040
41 // Incorrect return type41 // 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 {
147147
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"});
151151
152 // Incorrect return type152 // Incorrect return type
153 case.addError(153 case.addError(