authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-02-28 11:40:25+00:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-03-10 10:26:14+00:00
log978f7fb1ff1cdb36f48b774516aa09ab6a1dfbc0
tree1ec426433cf4ad5115189314da0999872469053c
parent0a246f5e67118328a11df51cd6dfa419793ebeb1
signaturelock-open Commit is signed but in an unrecognized format.

Zcu: improve error message sorting


2 files changed, 39 insertions(+), 69 deletions(-)

src/Compilation.zig+9-46
......@@ -4054,21 +4054,12 @@ pub fn getAllErrorsAlloc(comp: *Compilation) error{OutOfMemory}!ErrorBundle {
40544054 const SortOrder = struct {
40554055 zcu: *Zcu,
40564056 errors: []const *Zcu.ErrorMsg,
4057 read_err: *?ReadError,
4058 const ReadError = struct {
4059 file: *Zcu.File,
4060 err: Zcu.File.GetSourceError,
4061 };
40624057 pub fn lessThan(ctx: @This(), lhs_index: usize, rhs_index: usize) bool {
4063 if (ctx.read_err.* != null) return lhs_index < rhs_index;
4064 var bad_file: *Zcu.File = undefined;
4065 return ctx.errors[lhs_index].src_loc.lessThan(ctx.errors[rhs_index].src_loc, ctx.zcu, &bad_file) catch |err| {
4066 ctx.read_err.* = .{
4067 .file = bad_file,
4068 .err = err,
4069 };
4070 return lhs_index < rhs_index;
4071 };
4058 return Zcu.ErrorMsg.order(
4059 ctx.errors[lhs_index],
4060 ctx.errors[rhs_index],
4061 ctx.zcu,
4062 ).compare(.lt);
40724063 }
40734064 };
40744065
......@@ -4078,16 +4069,10 @@ pub fn getAllErrorsAlloc(comp: *Compilation) error{OutOfMemory}!ErrorBundle {
40784069 var entries = try zcu.failed_analysis.entries.clone(gpa);
40794070 errdefer entries.deinit(gpa);
40804071
4081 var read_err: ?SortOrder.ReadError = null;
40824072 entries.sort(SortOrder{
40834073 .zcu = zcu,
40844074 .errors = entries.items(.value),
4085 .read_err = &read_err,
40864075 });
4087 if (read_err) |e| {
4088 try unableToLoadZcuFile(zcu, &bundle, e.file, e.err);
4089 break :zcu_errors;
4090 }
40914076 break :s entries.slice();
40924077 };
40934078 defer sorted_failed_analysis.deinit(gpa);
......@@ -4208,33 +4193,11 @@ pub fn getAllErrorsAlloc(comp: *Compilation) error{OutOfMemory}!ErrorBundle {
42084193
42094194 // Okay, there *are* referenced compile logs. Sort them into a consistent order.
42104195
4211 {
4212 const SortContext = struct {
4213 zcu: *Zcu,
4214 read_err: *?ReadError,
4215 const ReadError = struct {
4216 file: *Zcu.File,
4217 err: Zcu.File.GetSourceError,
4218 };
4219 fn lessThan(ctx: @This(), lhs: Zcu.ErrorMsg, rhs: Zcu.ErrorMsg) bool {
4220 if (ctx.read_err.* != null) return false;
4221 var bad_file: *Zcu.File = undefined;
4222 return lhs.src_loc.lessThan(rhs.src_loc, ctx.zcu, &bad_file) catch |err| {
4223 ctx.read_err.* = .{
4224 .file = bad_file,
4225 .err = err,
4226 };
4227 return false;
4228 };
4229 }
4230 };
4231 var read_err: ?SortContext.ReadError = null;
4232 std.mem.sort(Zcu.ErrorMsg, messages.items, @as(SortContext, .{ .read_err = &read_err, .zcu = zcu }), SortContext.lessThan);
4233 if (read_err) |e| {
4234 try unableToLoadZcuFile(zcu, &bundle, e.file, e.err);
4235 break :compile_log_text "";
4196 std.mem.sort(Zcu.ErrorMsg, messages.items, zcu, struct {
4197 fn lessThan(zcu_inner: *Zcu, lhs: Zcu.ErrorMsg, rhs: Zcu.ErrorMsg) bool {
4198 return Zcu.ErrorMsg.order(&lhs, &rhs, zcu_inner).compare(.lt);
42364199 }
4237 }
4200 }.lessThan);
42384201
42394202 var log_text: std.ArrayList(u8) = .empty;
42404203 defer log_text.deinit(gpa);
src/Zcu.zig+30-23
......@@ -1250,6 +1250,15 @@ pub const ErrorMsg = struct {
12501250 notes: []ErrorMsg = &.{},
12511251 reference_trace_root: AnalUnit.Optional = .none,
12521252
1253 pub fn order(lhs: *const ErrorMsg, rhs: *const ErrorMsg, zcu: *Zcu) std.math.Order {
1254 return lhs.src_loc.order(rhs.src_loc, zcu).differ() orelse
1255 std.mem.order(u8, lhs.msg, rhs.msg).differ() orelse
1256 std.math.order(lhs.notes.len, rhs.notes.len).differ() orelse
1257 for (lhs.notes, rhs.notes) |*lhs_note, *rhs_note| {
1258 if (order(lhs_note, rhs_note, zcu).differ()) |o| break o;
1259 } else .eq;
1260 }
1261
12531262 pub fn create(
12541263 gpa: Allocator,
12551264 src_loc: LazySrcLoc,
......@@ -2724,36 +2733,34 @@ pub const LazySrcLoc = struct {
27242733 };
27252734 }
27262735
2727 /// Used to sort error messages, so that they're printed in a consistent order.
2728 /// If an error is returned, a file could not be read in order to resolve a source location.
2729 /// In that case, `bad_file_out` is populated, and sorting is impossible.
2730 pub fn lessThan(lhs_lazy: LazySrcLoc, rhs_lazy: LazySrcLoc, zcu: *Zcu, bad_file_out: **Zcu.File) File.GetSourceError!bool {
2731 const lhs_src = lhs_lazy.upgradeOrLost(zcu) orelse {
2736 pub fn order(lhs: LazySrcLoc, rhs: LazySrcLoc, zcu: *Zcu) std.math.Order {
2737 const lhs_resolved = lhs.upgradeOrLost(zcu) orelse {
27322738 // LHS source location lost, so should never be referenced. Just sort it to the end.
2733 return false;
2739 return .gt;
27342740 };
2735 const rhs_src = rhs_lazy.upgradeOrLost(zcu) orelse {
2741 const rhs_resolved = rhs.upgradeOrLost(zcu) orelse {
27362742 // RHS source location lost, so should never be referenced. Just sort it to the end.
2737 return true;
2743 return .lt;
27382744 };
2739 if (lhs_src.file_scope != rhs_src.file_scope) {
2740 const lhs_path = lhs_src.file_scope.path;
2741 const rhs_path = rhs_src.file_scope.path;
2742 if (lhs_path.root != rhs_path.root) {
2743 return @intFromEnum(lhs_path.root) < @intFromEnum(rhs_path.root);
2744 }
2745 return std.mem.order(u8, lhs_path.sub_path, rhs_path.sub_path).compare(.lt);
2745 if (lhs_resolved.file_scope != rhs_resolved.file_scope) {
2746 const lhs_path = lhs_resolved.file_scope.path;
2747 const rhs_path = rhs_resolved.file_scope.path;
2748 return std.math.order(@intFromEnum(lhs_path.root), @intFromEnum(rhs_path.root)).differ() orelse
2749 std.mem.order(u8, lhs_path.sub_path, rhs_path.sub_path).differ().?;
27462750 }
2747
2748 const lhs_span = lhs_src.span(zcu) catch |err| {
2749 bad_file_out.* = lhs_src.file_scope;
2750 return err;
2751 const prev_prot = zcu.comp.io.swapCancelProtection(.blocked);
2752 defer _ = zcu.comp.io.swapCancelProtection(prev_prot);
2753 const lhs_span = lhs_resolved.span(zcu) catch |err| {
2754 assert(err != error.Canceled); // we're protected
2755 // Failed to read LHS, so we'll get a transient error. Just sort it to the end.
2756 return .gt;
27512757 };
2752 const rhs_span = rhs_src.span(zcu) catch |err| {
2753 bad_file_out.* = rhs_src.file_scope;
2754 return err;
2758 const rhs_span = rhs_resolved.span(zcu) catch |err| {
2759 assert(err != error.Canceled); // we're protected
2760 // Failed to read RHS, so we'll get a transient error. Just sort it to the end.
2761 return .lt;
27552762 };
2756 return lhs_span.main < rhs_span.main;
2763 return std.math.order(lhs_span.main, rhs_span.main);
27572764 }
27582765};
27592766