diff --git a/src/Compilation.zig b/src/Compilation.zig index 2188d7b87a6c..8efc9117964b 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -226,20 +226,32 @@ pub const AllErrors = struct { arena: std.heap.ArenaAllocator.State, list: []const Message, - pub const Message = struct { - src_path: []const u8, - line: usize, - column: usize, - byte_offset: usize, - msg: []const u8, + pub const Message = union(enum) { + src: struct { + src_path: []const u8, + line: usize, + column: usize, + byte_offset: usize, + msg: []const u8, + }, + plain: struct { + msg: []const u8, + }, pub fn renderToStdErr(self: Message) void { - std.debug.print("{}:{}:{}: error: {}\n", .{ - self.src_path, - self.line + 1, - self.column + 1, - self.msg, - }); + switch (self) { + .src => |src| { + std.debug.print("{s}:{d}:{d}: error: {s}\n", .{ + src.src_path, + src.line + 1, + src.column + 1, + src.msg, + }); + }, + .plain => |plain| { + std.debug.print("error: {s}\n", .{plain.msg}); + }, + } } }; @@ -256,13 +268,23 @@ pub const AllErrors = struct { ) !void { const loc = std.zig.findLineColumn(source, simple_err_msg.byte_offset); try errors.append(.{ - .src_path = try arena.allocator.dupe(u8, sub_file_path), - .msg = try arena.allocator.dupe(u8, simple_err_msg.msg), - .byte_offset = simple_err_msg.byte_offset, - .line = loc.line, - .column = loc.column, + .src = .{ + .src_path = try arena.allocator.dupe(u8, sub_file_path), + .msg = try arena.allocator.dupe(u8, simple_err_msg.msg), + .byte_offset = simple_err_msg.byte_offset, + .line = loc.line, + .column = loc.column, + }, }); } + + fn addPlain( + arena: *std.heap.ArenaAllocator, + errors: *std.ArrayList(Message), + msg: []const u8, + ) !void { + try errors.append(.{ .plain = .{ .msg = msg } }); + } }; pub const Directory = struct { @@ -1169,11 +1191,15 @@ pub fn update(self: *Compilation) !void { // to force a refresh we unload now. if (module.root_scope.cast(Module.Scope.File)) |zig_file| { zig_file.unload(module.gpa); + module.failed_root_src_file = null; module.analyzeContainer(&zig_file.root_container) catch |err| switch (err) { error.AnalysisFail => { assert(self.totalErrorCount() != 0); }, - else => |e| return e, + error.OutOfMemory => return error.OutOfMemory, + else => |e| { + module.failed_root_src_file = e; + }, }; } else if (module.root_scope.cast(Module.Scope.ZIRModule)) |zir_module| { zir_module.unload(module.gpa); @@ -1251,7 +1277,8 @@ pub fn totalErrorCount(self: *Compilation) usize { if (self.bin_file.options.module) |module| { total += module.failed_decls.items().len + module.failed_exports.items().len + - module.failed_files.items().len; + module.failed_files.items().len + + @boolToInt(module.failed_root_src_file != null); } // The "no entry point found" error only counts if there are no other errors. @@ -1293,21 +1320,22 @@ pub fn getAllErrorsAlloc(self: *Compilation) !AllErrors { const source = try decl.scope.getSource(module); try AllErrors.add(&arena, &errors, decl.scope.subFilePath(), source, err_msg.*); } + if (module.failed_root_src_file) |err| { + const file_path = try module.root_pkg.root_src_directory.join(&arena.allocator, &[_][]const u8{ + module.root_pkg.root_src_path, + }); + const msg = try std.fmt.allocPrint(&arena.allocator, "unable to read {s}: {s}", .{ + file_path, @errorName(err), + }); + try AllErrors.addPlain(&arena, &errors, msg); + } } if (errors.items.len == 0 and self.link_error_flags.no_entry_point_found) { - const global_err_src_path = blk: { - if (self.bin_file.options.module) |module| break :blk module.root_pkg.root_src_path; - if (self.c_source_files.len != 0) break :blk self.c_source_files[0].src_path; - if (self.bin_file.options.objects.len != 0) break :blk self.bin_file.options.objects[0]; - break :blk "(no file)"; - }; try errors.append(.{ - .src_path = global_err_src_path, - .line = 0, - .column = 0, - .byte_offset = 0, - .msg = try std.fmt.allocPrint(&arena.allocator, "no entry point found", .{}), + .plain = .{ + .msg = try std.fmt.allocPrint(&arena.allocator, "no entry point found", .{}), + }, }); } @@ -2644,12 +2672,19 @@ pub fn updateSubCompilation(sub_compilation: *Compilation) !void { if (errors.list.len != 0) { for (errors.list) |full_err_msg| { - log.err("{}:{}:{}: {}\n", .{ - full_err_msg.src_path, - full_err_msg.line + 1, - full_err_msg.column + 1, - full_err_msg.msg, - }); + switch (full_err_msg) { + .src => |src| { + log.err("{s}:{d}:{d}: {s}\n", .{ + src.src_path, + src.line + 1, + src.column + 1, + src.msg, + }); + }, + .plain => |plain| { + log.err("{s}", .{plain.msg}); + }, + } } return error.BuildingLibCObjectFailed; } diff --git a/src/Module.zig b/src/Module.zig index 7ef32abcc3bd..20cb7bf1958c 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -78,6 +78,9 @@ import_table: std.StringArrayHashMapUnmanaged(*Scope.File) = .{}, /// previous analysis. generation: u32 = 0, +/// When populated it means there was an error opening/reading the root source file. +failed_root_src_file: ?anyerror = null, + stage1_flags: packed struct { have_winmain: bool = false, have_wwinmain: bool = false, diff --git a/src/test.zig b/src/test.zig index 0d71a6301f95..c72c5207f614 100644 --- a/src/test.zig +++ b/src/test.zig @@ -22,10 +22,52 @@ test "self-hosted" { try ctx.run(); } -const ErrorMsg = struct { - msg: []const u8, - line: u32, - column: u32, +const ErrorMsg = union(enum) { + src: struct { + msg: []const u8, + line: u32, + column: u32, + }, + plain: struct { + msg: []const u8, + }, + + fn init(other: Compilation.AllErrors.Message) ErrorMsg { + switch (other) { + .src => |src| return .{ + .src = .{ + .msg = src.msg, + .line = @intCast(u32, src.line), + .column = @intCast(u32, src.column), + }, + }, + .plain => |plain| return .{ + .plain = .{ + .msg = plain.msg, + }, + }, + } + } + + pub fn format( + self: ErrorMsg, + comptime fmt: []const u8, + options: std.fmt.FormatOptions, + writer: anytype, + ) !void { + switch (self) { + .src => |src| { + return writer.print(":{d}:{d}: error: {s}", .{ + src.line + 1, + src.column + 1, + src.msg, + }); + }, + .plain => |plain| { + return writer.print("error: {s}", .{plain.msg}); + }, + } + } }; pub const TestContext = struct { @@ -112,7 +154,8 @@ pub const TestContext = struct { var array = self.updates.allocator.alloc(ErrorMsg, errors.len) catch unreachable; for (errors) |e, i| { if (e[0] != ':') { - @panic("Invalid test: error must be specified as follows:\n:line:column: error: message\n=========\n"); + array[i] = .{ .plain = .{ .msg = e } }; + continue; } var cur = e[1..]; var line_index = std.mem.indexOf(u8, cur, ":"); @@ -137,9 +180,11 @@ pub const TestContext = struct { } array[i] = .{ - .msg = msg, - .line = line - 1, - .column = column - 1, + .src = .{ + .msg = msg, + .line = line - 1, + .column = column - 1, + }, }; } self.updates.append(.{ .src = src, .case = .{ .Error = array } }) catch unreachable; @@ -544,8 +589,17 @@ pub const TestContext = struct { defer all_errors.deinit(allocator); if (all_errors.list.len != 0) { std.debug.print("\nErrors occurred updating the compilation:\n================\n", .{}); - for (all_errors.list) |err| { - std.debug.print(":{}:{}: error: {}\n================\n", .{ err.line + 1, err.column + 1, err.msg }); + for (all_errors.list) |err_msg| { + switch (err_msg) { + .src => |src| { + std.debug.print(":{d}:{d}: error: {s}\n================\n", .{ + src.line + 1, src.column + 1, src.msg, + }); + }, + .plain => |plain| { + std.debug.print("error: {s}\n================\n", .{plain.msg}); + }, + } } if (case.cbe) { const C = comp.bin_file.cast(link.File.C).?; @@ -618,12 +672,34 @@ pub const TestContext = struct { defer all_errors.deinit(allocator); for (all_errors.list) |a| { for (e) |ex, i| { - if (a.line == ex.line and a.column == ex.column and std.mem.eql(u8, ex.msg, a.msg)) { - handled_errors[i] = true; - break; + const a_tag: @TagType(@TypeOf(a)) = a; + const ex_tag: @TagType(@TypeOf(ex)) = ex; + switch (a) { + .src => |src| { + if (ex_tag != .src) continue; + + if (src.line == ex.src.line and + src.column == ex.src.column and + std.mem.eql(u8, ex.src.msg, src.msg)) + { + handled_errors[i] = true; + break; + } + }, + .plain => |plain| { + if (ex_tag != .plain) continue; + + if (std.mem.eql(u8, ex.plain.msg, plain.msg)) { + handled_errors[i] = true; + break; + } + }, } } else { - std.debug.print("{}\nUnexpected error:\n================\n:{}:{}: error: {}\n================\nTest failed.\n", .{ case.name, a.line + 1, a.column + 1, a.msg }); + std.debug.print( + "{s}\nUnexpected error:\n================\n{}\n================\nTest failed.\n", + .{ case.name, ErrorMsg.init(a) }, + ); std.process.exit(1); } } @@ -631,7 +707,10 @@ pub const TestContext = struct { for (handled_errors) |h, i| { if (!h) { const er = e[i]; - std.debug.print("{}\nDid not receive error:\n================\n{}:{}: {}\n================\nTest failed.\n", .{ case.name, er.line, er.column, er.msg }); + std.debug.print( + "{s}\nDid not receive error:\n================\n{}\n================\nTest failed.\n", + .{ case.name, er }, + ); std.process.exit(1); } } diff --git a/test/stage2/test.zig b/test/stage2/test.zig index 032838eaf91d..ac3b4263822e 100644 --- a/test/stage2/test.zig +++ b/test/stage2/test.zig @@ -36,7 +36,7 @@ pub fn addCases(ctx: *TestContext) !void { { var case = ctx.exe("hello world with updates", linux_x64); - case.addError("", &[_][]const u8{":1:1: error: no entry point found"}); + case.addError("", &[_][]const u8{"no entry point found"}); // Incorrect return type case.addError( @@ -147,7 +147,7 @@ pub fn addCases(ctx: *TestContext) !void { { var case = ctx.exe("hello world with updates", macosx_x64); - case.addError("", &[_][]const u8{":1:1: error: no entry point found"}); + case.addError("", &[_][]const u8{"no entry point found"}); // Incorrect return type case.addError(