| author | |
| committer | |
| log | a05acaf9fd8ea1b42ec300ce4ba948ac00b89d76 |
| tree | 5d10ed6f6c238c6d8ffd56f9f0df2042c01aab99 |
| parent | d8699ae57ed1e69aac209d674b26685c6c569525 |
It doesn't actually do terminal color yet because we need to add
cross platform terminal color abstractions. But it toggles between
the single line error reporting and the multiline error reporting.
See #10264 files changed, 138 insertions(+), 47 deletions(-)
src-self-hosted/errmsg.zig created+87| ... | @@ -0,0 +1,87 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const mem = std.mem; | ||
| 3 | const os = std.os; | ||
| 4 | const Token = std.zig.Token; | ||
| 5 | const ast = std.zig.ast; | ||
| 6 | const TokenIndex = std.zig.ast.TokenIndex; | ||
| 7 | |||
| 8 | pub const Color = enum { | ||
| 9 | Auto, | ||
| 10 | Off, | ||
| 11 | On, | ||
| 12 | }; | ||
| 13 | |||
| 14 | pub const Msg = struct { | ||
| 15 | path: []const u8, | ||
| 16 | text: []u8, | ||
| 17 | first_token: TokenIndex, | ||
| 18 | last_token: TokenIndex, | ||
| 19 | tree: &ast.Tree, | ||
| 20 | }; | ||
| 21 | |||
| 22 | /// `path` must outlive the returned Msg | ||
| 23 | /// `tree` must outlive the returned Msg | ||
| 24 | /// Caller owns returned Msg and must free with `allocator` | ||
| 25 | pub fn createFromParseError( | ||
| 26 | allocator: &mem.Allocator, | ||
| 27 | parse_error: &const ast.Error, | ||
| 28 | tree: &ast.Tree, | ||
| 29 | path: []const u8, | ||
| 30 | ) !&Msg { | ||
| 31 | const loc_token = parse_error.loc(); | ||
| 32 | var text_buf = try std.Buffer.initSize(allocator, 0); | ||
| 33 | defer text_buf.deinit(); | ||
| 34 | |||
| 35 | var out_stream = &std.io.BufferOutStream.init(&text_buf).stream; | ||
| 36 | try parse_error.render(&tree.tokens, out_stream); | ||
| 37 | |||
| 38 | const msg = try allocator.construct(Msg{ | ||
| 39 | .tree = tree, | ||
| 40 | .path = path, | ||
| 41 | .text = text_buf.toOwnedSlice(), | ||
| 42 | .first_token = loc_token, | ||
| 43 | .last_token = loc_token, | ||
| 44 | }); | ||
| 45 | errdefer allocator.destroy(msg); | ||
| 46 | |||
| 47 | return msg; | ||
| 48 | } | ||
| 49 | |||
| 50 | pub fn printToStream(stream: var, msg: &const Msg, color_on: bool) !void { | ||
| 51 | const first_token = msg.tree.tokens.at(msg.first_token); | ||
| 52 | const last_token = msg.tree.tokens.at(msg.last_token); | ||
| 53 | const start_loc = msg.tree.tokenLocationPtr(0, first_token); | ||
| 54 | const end_loc = msg.tree.tokenLocationPtr(first_token.end, last_token); | ||
| 55 | if (!color_on) { | ||
| 56 | try stream.print( | ||
| 57 | "{}:{}:{}: error: {}\n", | ||
| 58 | msg.path, | ||
| 59 | start_loc.line + 1, | ||
| 60 | start_loc.column + 1, | ||
| 61 | msg.text, | ||
| 62 | ); | ||
| 63 | return; | ||
| 64 | } | ||
| 65 | |||
| 66 | try stream.print( | ||
| 67 | "{}:{}:{}: error: {}\n{}\n", | ||
| 68 | msg.path, | ||
| 69 | start_loc.line + 1, | ||
| 70 | start_loc.column + 1, | ||
| 71 | msg.text, | ||
| 72 | msg.tree.source[start_loc.line_start..start_loc.line_end], | ||
| 73 | ); | ||
| 74 | try stream.writeByteNTimes(' ', start_loc.column); | ||
| 75 | try stream.writeByteNTimes('~', last_token.end - first_token.start); | ||
| 76 | try stream.write("\n"); | ||
| 77 | } | ||
| 78 | |||
| 79 | pub fn printToFile(file: &os.File, msg: &const Msg, color: Color) !void { | ||
| 80 | const color_on = switch (color) { | ||
| 81 | Color.Auto => file.isTty(), | ||
| 82 | Color.On => true, | ||
| 83 | Color.Off => false, | ||
| 84 | }; | ||
| 85 | var stream = &std.io.FileOutStream.init(file).stream; | ||
| 86 | return printToStream(stream, msg, color_on); | ||
| 87 | } | ||
src-self-hosted/main.zig+40-31| ... | @@ -15,7 +15,9 @@ const Args = arg.Args; | ... | @@ -15,7 +15,9 @@ const Args = arg.Args; |
| 15 | const Flag = arg.Flag; | 15 | const Flag = arg.Flag; |
| 16 | const Module = @import("module.zig").Module; | 16 | const Module = @import("module.zig").Module; |
| 17 | const Target = @import("target.zig").Target; | 17 | const Target = @import("target.zig").Target; |
| 18 | const errmsg = @import("errmsg.zig"); | ||
| 18 | 19 | ||
| 20 | var stderr_file: os.File = undefined; | ||
| 19 | var stderr: &io.OutStream(io.FileOutStream.Error) = undefined; | 21 | var stderr: &io.OutStream(io.FileOutStream.Error) = undefined; |
| 20 | var stdout: &io.OutStream(io.FileOutStream.Error) = undefined; | 22 | var stdout: &io.OutStream(io.FileOutStream.Error) = undefined; |
| 21 | 23 | ||
| ... | @@ -51,7 +53,7 @@ pub fn main() !void { | ... | @@ -51,7 +53,7 @@ pub fn main() !void { |
| 51 | var stdout_out_stream = std.io.FileOutStream.init(&stdout_file); | 53 | var stdout_out_stream = std.io.FileOutStream.init(&stdout_file); |
| 52 | stdout = &stdout_out_stream.stream; | 54 | stdout = &stdout_out_stream.stream; |
| 53 | 55 | ||
| 54 | var stderr_file = try std.io.getStdErr(); | 56 | stderr_file = try std.io.getStdErr(); |
| 55 | var stderr_out_stream = std.io.FileOutStream.init(&stderr_file); | 57 | var stderr_out_stream = std.io.FileOutStream.init(&stderr_file); |
| 56 | stderr = &stderr_out_stream.stream; | 58 | stderr = &stderr_out_stream.stream; |
| 57 | 59 | ||
| ... | @@ -440,18 +442,19 @@ fn buildOutputType(allocator: &Allocator, args: []const []const u8, out_type: Mo | ... | @@ -440,18 +442,19 @@ fn buildOutputType(allocator: &Allocator, args: []const []const u8, out_type: Mo |
| 440 | build_mode = builtin.Mode.ReleaseSafe; | 442 | build_mode = builtin.Mode.ReleaseSafe; |
| 441 | } | 443 | } |
| 442 | 444 | ||
| 443 | var color = Module.ErrColor.Auto; | 445 | const color = blk: { |
| 444 | if (flags.single("color")) |color_flag| { | 446 | if (flags.single("color")) |color_flag| { |
| 445 | if (mem.eql(u8, color_flag, "auto")) { | 447 | if (mem.eql(u8, color_flag, "auto")) { |
| 446 | color = Module.ErrColor.Auto; | 448 | break :blk errmsg.Color.Auto; |
| 447 | } else if (mem.eql(u8, color_flag, "on")) { | 449 | } else if (mem.eql(u8, color_flag, "on")) { |
| 448 | color = Module.ErrColor.On; | 450 | break :blk errmsg.Color.On; |
| 449 | } else if (mem.eql(u8, color_flag, "off")) { | 451 | } else if (mem.eql(u8, color_flag, "off")) { |
| 450 | color = Module.ErrColor.Off; | 452 | break :blk errmsg.Color.Off; |
| 453 | } else unreachable; | ||
| 451 | } else { | 454 | } else { |
| 452 | unreachable; | 455 | break :blk errmsg.Color.Auto; |
| 453 | } | 456 | } |
| 454 | } | 457 | }; |
| 455 | 458 | ||
| 456 | var emit_type = Module.Emit.Binary; | 459 | var emit_type = Module.Emit.Binary; |
| 457 | if (flags.single("emit")) |emit_flag| { | 460 | if (flags.single("emit")) |emit_flag| { |
| ... | @@ -687,7 +690,14 @@ const usage_fmt = | ... | @@ -687,7 +690,14 @@ const usage_fmt = |
| 687 | \\ | 690 | \\ |
| 688 | ; | 691 | ; |
| 689 | 692 | ||
| 690 | const args_fmt_spec = []Flag{Flag.Bool("--help")}; | 693 | const args_fmt_spec = []Flag{ |
| 694 | Flag.Bool("--help"), | ||
| 695 | Flag.Option("--color", []const []const u8{ | ||
| 696 | "auto", | ||
| 697 | "off", | ||
| 698 | "on", | ||
| 699 | }), | ||
| 700 | }; | ||
| 691 | 701 | ||
| 692 | fn cmdFmt(allocator: &Allocator, args: []const []const u8) !void { | 702 | fn cmdFmt(allocator: &Allocator, args: []const []const u8) !void { |
| 693 | var flags = try Args.parse(allocator, args_fmt_spec, args); | 703 | var flags = try Args.parse(allocator, args_fmt_spec, args); |
| ... | @@ -703,6 +713,20 @@ fn cmdFmt(allocator: &Allocator, args: []const []const u8) !void { | ... | @@ -703,6 +713,20 @@ fn cmdFmt(allocator: &Allocator, args: []const []const u8) !void { |
| 703 | os.exit(1); | 713 | os.exit(1); |
| 704 | } | 714 | } |
| 705 | 715 | ||
| 716 | const color = blk: { | ||
| 717 | if (flags.single("color")) |color_flag| { | ||
| 718 | if (mem.eql(u8, color_flag, "auto")) { | ||
| 719 | break :blk errmsg.Color.Auto; | ||
| 720 | } else if (mem.eql(u8, color_flag, "on")) { | ||
| 721 | break :blk errmsg.Color.On; | ||
| 722 | } else if (mem.eql(u8, color_flag, "off")) { | ||
| 723 | break :blk errmsg.Color.Off; | ||
| 724 | } else unreachable; | ||
| 725 | } else { | ||
| 726 | break :blk errmsg.Color.Auto; | ||
| 727 | } | ||
| 728 | }; | ||
| 729 | |||
| 706 | for (flags.positionals.toSliceConst()) |file_path| { | 730 | for (flags.positionals.toSliceConst()) |file_path| { |
| 707 | var file = try os.File.openRead(allocator, file_path); | 731 | var file = try os.File.openRead(allocator, file_path); |
| 708 | defer file.close(); | 732 | defer file.close(); |
| ... | @@ -721,25 +745,10 @@ fn cmdFmt(allocator: &Allocator, args: []const []const u8) !void { | ... | @@ -721,25 +745,10 @@ fn cmdFmt(allocator: &Allocator, args: []const []const u8) !void { |
| 721 | 745 | ||
| 722 | var error_it = tree.errors.iterator(0); | 746 | var error_it = tree.errors.iterator(0); |
| 723 | while (error_it.next()) |parse_error| { | 747 | while (error_it.next()) |parse_error| { |
| 724 | const token = tree.tokens.at(parse_error.loc()); | 748 | const msg = try errmsg.createFromParseError(allocator, parse_error, &tree, file_path); |
| 725 | const loc = tree.tokenLocation(0, parse_error.loc()); | 749 | defer allocator.destroy(msg); |
| 726 | try stderr.print("{}:{}:{}: error: ", file_path, loc.line + 1, loc.column + 1); | 750 | |
| 727 | try tree.renderError(parse_error, stderr); | 751 | try errmsg.printToFile(&stderr_file, msg, color); |
| 728 | try stderr.print("\n{}\n", source_code[loc.line_start..loc.line_end]); | ||
| 729 | { | ||
| 730 | var i: usize = 0; | ||
| 731 | while (i < loc.column) : (i += 1) { | ||
| 732 | try stderr.write(" "); | ||
| 733 | } | ||
| 734 | } | ||
| 735 | { | ||
| 736 | const caret_count = token.end - token.start; | ||
| 737 | var i: usize = 0; | ||
| 738 | while (i < caret_count) : (i += 1) { | ||
| 739 | try stderr.write("~"); | ||
| 740 | } | ||
| 741 | } | ||
| 742 | try stderr.write("\n"); | ||
| 743 | } | 752 | } |
| 744 | if (tree.errors.len != 0) { | 753 | if (tree.errors.len != 0) { |
| 745 | continue; | 754 | continue; |
src-self-hosted/module.zig+3-8| ... | @@ -10,6 +10,7 @@ const Target = @import("target.zig").Target; | ... | @@ -10,6 +10,7 @@ const Target = @import("target.zig").Target; |
| 10 | const warn = std.debug.warn; | 10 | const warn = std.debug.warn; |
| 11 | const Token = std.zig.Token; | 11 | const Token = std.zig.Token; |
| 12 | const ArrayList = std.ArrayList; | 12 | const ArrayList = std.ArrayList; |
| 13 | const errmsg = @import("errmsg.zig"); | ||
| 13 | 14 | ||
| 14 | pub const Module = struct { | 15 | pub const Module = struct { |
| 15 | allocator: &mem.Allocator, | 16 | allocator: &mem.Allocator, |
| ... | @@ -55,7 +56,7 @@ pub const Module = struct { | ... | @@ -55,7 +56,7 @@ pub const Module = struct { |
| 55 | link_libs_list: ArrayList(&LinkLib), | 56 | link_libs_list: ArrayList(&LinkLib), |
| 56 | libc_link_lib: ?&LinkLib, | 57 | libc_link_lib: ?&LinkLib, |
| 57 | 58 | ||
| 58 | err_color: ErrColor, | 59 | err_color: errmsg.Color, |
| 59 | 60 | ||
| 60 | verbose_tokenize: bool, | 61 | verbose_tokenize: bool, |
| 61 | verbose_ast_tree: bool, | 62 | verbose_ast_tree: bool, |
| ... | @@ -87,12 +88,6 @@ pub const Module = struct { | ... | @@ -87,12 +88,6 @@ pub const Module = struct { |
| 87 | Obj, | 88 | Obj, |
| 88 | }; | 89 | }; |
| 89 | 90 | ||
| 90 | pub const ErrColor = enum { | ||
| 91 | Auto, | ||
| 92 | Off, | ||
| 93 | On, | ||
| 94 | }; | ||
| 95 | |||
| 96 | pub const LinkLib = struct { | 91 | pub const LinkLib = struct { |
| 97 | name: []const u8, | 92 | name: []const u8, |
| 98 | path: ?[]const u8, | 93 | path: ?[]const u8, |
| ... | @@ -195,7 +190,7 @@ pub const Module = struct { | ... | @@ -195,7 +190,7 @@ pub const Module = struct { |
| 195 | .windows_subsystem_console = false, | 190 | .windows_subsystem_console = false, |
| 196 | .link_libs_list = ArrayList(&LinkLib).init(allocator), | 191 | .link_libs_list = ArrayList(&LinkLib).init(allocator), |
| 197 | .libc_link_lib = null, | 192 | .libc_link_lib = null, |
| 198 | .err_color = ErrColor.Auto, | 193 | .err_color = errmsg.Color.Auto, |
| 199 | .darwin_frameworks = [][]const u8{}, | 194 | .darwin_frameworks = [][]const u8{}, |
| 200 | .darwin_version_min = DarwinVersionMin.None, | 195 | .darwin_version_min = DarwinVersionMin.None, |
| 201 | .test_filters = [][]const u8{}, | 196 | .test_filters = [][]const u8{}, |
std/zig/ast.zig+8-8| ... | @@ -120,7 +120,7 @@ pub const Error = union(enum) { | ... | @@ -120,7 +120,7 @@ pub const Error = union(enum) { |
| 120 | ExpectedToken: ExpectedToken, | 120 | ExpectedToken: ExpectedToken, |
| 121 | ExpectedCommaOrEnd: ExpectedCommaOrEnd, | 121 | ExpectedCommaOrEnd: ExpectedCommaOrEnd, |
| 122 | 122 | ||
| 123 | pub fn render(self: &Error, tokens: &Tree.TokenList, stream: var) !void { | 123 | pub fn render(self: &const Error, tokens: &Tree.TokenList, stream: var) !void { |
| 124 | switch (self.*) { | 124 | switch (self.*) { |
| 125 | // TODO https://github.com/ziglang/zig/issues/683 | 125 | // TODO https://github.com/ziglang/zig/issues/683 |
| 126 | @TagType(Error).InvalidToken => |*x| return x.render(tokens, stream), | 126 | @TagType(Error).InvalidToken => |*x| return x.render(tokens, stream), |
| ... | @@ -145,7 +145,7 @@ pub const Error = union(enum) { | ... | @@ -145,7 +145,7 @@ pub const Error = union(enum) { |
| 145 | } | 145 | } |
| 146 | } | 146 | } |
| 147 | 147 | ||
| 148 | pub fn loc(self: &Error) TokenIndex { | 148 | pub fn loc(self: &const Error) TokenIndex { |
| 149 | switch (self.*) { | 149 | switch (self.*) { |
| 150 | // TODO https://github.com/ziglang/zig/issues/683 | 150 | // TODO https://github.com/ziglang/zig/issues/683 |
| 151 | @TagType(Error).InvalidToken => |x| return x.token, | 151 | @TagType(Error).InvalidToken => |x| return x.token, |
| ... | @@ -190,7 +190,7 @@ pub const Error = union(enum) { | ... | @@ -190,7 +190,7 @@ pub const Error = union(enum) { |
| 190 | pub const ExpectedCall = struct { | 190 | pub const ExpectedCall = struct { |
| 191 | node: &Node, | 191 | node: &Node, |
| 192 | 192 | ||
| 193 | pub fn render(self: &ExpectedCall, tokens: &Tree.TokenList, stream: var) !void { | 193 | pub fn render(self: &const ExpectedCall, tokens: &Tree.TokenList, stream: var) !void { |
| 194 | return stream.print("expected " ++ @tagName(@TagType(Node.SuffixOp.Op).Call) ++ ", found {}", @tagName(self.node.id)); | 194 | return stream.print("expected " ++ @tagName(@TagType(Node.SuffixOp.Op).Call) ++ ", found {}", @tagName(self.node.id)); |
| 195 | } | 195 | } |
| 196 | }; | 196 | }; |
| ... | @@ -198,7 +198,7 @@ pub const Error = union(enum) { | ... | @@ -198,7 +198,7 @@ pub const Error = union(enum) { |
| 198 | pub const ExpectedCallOrFnProto = struct { | 198 | pub const ExpectedCallOrFnProto = struct { |
| 199 | node: &Node, | 199 | node: &Node, |
| 200 | 200 | ||
| 201 | pub fn render(self: &ExpectedCallOrFnProto, tokens: &Tree.TokenList, stream: var) !void { | 201 | pub fn render(self: &const ExpectedCallOrFnProto, tokens: &Tree.TokenList, stream: var) !void { |
| 202 | return stream.print("expected " ++ @tagName(@TagType(Node.SuffixOp.Op).Call) ++ " or " ++ @tagName(Node.Id.FnProto) ++ ", found {}", @tagName(self.node.id)); | 202 | return stream.print("expected " ++ @tagName(@TagType(Node.SuffixOp.Op).Call) ++ " or " ++ @tagName(Node.Id.FnProto) ++ ", found {}", @tagName(self.node.id)); |
| 203 | } | 203 | } |
| 204 | }; | 204 | }; |
| ... | @@ -207,7 +207,7 @@ pub const Error = union(enum) { | ... | @@ -207,7 +207,7 @@ pub const Error = union(enum) { |
| 207 | token: TokenIndex, | 207 | token: TokenIndex, |
| 208 | expected_id: @TagType(Token.Id), | 208 | expected_id: @TagType(Token.Id), |
| 209 | 209 | ||
| 210 | pub fn render(self: &ExpectedToken, tokens: &Tree.TokenList, stream: var) !void { | 210 | pub fn render(self: &const ExpectedToken, tokens: &Tree.TokenList, stream: var) !void { |
| 211 | const token_name = @tagName(tokens.at(self.token).id); | 211 | const token_name = @tagName(tokens.at(self.token).id); |
| 212 | return stream.print("expected {}, found {}", @tagName(self.expected_id), token_name); | 212 | return stream.print("expected {}, found {}", @tagName(self.expected_id), token_name); |
| 213 | } | 213 | } |
| ... | @@ -217,7 +217,7 @@ pub const Error = union(enum) { | ... | @@ -217,7 +217,7 @@ pub const Error = union(enum) { |
| 217 | token: TokenIndex, | 217 | token: TokenIndex, |
| 218 | end_id: @TagType(Token.Id), | 218 | end_id: @TagType(Token.Id), |
| 219 | 219 | ||
| 220 | pub fn render(self: &ExpectedCommaOrEnd, tokens: &Tree.TokenList, stream: var) !void { | 220 | pub fn render(self: &const ExpectedCommaOrEnd, tokens: &Tree.TokenList, stream: var) !void { |
| 221 | const token_name = @tagName(tokens.at(self.token).id); | 221 | const token_name = @tagName(tokens.at(self.token).id); |
| 222 | return stream.print("expected ',' or {}, found {}", @tagName(self.end_id), token_name); | 222 | return stream.print("expected ',' or {}, found {}", @tagName(self.end_id), token_name); |
| 223 | } | 223 | } |
| ... | @@ -229,7 +229,7 @@ pub const Error = union(enum) { | ... | @@ -229,7 +229,7 @@ pub const Error = union(enum) { |
| 229 | 229 | ||
| 230 | token: TokenIndex, | 230 | token: TokenIndex, |
| 231 | 231 | ||
| 232 | pub fn render(self: &ThisError, tokens: &Tree.TokenList, stream: var) !void { | 232 | pub fn render(self: &const ThisError, tokens: &Tree.TokenList, stream: var) !void { |
| 233 | const token_name = @tagName(tokens.at(self.token).id); | 233 | const token_name = @tagName(tokens.at(self.token).id); |
| 234 | return stream.print(msg, token_name); | 234 | return stream.print(msg, token_name); |
| 235 | } | 235 | } |
| ... | @@ -242,7 +242,7 @@ pub const Error = union(enum) { | ... | @@ -242,7 +242,7 @@ pub const Error = union(enum) { |
| 242 | 242 | ||
| 243 | token: TokenIndex, | 243 | token: TokenIndex, |
| 244 | 244 | ||
| 245 | pub fn render(self: &ThisError, tokens: &Tree.TokenList, stream: var) !void { | 245 | pub fn render(self: &const ThisError, tokens: &Tree.TokenList, stream: var) !void { |
| 246 | return stream.write(msg); | 246 | return stream.write(msg); |
| 247 | } | 247 | } |
| 248 | }; | 248 | }; |