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 =
5050 \\ c++ Use Zig as a drop-in C++ compiler
5151 \\ env Print lib path, std path, cache directory, and version
5252 \\ fmt Reformat Zig source into canonical form
53 \\ ast-check Look for simple compile errors in any set of files
5354 \\ help Print this help and exit
5455 \\ init-exe Initialize a `zig build` application in the cwd
5556 \\ init-lib Initialize a `zig build` library in the cwd
......@@ -71,7 +72,6 @@ const debug_usage = normal_usage ++
7172 \\
7273 \\Debug Commands:
7374 \\
74 \\ astgen Print ZIR code for a .zig source file
7575 \\ changelist Compute mappings from old ZIR to new ZIR
7676 \\
7777;
......@@ -239,8 +239,8 @@ pub fn mainArgs(gpa: *Allocator, arena: *Allocator, args: []const []const u8) !v
239239 return io.getStdOut().writeAll(info_zen);
240240 } else if (mem.eql(u8, cmd, "help") or mem.eql(u8, cmd, "-h") or mem.eql(u8, cmd, "--help")) {
241241 return io.getStdOut().writeAll(usage);
242 } else if (debug_extensions_enabled and mem.eql(u8, cmd, "astgen")) {
243 return cmdAstgen(gpa, arena, cmd_args);
242 } else if (mem.eql(u8, cmd, "ast-check")) {
243 return cmdAstCheck(gpa, arena, cmd_args);
244244 } else if (debug_extensions_enabled and mem.eql(u8, cmd, "changelist")) {
245245 return cmdChangelist(gpa, arena, cmd_args);
246246 } else {
......@@ -2246,7 +2246,8 @@ fn updateModule(gpa: *Allocator, comp: *Compilation, hook: AfterUpdateHook) !voi
22462246
22472247 if (errors.list.len != 0) {
22482248 const ttyconf: std.debug.TTY.Config = switch (comp.color) {
2249 .auto, .on => std.debug.detectTTYConfig(),
2249 .auto => std.debug.detectTTYConfig(),
2250 .on => .escape_codes,
22502251 .off => .no_color,
22512252 };
22522253 for (errors.list) |full_err_msg| {
......@@ -2823,13 +2824,17 @@ fn argvCmd(allocator: *Allocator, argv: []const []const u8) ![]u8 {
28232824 return cmd.toOwnedSlice();
28242825}
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 {
28272832 const source_code = input.readToEndAllocOptions(
28282833 allocator,
28292834 max_src_size,
28302835 size_hint,
28312836 @alignOf(u16),
2832 null,
2837 0,
28332838 ) catch |err| switch (err) {
28342839 error.ConnectionResetByPeer => unreachable,
28352840 error.ConnectionTimedOut => unreachable,
......@@ -2853,7 +2858,7 @@ fn readSourceFileToEndAlloc(allocator: *mem.Allocator, input: *const fs.File, si
28532858 // If the file starts with a UTF-16 little endian BOM, translate it to UTF-8
28542859 if (mem.startsWith(u8, source_code, "\xff\xfe")) {
28552860 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) {
28572862 error.DanglingSurrogateHalf => error.UnsupportedEncoding,
28582863 error.ExpectedSecondSurrogateHalf => error.UnsupportedEncoding,
28592864 error.UnexpectedSecondSurrogateHalf => error.UnsupportedEncoding,
......@@ -3562,8 +3567,22 @@ pub fn cleanExit() void {
35623567 }
35633568}
35643569
3565/// This is only enabled for debug builds.
3566pub fn cmdAstgen(
3570const usage_ast_check =
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(
35673586 gpa: *Allocator,
35683587 arena: *Allocator,
35693588 args: []const []const u8,
......@@ -3572,45 +3591,91 @@ pub fn cmdAstgen(
35723591 const AstGen = @import("AstGen.zig");
35733592 const Zir = @import("Zir.zig");
35743593
3575 const zig_source_file = args[0];
3576
3577 var f = try fs.cwd().openFile(zig_source_file, .{});
3578 defer f.close();
3579
3580 const stat = try f.stat();
3581
3582 if (stat.size > max_src_size)
3583 return error.FileTooBig;
3594 var color: Color = .auto;
3595 var want_output_text = false;
3596 var have_zig_source_file = false;
3597 var zig_source_file: ?[]const u8 = null;
3598
3599 var i: usize = 0;
3600 while (i < args.len) : (i += 1) {
3601 const arg = args[i];
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
35853627 var file: Module.Scope.File = .{
35863628 .status = .never_loaded,
35873629 .source_loaded = false,
35883630 .tree_loaded = false,
35893631 .zir_loaded = false,
3590 .sub_file_path = zig_source_file,
3632 .sub_file_path = undefined,
35913633 .source = undefined,
3592 .stat_size = stat.size,
3593 .stat_inode = stat.inode,
3594 .stat_mtime = stat.mtime,
3634 .stat_size = undefined,
3635 .stat_inode = undefined,
3636 .stat_mtime = undefined,
35953637 .tree = undefined,
35963638 .zir = undefined,
35973639 .pkg = undefined,
35983640 .root_decl = null,
35993641 };
3600
3601 const source = try arena.allocSentinel(u8, stat.size, 0);
3602 const amt = try f.readAll(source);
3603 if (amt != stat.size)
3604 return error.UnexpectedEndOfFile;
3605 file.source = source;
3606 file.source_loaded = true;
3642 if (zig_source_file) |file_name| {
3643 var f = try fs.cwd().openFile(file_name, .{});
3644 defer f.close();
3645
3646 const stat = try f.stat();
3647
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
36083673 file.tree = try std.zig.parse(gpa, file.source);
36093674 file.tree_loaded = true;
36103675 defer file.tree.deinit(gpa);
36113676
36123677 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);
36143679 }
36153680 if (file.tree.errors.len != 0) {
36163681 process.exit(1);
......@@ -3620,6 +3685,27 @@ pub fn cmdAstgen(
36203685 file.zir_loaded = true;
36213686 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
36233709 {
36243710 const token_bytes = @sizeOf(std.zig.ast.TokenList) +
36253711 file.tree.tokens.len * (@sizeOf(std.zig.Token.Tag) + @sizeOf(std.zig.ast.ByteOffset));
......@@ -3647,7 +3733,7 @@ pub fn cmdAstgen(
36473733 \\# Extra Data Items: {d} ({})
36483734 \\
36493735 , .{
3650 fmtIntSizeBin(source.len),
3736 fmtIntSizeBin(file.source.len),
36513737 file.tree.tokens.len, fmtIntSizeBin(token_bytes),
36523738 file.tree.nodes.len, fmtIntSizeBin(tree_bytes),
36533739 fmtIntSizeBin(total_bytes),
......@@ -3658,16 +3744,6 @@ pub fn cmdAstgen(
36583744 // zig fmt: on
36593745 }
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
36713747 return Zir.renderAsTextToFile(gpa, &file, io.getStdOut());
36723748}
36733749