mirror of
https://codeberg.org/ziglang/zig.git
synced 2025-12-06 13:54:21 +00:00
Compilation: indent multiline error messages properly
Co-authored-by: Veikka Tuominen <git@vexu.eu>
This commit is contained in:
parent
ade9bd9287
commit
da75eb0d79
3 changed files with 84 additions and 14 deletions
|
|
@ -344,6 +344,20 @@ pub const AllErrors = struct {
|
|||
/// Does not include the trailing newline.
|
||||
source_line: ?[]const u8,
|
||||
notes: []Message = &.{},
|
||||
|
||||
/// Splits the error message up into lines to properly indent them
|
||||
/// to allow for long, good-looking error messages.
|
||||
///
|
||||
/// This is used to split the message in `@compileError("hello\nworld")` for example.
|
||||
fn writeMsg(src: @This(), stderr: anytype, indent: usize) !void {
|
||||
var lines = mem.split(u8, src.msg, "\n");
|
||||
while (lines.next()) |line| {
|
||||
try stderr.writeAll(line);
|
||||
if (lines.index == null) break;
|
||||
try stderr.writeByte('\n');
|
||||
try stderr.writeByteNTimes(' ', indent);
|
||||
}
|
||||
}
|
||||
},
|
||||
plain: struct {
|
||||
msg: []const u8,
|
||||
|
|
@ -367,35 +381,41 @@ pub const AllErrors = struct {
|
|||
std.debug.getStderrMutex().lock();
|
||||
defer std.debug.getStderrMutex().unlock();
|
||||
const stderr = std.io.getStdErr();
|
||||
return msg.renderToStdErrInner(ttyconf, stderr, "error:", .Red, 0) catch return;
|
||||
return msg.renderToWriter(ttyconf, stderr.writer(), "error", .Red, 0) catch return;
|
||||
}
|
||||
|
||||
fn renderToStdErrInner(
|
||||
pub fn renderToWriter(
|
||||
msg: Message,
|
||||
ttyconf: std.debug.TTY.Config,
|
||||
stderr_file: std.fs.File,
|
||||
stderr: anytype,
|
||||
kind: []const u8,
|
||||
color: std.debug.TTY.Color,
|
||||
indent: usize,
|
||||
) anyerror!void {
|
||||
const stderr = stderr_file.writer();
|
||||
var counting_writer = std.io.countingWriter(stderr);
|
||||
const counting_stderr = counting_writer.writer();
|
||||
switch (msg) {
|
||||
.src => |src| {
|
||||
try stderr.writeByteNTimes(' ', indent);
|
||||
try counting_stderr.writeByteNTimes(' ', indent);
|
||||
ttyconf.setColor(stderr, .Bold);
|
||||
try stderr.print("{s}:{d}:{d}: ", .{
|
||||
try counting_stderr.print("{s}:{d}:{d}: ", .{
|
||||
src.src_path,
|
||||
src.line + 1,
|
||||
src.column + 1,
|
||||
});
|
||||
ttyconf.setColor(stderr, color);
|
||||
try stderr.writeAll(kind);
|
||||
try counting_stderr.writeAll(kind);
|
||||
try counting_stderr.writeAll(": ");
|
||||
// This is the length of the part before the error message:
|
||||
// e.g. "file.zig:4:5: error: "
|
||||
const prefix_len = @intCast(usize, counting_stderr.context.bytes_written);
|
||||
ttyconf.setColor(stderr, .Reset);
|
||||
ttyconf.setColor(stderr, .Bold);
|
||||
if (src.count == 1) {
|
||||
try stderr.print(" {s}\n", .{src.msg});
|
||||
try src.writeMsg(stderr, prefix_len);
|
||||
try stderr.writeByte('\n');
|
||||
} else {
|
||||
try stderr.print(" {s}", .{src.msg});
|
||||
try src.writeMsg(stderr, prefix_len);
|
||||
ttyconf.setColor(stderr, .Dim);
|
||||
try stderr.print(" ({d} times)\n", .{src.count});
|
||||
}
|
||||
|
|
@ -414,13 +434,14 @@ pub const AllErrors = struct {
|
|||
}
|
||||
}
|
||||
for (src.notes) |note| {
|
||||
try note.renderToStdErrInner(ttyconf, stderr_file, "note:", .Cyan, indent);
|
||||
try note.renderToWriter(ttyconf, stderr, "note", .Cyan, indent);
|
||||
}
|
||||
},
|
||||
.plain => |plain| {
|
||||
ttyconf.setColor(stderr, color);
|
||||
try stderr.writeByteNTimes(' ', indent);
|
||||
try stderr.writeAll(kind);
|
||||
try stderr.writeAll(": ");
|
||||
ttyconf.setColor(stderr, .Reset);
|
||||
if (plain.count == 1) {
|
||||
try stderr.print("{s}\n", .{plain.msg});
|
||||
|
|
@ -431,7 +452,7 @@ pub const AllErrors = struct {
|
|||
}
|
||||
ttyconf.setColor(stderr, .Reset);
|
||||
for (plain.notes) |note| {
|
||||
try note.renderToStdErrInner(ttyconf, stderr_file, "error:", .Red, indent + 4);
|
||||
try note.renderToWriter(ttyconf, stderr, "error", .Red, indent + 4);
|
||||
}
|
||||
},
|
||||
}
|
||||
|
|
|
|||
15
src/test.zig
15
src/test.zig
|
|
@ -1690,12 +1690,25 @@ pub const TestContext = struct {
|
|||
tmp_dir_path_plus_slash,
|
||||
);
|
||||
|
||||
var buf: [1024]u8 = undefined;
|
||||
const rendered_msg = blk: {
|
||||
var msg: Compilation.AllErrors.Message = actual_error;
|
||||
msg.src.src_path = case_msg.src.src_path;
|
||||
msg.src.notes = &.{};
|
||||
var fib = std.io.fixedBufferStream(&buf);
|
||||
try msg.renderToWriter(.no_color, fib.writer(), "error", .Red, 0);
|
||||
var it = std.mem.split(u8, fib.getWritten(), "error: ");
|
||||
_ = it.next();
|
||||
const rendered = it.rest();
|
||||
break :blk rendered[0 .. rendered.len - 1]; // trim final newline
|
||||
};
|
||||
|
||||
if (src_path_ok and
|
||||
(case_msg.src.line == std.math.maxInt(u32) or
|
||||
actual_msg.line == case_msg.src.line) and
|
||||
(case_msg.src.column == std.math.maxInt(u32) or
|
||||
actual_msg.column == case_msg.src.column) and
|
||||
std.mem.eql(u8, expected_msg, actual_msg.msg) and
|
||||
std.mem.eql(u8, expected_msg, rendered_msg) and
|
||||
case_msg.src.kind == .@"error" and
|
||||
actual_msg.count == case_msg.src.count)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -138,6 +138,42 @@ pub fn addCases(ctx: *TestContext) !void {
|
|||
"tmp.zig:2:1: error: invalid character: '\\t'",
|
||||
});
|
||||
|
||||
{
|
||||
const case = ctx.obj("multiline error messages", .{});
|
||||
case.backend = .stage2;
|
||||
|
||||
case.addError(
|
||||
\\comptime {
|
||||
\\ @compileError("hello\nworld");
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
\\:2:5: error: hello
|
||||
\\ world
|
||||
});
|
||||
|
||||
case.addError(
|
||||
\\comptime {
|
||||
\\ @compileError(
|
||||
\\ \\
|
||||
\\ \\hello!
|
||||
\\ \\I'm a multiline error message.
|
||||
\\ \\I hope to be very useful!
|
||||
\\ \\
|
||||
\\ \\also I will leave this trailing newline here if you don't mind
|
||||
\\ \\
|
||||
\\ );
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
\\:2:5: error:
|
||||
\\ hello!
|
||||
\\ I'm a multiline error message.
|
||||
\\ I hope to be very useful!
|
||||
\\
|
||||
\\ also I will leave this trailing newline here if you don't mind
|
||||
\\
|
||||
});
|
||||
}
|
||||
|
||||
// TODO test this in stage2, but we won't even try in stage1
|
||||
//ctx.objErrStage1("inline fn calls itself indirectly",
|
||||
// \\export fn foo() void {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue