Commit 9a2de796bd

Andrew Kelley <andrew@ziglang.org>
2021-04-09 09:19:44
stage2: clean up pretty printing compile errors
1 parent 0f105ea
Changed files (4)
lib/std/zig.zig
@@ -31,21 +31,38 @@ pub fn hashSrc(src: []const u8) SrcHash {
     return out;
 }
 
-pub fn findLineColumn(source: []const u8, byte_offset: usize) struct { line: usize, column: usize } {
+pub const Loc = struct {
+    line: usize,
+    column: usize,
+    /// Does not include the trailing newline.
+    source_line: []const u8,
+};
+
+pub fn findLineColumn(source: []const u8, byte_offset: usize) Loc {
     var line: usize = 0;
     var column: usize = 0;
-    for (source[0..byte_offset]) |byte| {
-        switch (byte) {
+    var line_start: usize = 0;
+    var i: usize = 0;
+    while (i < byte_offset) : (i += 1) {
+        switch (source[i]) {
             '\n' => {
                 line += 1;
                 column = 0;
+                line_start = i + 1;
             },
             else => {
                 column += 1;
             },
         }
     }
-    return .{ .line = line, .column = column };
+    while (i < source.len and source[i] != '\n') {
+        i += 1;
+    }
+    return .{
+        .line = line,
+        .column = column,
+        .source_line = source[line_start..i],
+    };
 }
 
 pub fn lineDelta(source: []const u8, start: usize, end: usize) isize {
src/Compilation.zig
@@ -6,7 +6,6 @@ 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;
@@ -267,103 +266,62 @@ 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: union(enum) {
-                path: []const u8,
-                scope: *Module.Scope.File,
-            },
+            src_path: []const u8,
             line: u32,
             column: u32,
             byte_offset: u32,
+            /// Does not include the trailing newline.
+            source_line: ?[]const u8,
             notes: []Message = &.{},
         },
         plain: struct {
             msg: []const u8,
         },
 
-        pub fn renderToStdErr(msg: Message, mod: ?*Module) !void {
-            return msg.renderToStdErrInner(.err, mod);
+        pub fn renderToStdErr(msg: Message, ttyconf: std.debug.TTY.Config) void {
+            const stderr_mutex = std.debug.getStderrMutex();
+            const held = std.debug.getStderrMutex().acquire();
+            defer held.release();
+            const stderr = std.io.getStdErr();
+            return msg.renderToStdErrInner(ttyconf, stderr, "error", .Red) catch return;
         }
 
-        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();
+        fn renderToStdErrInner(
+            msg: Message,
+            ttyconf: std.debug.TTY.Config,
+            stderr_file: std.fs.File,
+            kind: []const u8,
+            color: std.debug.TTY.Color,
+        ) anyerror!void {
+            const stderr = stderr_file.writer();
             switch (msg) {
                 .src => |src| {
-                    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);
-                        },
+                    try stderr.print("{s}:{d}:{d}: ", .{
+                        src.src_path,
+                        src.line + 1,
+                        src.column + 1,
+                    });
+                    ttyconf.setColor(stderr, color);
+                    try stderr.writeAll(kind);
+                    ttyconf.setColor(stderr, .Bold);
+                    try stderr.print(" {s}\n", .{src.msg});
+                    ttyconf.setColor(stderr, .Reset);
+                    if (src.source_line) |line| {
+                        try stderr.writeAll(line);
+                        try stderr.writeByte('\n');
+                        try stderr.writeByteNTimes(' ', src.column);
+                        ttyconf.setColor(stderr, .Green);
+                        try stderr.writeAll("^\n");
+                        ttyconf.setColor(stderr, .Reset);
                     }
                     for (src.notes) |note| {
-                        try note.renderToStdErrInner(.note, mod);
+                        try note.renderToStdErrInner(ttyconf, stderr_file, "note", .Cyan);
                     }
                 },
                 .plain => |plain| {
-                    debug.print("{s}: {s}\n", .{ kind, plain.msg });
+                    std.debug.print("{s}: {s}\n", .{ kind, plain.msg });
                 },
             }
         }
@@ -385,30 +343,31 @@ 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 fscope = module_note.src_loc.fileScope();
+            const sub_file_path = module_note.src_loc.fileScope().sub_file_path;
             note.* = .{
                 .src = .{
-                    // TODO this might be freed, might need to dupe the source
-                    .src_path = .{ .scope = fscope },
+                    .src_path = try arena.allocator.dupe(u8, sub_file_path),
                     .msg = try arena.allocator.dupe(u8, module_note.msg),
                     .byte_offset = byte_offset,
                     .line = @intCast(u32, loc.line),
                     .column = @intCast(u32, loc.column),
+                    .source_line = try arena.allocator.dupe(u8, loc.source_line),
                 },
             };
         }
         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 fscope = module_err_msg.src_loc.fileScope();
+        const sub_file_path = module_err_msg.src_loc.fileScope().sub_file_path;
         try errors.append(.{
             .src = .{
-                .src_path = .{ .scope = fscope },
+                .src_path = try arena.allocator.dupe(u8, sub_file_path),
                 .msg = try arena.allocator.dupe(u8, module_err_msg.msg),
                 .byte_offset = byte_offset,
                 .line = @intCast(u32, loc.line),
                 .column = @intCast(u32, loc.column),
                 .notes = notes,
+                .source_line = try arena.allocator.dupe(u8, loc.source_line),
             },
         });
     }
@@ -1549,13 +1508,14 @@ pub fn getAllErrorsAlloc(self: *Compilation) !AllErrors {
         // C error reporting bubbling up.
         try errors.append(.{
             .src = .{
-                .src_path = .{ .path = try arena.allocator.dupe(u8, c_object.src.src_path) },
+                .src_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,
                 }),
                 .byte_offset = 0,
                 .line = err_msg.line,
                 .column = err_msg.column,
+                .source_line = null, // TODO
             },
         });
     }
src/main.zig
@@ -2106,8 +2106,12 @@ fn updateModule(gpa: *Allocator, comp: *Compilation, hook: AfterUpdateHook) !voi
     defer errors.deinit(comp.gpa);
 
     if (errors.list.len != 0) {
+        const ttyconf: std.debug.TTY.Config = switch (comp.color) {
+            .auto, .on => std.debug.detectTTYConfig(),
+            .off => .no_color,
+        };
         for (errors.list) |full_err_msg| {
-            try full_err_msg.renderToStdErr(comp.bin_file.options.module);
+            full_err_msg.renderToStdErr(ttyconf);
         }
         const log_text = comp.getCompileLogOutput();
         if (log_text.len != 0) {
src/Module.zig
@@ -3219,7 +3219,9 @@ pub fn getAstTree(mod: *Module, root_scope: *Scope.File) !*const ast.Tree {
 
             const source = try root_scope.getSource(mod);
 
+            var keep_tree = false;
             root_scope.tree = try std.zig.parse(mod.gpa, source);
+            defer if (!keep_tree) root_scope.tree.deinit(mod.gpa);
 
             const tree = &root_scope.tree;
 
@@ -3247,6 +3249,7 @@ pub fn getAstTree(mod: *Module, root_scope: *Scope.File) !*const ast.Tree {
             }
 
             root_scope.status = .loaded_success;
+            keep_tree = true;
 
             return tree;
         },