authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-05-22 17:20:30-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-05-22 17:23:16-07:00
logb750e074c621b8679e14e53f9ecd2faa4432eff9
treee76556b55be460c01787a18e40f5cc19482ade2a
parent51701fb2da3562363d2c4966c5bf5d1b0ccad1a6

stage2: rework astgen command into `zig ast-check`

This addresses the use case of quickly reporting AstGen compile errors for a file, for use with an IDE for example. * Rename from `zig asgen` to `zig ast-check` * It is now a command always available; not only in debug builds. * Give it usage text and proper CLI parsing. * Support reading from stdin when no positional arg is provided. * `-t` flag makes it print textual ZIR. Without this flag, it only provides compile errors. * Support `--color` parameter to override the tty detection closes #8871

1 files changed, 117 insertions(+), 41 deletions(-)

src/main.zig+117-41
...@@ -50,6 +50,7 @@ const normal_usage =...@@ -50,6 +50,7 @@ const normal_usage =
50 \\ c++ Use Zig as a drop-in C++ compiler50 \\ c++ Use Zig as a drop-in C++ compiler
51 \\ env Print lib path, std path, cache directory, and version51 \\ env Print lib path, std path, cache directory, and version
52 \\ fmt Reformat Zig source into canonical form52 \\ fmt Reformat Zig source into canonical form
53 \\ ast-check Look for simple compile errors in any set of files
53 \\ help Print this help and exit54 \\ help Print this help and exit
54 \\ init-exe Initialize a `zig build` application in the cwd55 \\ init-exe Initialize a `zig build` application in the cwd
55 \\ init-lib Initialize a `zig build` library in the cwd56 \\ init-lib Initialize a `zig build` library in the cwd
...@@ -71,7 +72,6 @@ const debug_usage = normal_usage ++...@@ -71,7 +72,6 @@ const debug_usage = normal_usage ++
71 \\72 \\
72 \\Debug Commands:73 \\Debug Commands:
73 \\74 \\
74 \\ astgen Print ZIR code for a .zig source file
75 \\ changelist Compute mappings from old ZIR to new ZIR75 \\ changelist Compute mappings from old ZIR to new ZIR
76 \\76 \\
77;77;
...@@ -239,8 +239,8 @@ pub fn mainArgs(gpa: *Allocator, arena: *Allocator, args: []const []const u8) !v...@@ -239,8 +239,8 @@ pub fn mainArgs(gpa: *Allocator, arena: *Allocator, args: []const []const u8) !v
239 return io.getStdOut().writeAll(info_zen);239 return io.getStdOut().writeAll(info_zen);
240 } else if (mem.eql(u8, cmd, "help") or mem.eql(u8, cmd, "-h") or mem.eql(u8, cmd, "--help")) {240 } else if (mem.eql(u8, cmd, "help") or mem.eql(u8, cmd, "-h") or mem.eql(u8, cmd, "--help")) {
241 return io.getStdOut().writeAll(usage);241 return io.getStdOut().writeAll(usage);
242 } else if (debug_extensions_enabled and mem.eql(u8, cmd, "astgen")) {242 } else if (mem.eql(u8, cmd, "ast-check")) {
243 return cmdAstgen(gpa, arena, cmd_args);243 return cmdAstCheck(gpa, arena, cmd_args);
244 } else if (debug_extensions_enabled and mem.eql(u8, cmd, "changelist")) {244 } else if (debug_extensions_enabled and mem.eql(u8, cmd, "changelist")) {
245 return cmdChangelist(gpa, arena, cmd_args);245 return cmdChangelist(gpa, arena, cmd_args);
246 } else {246 } else {
...@@ -2246,7 +2246,8 @@ fn updateModule(gpa: *Allocator, comp: *Compilation, hook: AfterUpdateHook) !voi...@@ -2246,7 +2246,8 @@ fn updateModule(gpa: *Allocator, comp: *Compilation, hook: AfterUpdateHook) !voi
22462246
2247 if (errors.list.len != 0) {2247 if (errors.list.len != 0) {
2248 const ttyconf: std.debug.TTY.Config = switch (comp.color) {2248 const ttyconf: std.debug.TTY.Config = switch (comp.color) {
2249 .auto, .on => std.debug.detectTTYConfig(),2249 .auto => std.debug.detectTTYConfig(),
2250 .on => .escape_codes,
2250 .off => .no_color,2251 .off => .no_color,
2251 };2252 };
2252 for (errors.list) |full_err_msg| {2253 for (errors.list) |full_err_msg| {
...@@ -2823,13 +2824,17 @@ fn argvCmd(allocator: *Allocator, argv: []const []const u8) ![]u8 {...@@ -2823,13 +2824,17 @@ fn argvCmd(allocator: *Allocator, argv: []const []const u8) ![]u8 {
2823 return cmd.toOwnedSlice();2824 return cmd.toOwnedSlice();
2824}2825}
28252826
2826fn readSourceFileToEndAlloc(allocator: *mem.Allocator, input: *const fs.File, size_hint: ?usize) ![]const u8 {2827fn readSourceFileToEndAlloc(
2828 allocator: *mem.Allocator,
2829 input: *const fs.File,
2830 size_hint: ?usize,
2831) ![:0]u8 {
2827 const source_code = input.readToEndAllocOptions(2832 const source_code = input.readToEndAllocOptions(
2828 allocator,2833 allocator,
2829 max_src_size,2834 max_src_size,
2830 size_hint,2835 size_hint,
2831 @alignOf(u16),2836 @alignOf(u16),
2832 null,2837 0,
2833 ) catch |err| switch (err) {2838 ) catch |err| switch (err) {
2834 error.ConnectionResetByPeer => unreachable,2839 error.ConnectionResetByPeer => unreachable,
2835 error.ConnectionTimedOut => unreachable,2840 error.ConnectionTimedOut => unreachable,
...@@ -2853,7 +2858,7 @@ fn readSourceFileToEndAlloc(allocator: *mem.Allocator, input: *const fs.File, si...@@ -2853,7 +2858,7 @@ fn readSourceFileToEndAlloc(allocator: *mem.Allocator, input: *const fs.File, si
2853 // If the file starts with a UTF-16 little endian BOM, translate it to UTF-82858 // If the file starts with a UTF-16 little endian BOM, translate it to UTF-8
2854 if (mem.startsWith(u8, source_code, "\xff\xfe")) {2859 if (mem.startsWith(u8, source_code, "\xff\xfe")) {
2855 const source_code_utf16_le = mem.bytesAsSlice(u16, source_code);2860 const source_code_utf16_le = mem.bytesAsSlice(u16, source_code);
2856 const source_code_utf8 = std.unicode.utf16leToUtf8Alloc(allocator, source_code_utf16_le) catch |err| switch (err) {2861 const source_code_utf8 = std.unicode.utf16leToUtf8AllocZ(allocator, source_code_utf16_le) catch |err| switch (err) {
2857 error.DanglingSurrogateHalf => error.UnsupportedEncoding,2862 error.DanglingSurrogateHalf => error.UnsupportedEncoding,
2858 error.ExpectedSecondSurrogateHalf => error.UnsupportedEncoding,2863 error.ExpectedSecondSurrogateHalf => error.UnsupportedEncoding,
2859 error.UnexpectedSecondSurrogateHalf => error.UnsupportedEncoding,2864 error.UnexpectedSecondSurrogateHalf => error.UnsupportedEncoding,
...@@ -3562,8 +3567,22 @@ pub fn cleanExit() void {...@@ -3562,8 +3567,22 @@ pub fn cleanExit() void {
3562 }3567 }
3563}3568}
35643569
3565/// This is only enabled for debug builds.3570const usage_ast_check =
3566pub fn cmdAstgen(3571 \\Usage: zig ast-check [file]
3572 \\
3573 \\ Given a .zig source file, reports any compile errors that can be
3574 \\ ascertained on the basis of the source code alone, without target
3575 \\ information or type checking.
3576 \\
3577 \\ If [file] is omitted, stdin is used.
3578 \\
3579 \\Options:
3580 \\ -h, --help Print this help and exit
3581 \\ --color [auto|off|on] Enable or disable colored error messages
3582 \\ -t (debug option) Output ZIR in text form to stdout
3583;
3584
3585pub fn cmdAstCheck(
3567 gpa: *Allocator,3586 gpa: *Allocator,
3568 arena: *Allocator,3587 arena: *Allocator,
3569 args: []const []const u8,3588 args: []const []const u8,
...@@ -3572,45 +3591,91 @@ pub fn cmdAstgen(...@@ -3572,45 +3591,91 @@ pub fn cmdAstgen(
3572 const AstGen = @import("AstGen.zig");3591 const AstGen = @import("AstGen.zig");
3573 const Zir = @import("Zir.zig");3592 const Zir = @import("Zir.zig");
35743593
3575 const zig_source_file = args[0];3594 var color: Color = .auto;
35763595 var want_output_text = false;
3577 var f = try fs.cwd().openFile(zig_source_file, .{});3596 var have_zig_source_file = false;
3578 defer f.close();3597 var zig_source_file: ?[]const u8 = null;
35793598
3580 const stat = try f.stat();3599 var i: usize = 0;
35813600 while (i < args.len) : (i += 1) {
3582 if (stat.size > max_src_size)3601 const arg = args[i];
3583 return error.FileTooBig;3602 if (mem.startsWith(u8, arg, "-")) {
3603 if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) {
3604 try io.getStdOut().writeAll(usage_ast_check);
3605 return cleanExit();
3606 } else if (mem.eql(u8, arg, "-t")) {
3607 want_output_text = true;
3608 } else if (mem.eql(u8, arg, "--color")) {
3609 if (i + 1 >= args.len) {
3610 fatal("expected [auto|on|off] after --color", .{});
3611 }
3612 i += 1;
3613 const next_arg = args[i];
3614 color = std.meta.stringToEnum(Color, next_arg) orelse {
3615 fatal("expected [auto|on|off] after --color, found '{s}'", .{next_arg});
3616 };
3617 } else {
3618 fatal("unrecognized parameter: '{s}'", .{arg});
3619 }
3620 } else if (zig_source_file == null) {
3621 zig_source_file = arg;
3622 } else {
3623 fatal("extra positional parameter: '{s}'", .{arg});
3624 }
3625 }
35843626
3585 var file: Module.Scope.File = .{3627 var file: Module.Scope.File = .{
3586 .status = .never_loaded,3628 .status = .never_loaded,
3587 .source_loaded = false,3629 .source_loaded = false,
3588 .tree_loaded = false,3630 .tree_loaded = false,
3589 .zir_loaded = false,3631 .zir_loaded = false,
3590 .sub_file_path = zig_source_file,3632 .sub_file_path = undefined,
3591 .source = undefined,3633 .source = undefined,
3592 .stat_size = stat.size,3634 .stat_size = undefined,
3593 .stat_inode = stat.inode,3635 .stat_inode = undefined,
3594 .stat_mtime = stat.mtime,3636 .stat_mtime = undefined,
3595 .tree = undefined,3637 .tree = undefined,
3596 .zir = undefined,3638 .zir = undefined,
3597 .pkg = undefined,3639 .pkg = undefined,
3598 .root_decl = null,3640 .root_decl = null,
3599 };3641 };
36003642 if (zig_source_file) |file_name| {
3601 const source = try arena.allocSentinel(u8, stat.size, 0);3643 var f = try fs.cwd().openFile(file_name, .{});
3602 const amt = try f.readAll(source);3644 defer f.close();
3603 if (amt != stat.size)3645
3604 return error.UnexpectedEndOfFile;3646 const stat = try f.stat();
3605 file.source = source;3647
3606 file.source_loaded = true;3648 if (stat.size > max_src_size)
3649 return error.FileTooBig;
3650
3651 const source = try arena.allocSentinel(u8, stat.size, 0);
3652 const amt = try f.readAll(source);
3653 if (amt != stat.size)
3654 return error.UnexpectedEndOfFile;
3655
3656 file.sub_file_path = file_name;
3657 file.source = source;
3658 file.source_loaded = true;
3659 file.stat_size = stat.size;
3660 file.stat_inode = stat.inode;
3661 file.stat_mtime = stat.mtime;
3662 } else {
3663 const stdin = io.getStdIn();
3664 const source = readSourceFileToEndAlloc(arena, &stdin, null) catch |err| {
3665 fatal("unable to read stdin: {s}", .{err});
3666 };
3667 file.sub_file_path = "<stdin>";
3668 file.source = source;
3669 file.source_loaded = true;
3670 file.stat_size = source.len;
3671 }
36073672
3608 file.tree = try std.zig.parse(gpa, file.source);3673 file.tree = try std.zig.parse(gpa, file.source);
3609 file.tree_loaded = true;3674 file.tree_loaded = true;
3610 defer file.tree.deinit(gpa);3675 defer file.tree.deinit(gpa);
36113676
3612 for (file.tree.errors) |parse_error| {3677 for (file.tree.errors) |parse_error| {
3613 try printErrMsgToFile(gpa, parse_error, file.tree, zig_source_file, io.getStdErr(), .auto);3678 try printErrMsgToFile(gpa, parse_error, file.tree, file.sub_file_path, io.getStdErr(), color);
3614 }3679 }
3615 if (file.tree.errors.len != 0) {3680 if (file.tree.errors.len != 0) {
3616 process.exit(1);3681 process.exit(1);
...@@ -3620,6 +3685,27 @@ pub fn cmdAstgen(...@@ -3620,6 +3685,27 @@ pub fn cmdAstgen(
3620 file.zir_loaded = true;3685 file.zir_loaded = true;
3621 defer file.zir.deinit(gpa);3686 defer file.zir.deinit(gpa);
36223687
3688 if (file.zir.hasCompileErrors()) {
3689 var errors = std.ArrayList(Compilation.AllErrors.Message).init(arena);
3690 try Compilation.AllErrors.addZir(arena, &errors, &file);
3691 const ttyconf: std.debug.TTY.Config = switch (color) {
3692 .auto => std.debug.detectTTYConfig(),
3693 .on => .escape_codes,
3694 .off => .no_color,
3695 };
3696 for (errors.items) |full_err_msg| {
3697 full_err_msg.renderToStdErr(ttyconf);
3698 }
3699 process.exit(1);
3700 }
3701
3702 if (!want_output_text) {
3703 return cleanExit();
3704 }
3705 if (!debug_extensions_enabled) {
3706 fatal("-t option only available in debug builds of zig", .{});
3707 }
3708
3623 {3709 {
3624 const token_bytes = @sizeOf(std.zig.ast.TokenList) +3710 const token_bytes = @sizeOf(std.zig.ast.TokenList) +
3625 file.tree.tokens.len * (@sizeOf(std.zig.Token.Tag) + @sizeOf(std.zig.ast.ByteOffset));3711 file.tree.tokens.len * (@sizeOf(std.zig.Token.Tag) + @sizeOf(std.zig.ast.ByteOffset));
...@@ -3647,7 +3733,7 @@ pub fn cmdAstgen(...@@ -3647,7 +3733,7 @@ pub fn cmdAstgen(
3647 \\# Extra Data Items: {d} ({})3733 \\# Extra Data Items: {d} ({})
3648 \\3734 \\
3649 , .{3735 , .{
3650 fmtIntSizeBin(source.len),3736 fmtIntSizeBin(file.source.len),
3651 file.tree.tokens.len, fmtIntSizeBin(token_bytes),3737 file.tree.tokens.len, fmtIntSizeBin(token_bytes),
3652 file.tree.nodes.len, fmtIntSizeBin(tree_bytes),3738 file.tree.nodes.len, fmtIntSizeBin(tree_bytes),
3653 fmtIntSizeBin(total_bytes),3739 fmtIntSizeBin(total_bytes),
...@@ -3658,16 +3744,6 @@ pub fn cmdAstgen(...@@ -3658,16 +3744,6 @@ pub fn cmdAstgen(
3658 // zig fmt: on3744 // zig fmt: on
3659 }3745 }
36603746
3661 if (file.zir.hasCompileErrors()) {
3662 var errors = std.ArrayList(Compilation.AllErrors.Message).init(arena);
3663 try Compilation.AllErrors.addZir(arena, &errors, &file);
3664 const ttyconf = std.debug.detectTTYConfig();
3665 for (errors.items) |full_err_msg| {
3666 full_err_msg.renderToStdErr(ttyconf);
3667 }
3668 process.exit(1);
3669 }
3670
3671 return Zir.renderAsTextToFile(gpa, &file, io.getStdOut());3747 return Zir.renderAsTextToFile(gpa, &file, io.getStdOut());
3672}3748}
36733749