Commit 99ec511c4c

jacob gw <jacoblevgw@gmail.com>
2021-04-06 23:19:34
stage2: pretty print compilation errors
1 parent c28d1fe
Changed files (3)
lib/std/debug.zig
@@ -462,7 +462,7 @@ pub const TTY = struct {
         // TODO give this a payload of file handle
         windows_api,
 
-        fn setColor(conf: Config, out_stream: anytype, color: Color) void {
+        pub fn setColor(conf: Config, out_stream: anytype, color: Color) void {
             nosuspend switch (conf) {
                 .no_color => return,
                 .escape_codes => switch (color) {
src/Compilation.zig
@@ -6,6 +6,7 @@ const Allocator = std.mem.Allocator;
 const assert = std.debug.assert;
 const log = std.log.scoped(.compilation);
 const Target = std.Target;
+const debug = std.debug;
 
 const Value = @import("value.zig").Value;
 const Type = @import("type.zig").Type;
@@ -266,9 +267,30 @@ pub const AllErrors = struct {
     list: []const Message,
 
     pub const Message = union(enum) {
+        const MessageType = enum {
+            pub fn render(self: @This(), stderr: anytype, ttyconf: std.debug.TTY.Config) !void {
+                switch (self) {
+                    .err => {
+                        ttyconf.setColor(stderr, .Red);
+                        try stderr.writeAll("error:");
+                    },
+                    .note => {
+                        ttyconf.setColor(stderr, .Green);
+                        try stderr.writeAll("note:");
+                    },
+                }
+                ttyconf.setColor(stderr, .Reset);
+            }
+            err,
+            note
+        };
+
         src: struct {
             msg: []const u8,
-            src_path: []const u8,
+            src_path: union(enum) {
+                path: []const u8,
+                scope: *Module.Scope.File,
+            },
             line: u32,
             column: u32,
             byte_offset: u32,
@@ -278,26 +300,70 @@ pub const AllErrors = struct {
             msg: []const u8,
         },
 
-        pub fn renderToStdErr(msg: Message) void {
-            return msg.renderToStdErrInner("error");
+        pub fn renderToStdErr(msg: Message, mod: ?*Module) !void {
+            return msg.renderToStdErrInner(.err, mod);
         }
 
-        fn renderToStdErrInner(msg: Message, kind: []const u8) void {
+        fn renderToStdErrInner(msg: Message, kind: MessageType, mod: ?*Module) (std.fs.File.WriteError || @typeInfo(@typeInfo(@TypeOf(Module.Scope.File.getSource)).Fn.return_type.?).ErrorUnion.error_set)!void {
+            const ttyconf = debug.detectTTYConfig();
+            const stderr = std.io.getStdErr().writer();
             switch (msg) {
                 .src => |src| {
-                    std.debug.print("{s}:{d}:{d}: {s}: {s}\n", .{
-                        src.src_path,
-                        src.line + 1,
-                        src.column + 1,
-                        kind,
-                        src.msg,
-                    });
+                    switch (src.src_path) {
+                        .path => |p| {
+                            // TODO read the file and do pretty printing for c objects?
+                            try stderr.print("{s}:{d}:{d}: ", .{
+                                p,
+                                src.line + 1,
+                                src.column + 1,
+                            });
+                            try kind.render(stderr, ttyconf);
+                            try stderr.print(" {s}\n", .{
+                                src.msg,
+                            });
+                        },
+                        .scope => |s| {
+                            const module = mod.?;
+                            try stderr.print("{s}:{d}:{d}: ", .{
+                                s.sub_file_path,
+                                src.line + 1,
+                                src.column + 1,
+                            });
+                            try kind.render(stderr, ttyconf);
+                            try stderr.print(" {s}\n", .{
+                                src.msg,
+                            });
+                            const fsource = try s.getSource(module);
+                            const end_pos = blk: for (fsource[src.byte_offset..]) |c, i| {
+                                if (c == '\n')
+                                    break :blk src.byte_offset + i + 1;
+                                if (c == 0)
+                                    break :blk fsource.len - 1;
+                            } else unreachable;
+                            const start_pos = blk: {
+                                var i = src.byte_offset;
+                                var c: u8 = fsource[i];
+                                while (i > 0) : ({
+                                    i -= 1;
+                                    c = fsource[i];
+                                }) {
+                                    if (c == '\n') break :blk i + 1;
+                                }
+                                break :blk 0;
+                            };
+                            try stderr.writeAll(fsource[start_pos..end_pos]);
+                            try stderr.writeByteNTimes(' ', src.column);
+                            ttyconf.setColor(stderr, .Cyan);
+                            try stderr.writeAll("^\n");
+                            ttyconf.setColor(stderr, .Reset);
+                        },
+                    }
                     for (src.notes) |note| {
-                        note.renderToStdErrInner("note");
+                        try note.renderToStdErrInner(.note, mod);
                     }
                 },
                 .plain => |plain| {
-                    std.debug.print("{s}: {s}\n", .{ kind, plain.msg });
+                    debug.print("{s}: {s}\n", .{ kind, plain.msg });
                 },
             }
         }
@@ -319,10 +385,11 @@ pub const AllErrors = struct {
             const source = try module_note.src_loc.fileScope().getSource(module);
             const byte_offset = try module_note.src_loc.byteOffset();
             const loc = std.zig.findLineColumn(source, byte_offset);
-            const sub_file_path = module_note.src_loc.fileScope().sub_file_path;
+            const fscope = module_note.src_loc.fileScope();
             note.* = .{
                 .src = .{
-                    .src_path = try arena.allocator.dupe(u8, sub_file_path),
+                    // TODO this might be freed, might need to dupe the source
+                    .src_path = .{ .scope = fscope },
                     .msg = try arena.allocator.dupe(u8, module_note.msg),
                     .byte_offset = byte_offset,
                     .line = @intCast(u32, loc.line),
@@ -333,10 +400,10 @@ pub const AllErrors = struct {
         const source = try module_err_msg.src_loc.fileScope().getSource(module);
         const byte_offset = try module_err_msg.src_loc.byteOffset();
         const loc = std.zig.findLineColumn(source, byte_offset);
-        const sub_file_path = module_err_msg.src_loc.fileScope().sub_file_path;
+        const fscope = module_err_msg.src_loc.fileScope();
         try errors.append(.{
             .src = .{
-                .src_path = try arena.allocator.dupe(u8, sub_file_path),
+                .src_path = .{ .scope = fscope },
                 .msg = try arena.allocator.dupe(u8, module_err_msg.msg),
                 .byte_offset = byte_offset,
                 .line = @intCast(u32, loc.line),
@@ -1482,7 +1549,7 @@ pub fn getAllErrorsAlloc(self: *Compilation) !AllErrors {
         // C error reporting bubbling up.
         try errors.append(.{
             .src = .{
-                .src_path = try arena.allocator.dupe(u8, c_object.src.src_path),
+                .src_path = .{ .path = try arena.allocator.dupe(u8, c_object.src.src_path) },
                 .msg = try std.fmt.allocPrint(&arena.allocator, "unable to build C object: {s}", .{
                     err_msg.msg,
                 }),
src/main.zig
@@ -2107,7 +2107,7 @@ fn updateModule(gpa: *Allocator, comp: *Compilation, hook: AfterUpdateHook) !voi
 
     if (errors.list.len != 0) {
         for (errors.list) |full_err_msg| {
-            full_err_msg.renderToStdErr();
+            try full_err_msg.renderToStdErr(comp.bin_file.options.module);
         }
         const log_text = comp.getCompileLogOutput();
         if (log_text.len != 0) {