| ... | @@ -50,6 +50,7 @@ const normal_usage = | ... | @@ -50,6 +50,7 @@ const normal_usage = |
| 50 | \\ c++ Use Zig as a drop-in C++ compiler | 50 | \\ c++ Use Zig as a drop-in C++ compiler |
| 51 | \\ env Print lib path, std path, cache directory, and version | 51 | \\ env Print lib path, std path, cache directory, and version |
| 52 | \\ fmt Reformat Zig source into canonical form | 52 | \\ 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 exit | 54 | \\ help Print this help and exit |
| 54 | \\ init-exe Initialize a `zig build` application in the cwd | 55 | \\ init-exe Initialize a `zig build` application in the cwd |
| 55 | \\ init-lib Initialize a `zig build` library in the cwd | 56 | \\ 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 ZIR | 75 | \\ 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 |
| 2246 | | 2246 | |
| 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 | } |
| 2825 | | 2826 | |
| 2826 | fn readSourceFileToEndAlloc(allocator: *mem.Allocator, input: *const fs.File, size_hint: ?usize) ![]const u8 { | 2827 | fn 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-8 | 2858 | // 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 | } |
| 3564 | | 3569 | |
| 3565 | /// This is only enabled for debug builds. | 3570 | const usage_ast_check = |
| 3566 | pub 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 | |
| | 3585 | pub 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"); |
| 3574 | | 3593 | |
| 3575 | const zig_source_file = args[0]; | 3594 | var color: Color = .auto; |
| 3576 | | 3595 | 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; |
| 3579 | | 3598 | |
| 3580 | const stat = try f.stat(); | 3599 | var i: usize = 0; |
| 3581 | | 3600 | 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 | } |
| 3584 | | 3626 | |
| 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 | }; |
| 3600 | | 3642 | 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 | } |
| 3607 | | 3672 | |
| 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); |
| 3611 | | 3676 | |
| 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); |
| 3622 | | 3687 | |
| | 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: on | 3744 | // zig fmt: on |
| 3659 | } | 3745 | } |
| 3660 | | 3746 | |
| 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 | } |
| 3673 | | 3749 | |