Zcu: improve error message sorting

This commit is contained in:
Matthew Lugg 2026-02-28 11:40:25 +00:00
parent 1ccada0452
commit b28ce1a809
No known key found for this signature in database
GPG key ID: 3F5B7DCCBF4AF02E
2 changed files with 39 additions and 69 deletions

View file

@ -4054,21 +4054,12 @@ pub fn getAllErrorsAlloc(comp: *Compilation) error{OutOfMemory}!ErrorBundle {
const SortOrder = struct {
zcu: *Zcu,
errors: []const *Zcu.ErrorMsg,
read_err: *?ReadError,
const ReadError = struct {
file: *Zcu.File,
err: Zcu.File.GetSourceError,
};
pub fn lessThan(ctx: @This(), lhs_index: usize, rhs_index: usize) bool {
if (ctx.read_err.* != null) return lhs_index < rhs_index;
var bad_file: *Zcu.File = undefined;
return ctx.errors[lhs_index].src_loc.lessThan(ctx.errors[rhs_index].src_loc, ctx.zcu, &bad_file) catch |err| {
ctx.read_err.* = .{
.file = bad_file,
.err = err,
};
return lhs_index < rhs_index;
};
return Zcu.ErrorMsg.order(
ctx.errors[lhs_index],
ctx.errors[rhs_index],
ctx.zcu,
).compare(.lt);
}
};
@ -4078,16 +4069,10 @@ pub fn getAllErrorsAlloc(comp: *Compilation) error{OutOfMemory}!ErrorBundle {
var entries = try zcu.failed_analysis.entries.clone(gpa);
errdefer entries.deinit(gpa);
var read_err: ?SortOrder.ReadError = null;
entries.sort(SortOrder{
.zcu = zcu,
.errors = entries.items(.value),
.read_err = &read_err,
});
if (read_err) |e| {
try unableToLoadZcuFile(zcu, &bundle, e.file, e.err);
break :zcu_errors;
}
break :s entries.slice();
};
defer sorted_failed_analysis.deinit(gpa);
@ -4208,33 +4193,11 @@ pub fn getAllErrorsAlloc(comp: *Compilation) error{OutOfMemory}!ErrorBundle {
// Okay, there *are* referenced compile logs. Sort them into a consistent order.
{
const SortContext = struct {
zcu: *Zcu,
read_err: *?ReadError,
const ReadError = struct {
file: *Zcu.File,
err: Zcu.File.GetSourceError,
};
fn lessThan(ctx: @This(), lhs: Zcu.ErrorMsg, rhs: Zcu.ErrorMsg) bool {
if (ctx.read_err.* != null) return false;
var bad_file: *Zcu.File = undefined;
return lhs.src_loc.lessThan(rhs.src_loc, ctx.zcu, &bad_file) catch |err| {
ctx.read_err.* = .{
.file = bad_file,
.err = err,
};
return false;
};
}
};
var read_err: ?SortContext.ReadError = null;
std.mem.sort(Zcu.ErrorMsg, messages.items, @as(SortContext, .{ .read_err = &read_err, .zcu = zcu }), SortContext.lessThan);
if (read_err) |e| {
try unableToLoadZcuFile(zcu, &bundle, e.file, e.err);
break :compile_log_text "";
std.mem.sort(Zcu.ErrorMsg, messages.items, zcu, struct {
fn lessThan(zcu_inner: *Zcu, lhs: Zcu.ErrorMsg, rhs: Zcu.ErrorMsg) bool {
return Zcu.ErrorMsg.order(&lhs, &rhs, zcu_inner).compare(.lt);
}
}
}.lessThan);
var log_text: std.ArrayList(u8) = .empty;
defer log_text.deinit(gpa);

View file

@ -1250,6 +1250,15 @@ pub const ErrorMsg = struct {
notes: []ErrorMsg = &.{},
reference_trace_root: AnalUnit.Optional = .none,
pub fn order(lhs: *const ErrorMsg, rhs: *const ErrorMsg, zcu: *Zcu) std.math.Order {
return lhs.src_loc.order(rhs.src_loc, zcu).differ() orelse
std.mem.order(u8, lhs.msg, rhs.msg).differ() orelse
std.math.order(lhs.notes.len, rhs.notes.len).differ() orelse
for (lhs.notes, rhs.notes) |*lhs_note, *rhs_note| {
if (order(lhs_note, rhs_note, zcu).differ()) |o| break o;
} else .eq;
}
pub fn create(
gpa: Allocator,
src_loc: LazySrcLoc,
@ -2724,36 +2733,34 @@ pub const LazySrcLoc = struct {
};
}
/// Used to sort error messages, so that they're printed in a consistent order.
/// If an error is returned, a file could not be read in order to resolve a source location.
/// In that case, `bad_file_out` is populated, and sorting is impossible.
pub fn lessThan(lhs_lazy: LazySrcLoc, rhs_lazy: LazySrcLoc, zcu: *Zcu, bad_file_out: **Zcu.File) File.GetSourceError!bool {
const lhs_src = lhs_lazy.upgradeOrLost(zcu) orelse {
pub fn order(lhs: LazySrcLoc, rhs: LazySrcLoc, zcu: *Zcu) std.math.Order {
const lhs_resolved = lhs.upgradeOrLost(zcu) orelse {
// LHS source location lost, so should never be referenced. Just sort it to the end.
return false;
return .gt;
};
const rhs_src = rhs_lazy.upgradeOrLost(zcu) orelse {
const rhs_resolved = rhs.upgradeOrLost(zcu) orelse {
// RHS source location lost, so should never be referenced. Just sort it to the end.
return true;
return .lt;
};
if (lhs_src.file_scope != rhs_src.file_scope) {
const lhs_path = lhs_src.file_scope.path;
const rhs_path = rhs_src.file_scope.path;
if (lhs_path.root != rhs_path.root) {
return @intFromEnum(lhs_path.root) < @intFromEnum(rhs_path.root);
}
return std.mem.order(u8, lhs_path.sub_path, rhs_path.sub_path).compare(.lt);
if (lhs_resolved.file_scope != rhs_resolved.file_scope) {
const lhs_path = lhs_resolved.file_scope.path;
const rhs_path = rhs_resolved.file_scope.path;
return std.math.order(@intFromEnum(lhs_path.root), @intFromEnum(rhs_path.root)).differ() orelse
std.mem.order(u8, lhs_path.sub_path, rhs_path.sub_path).differ().?;
}
const lhs_span = lhs_src.span(zcu) catch |err| {
bad_file_out.* = lhs_src.file_scope;
return err;
const prev_prot = zcu.comp.io.swapCancelProtection(.blocked);
defer _ = zcu.comp.io.swapCancelProtection(prev_prot);
const lhs_span = lhs_resolved.span(zcu) catch |err| {
assert(err != error.Canceled); // we're protected
// Failed to read LHS, so we'll get a transient error. Just sort it to the end.
return .gt;
};
const rhs_span = rhs_src.span(zcu) catch |err| {
bad_file_out.* = rhs_src.file_scope;
return err;
const rhs_span = rhs_resolved.span(zcu) catch |err| {
assert(err != error.Canceled); // we're protected
// Failed to read RHS, so we'll get a transient error. Just sort it to the end.
return .lt;
};
return lhs_span.main < rhs_span.main;
return std.math.order(lhs_span.main, rhs_span.main);
}
};