diff options
| author | Andrew Kelley <andrew@ziglang.org> | 2021-05-22 17:20:30 -0700 |
|---|---|---|
| committer | Andrew Kelley <andrew@ziglang.org> | 2021-05-22 17:23:16 -0700 |
| commit | b750e074c621b8679e14e53f9ecd2faa4432eff9 (patch) | |
| tree | e76556b55be460c01787a18e40f5cc19482ade2a /src | |
| parent | 51701fb2da3562363d2c4966c5bf5d1b0ccad1a6 (diff) | |
| download | zig-b750e074c621b8679e14e53f9ecd2faa4432eff9.tar.gz zig-b750e074c621b8679e14e53f9ecd2faa4432eff9.zip | |
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
Diffstat (limited to 'src')
| -rw-r--r-- | src/main.zig | 158 |
1 files changed, 117 insertions, 41 deletions
diff --git a/src/main.zig b/src/main.zig index b388329508..2b144f2cf8 100644 --- a/src/main.zig +++ b/src/main.zig @@ -50,6 +50,7 @@ const normal_usage = \\ c++ Use Zig as a drop-in C++ compiler \\ env Print lib path, std path, cache directory, and version \\ fmt Reformat Zig source into canonical form + \\ ast-check Look for simple compile errors in any set of files \\ help Print this help and exit \\ init-exe Initialize a `zig build` application in the cwd \\ init-lib Initialize a `zig build` library in the cwd @@ -71,7 +72,6 @@ const debug_usage = normal_usage ++ \\ \\Debug Commands: \\ - \\ astgen Print ZIR code for a .zig source file \\ changelist Compute mappings from old ZIR to new ZIR \\ ; @@ -239,8 +239,8 @@ pub fn mainArgs(gpa: *Allocator, arena: *Allocator, args: []const []const u8) !v return io.getStdOut().writeAll(info_zen); } else if (mem.eql(u8, cmd, "help") or mem.eql(u8, cmd, "-h") or mem.eql(u8, cmd, "--help")) { return io.getStdOut().writeAll(usage); - } else if (debug_extensions_enabled and mem.eql(u8, cmd, "astgen")) { - return cmdAstgen(gpa, arena, cmd_args); + } else if (mem.eql(u8, cmd, "ast-check")) { + return cmdAstCheck(gpa, arena, cmd_args); } else if (debug_extensions_enabled and mem.eql(u8, cmd, "changelist")) { return cmdChangelist(gpa, arena, cmd_args); } else { @@ -2246,7 +2246,8 @@ fn updateModule(gpa: *Allocator, comp: *Compilation, hook: AfterUpdateHook) !voi if (errors.list.len != 0) { const ttyconf: std.debug.TTY.Config = switch (comp.color) { - .auto, .on => std.debug.detectTTYConfig(), + .auto => std.debug.detectTTYConfig(), + .on => .escape_codes, .off => .no_color, }; for (errors.list) |full_err_msg| { @@ -2823,13 +2824,17 @@ fn argvCmd(allocator: *Allocator, argv: []const []const u8) ![]u8 { return cmd.toOwnedSlice(); } -fn readSourceFileToEndAlloc(allocator: *mem.Allocator, input: *const fs.File, size_hint: ?usize) ![]const u8 { +fn readSourceFileToEndAlloc( + allocator: *mem.Allocator, + input: *const fs.File, + size_hint: ?usize, +) ![:0]u8 { const source_code = input.readToEndAllocOptions( allocator, max_src_size, size_hint, @alignOf(u16), - null, + 0, ) catch |err| switch (err) { error.ConnectionResetByPeer => unreachable, error.ConnectionTimedOut => unreachable, @@ -2853,7 +2858,7 @@ fn readSourceFileToEndAlloc(allocator: *mem.Allocator, input: *const fs.File, si // If the file starts with a UTF-16 little endian BOM, translate it to UTF-8 if (mem.startsWith(u8, source_code, "\xff\xfe")) { const source_code_utf16_le = mem.bytesAsSlice(u16, source_code); - const source_code_utf8 = std.unicode.utf16leToUtf8Alloc(allocator, source_code_utf16_le) catch |err| switch (err) { + const source_code_utf8 = std.unicode.utf16leToUtf8AllocZ(allocator, source_code_utf16_le) catch |err| switch (err) { error.DanglingSurrogateHalf => error.UnsupportedEncoding, error.ExpectedSecondSurrogateHalf => error.UnsupportedEncoding, error.UnexpectedSecondSurrogateHalf => error.UnsupportedEncoding, @@ -3562,8 +3567,22 @@ pub fn cleanExit() void { } } -/// This is only enabled for debug builds. -pub fn cmdAstgen( +const usage_ast_check = + \\Usage: zig ast-check [file] + \\ + \\ Given a .zig source file, reports any compile errors that can be + \\ ascertained on the basis of the source code alone, without target + \\ information or type checking. + \\ + \\ If [file] is omitted, stdin is used. + \\ + \\Options: + \\ -h, --help Print this help and exit + \\ --color [auto|off|on] Enable or disable colored error messages + \\ -t (debug option) Output ZIR in text form to stdout +; + +pub fn cmdAstCheck( gpa: *Allocator, arena: *Allocator, args: []const []const u8, @@ -3572,45 +3591,91 @@ pub fn cmdAstgen( const AstGen = @import("AstGen.zig"); const Zir = @import("Zir.zig"); - const zig_source_file = args[0]; - - var f = try fs.cwd().openFile(zig_source_file, .{}); - defer f.close(); - - const stat = try f.stat(); - - if (stat.size > max_src_size) - return error.FileTooBig; + var color: Color = .auto; + var want_output_text = false; + var have_zig_source_file = false; + var zig_source_file: ?[]const u8 = null; + + var i: usize = 0; + while (i < args.len) : (i += 1) { + const arg = args[i]; + if (mem.startsWith(u8, arg, "-")) { + if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) { + try io.getStdOut().writeAll(usage_ast_check); + return cleanExit(); + } else if (mem.eql(u8, arg, "-t")) { + want_output_text = true; + } else if (mem.eql(u8, arg, "--color")) { + if (i + 1 >= args.len) { + fatal("expected [auto|on|off] after --color", .{}); + } + i += 1; + const next_arg = args[i]; + color = std.meta.stringToEnum(Color, next_arg) orelse { + fatal("expected [auto|on|off] after --color, found '{s}'", .{next_arg}); + }; + } else { + fatal("unrecognized parameter: '{s}'", .{arg}); + } + } else if (zig_source_file == null) { + zig_source_file = arg; + } else { + fatal("extra positional parameter: '{s}'", .{arg}); + } + } var file: Module.Scope.File = .{ .status = .never_loaded, .source_loaded = false, .tree_loaded = false, .zir_loaded = false, - .sub_file_path = zig_source_file, + .sub_file_path = undefined, .source = undefined, - .stat_size = stat.size, - .stat_inode = stat.inode, - .stat_mtime = stat.mtime, + .stat_size = undefined, + .stat_inode = undefined, + .stat_mtime = undefined, .tree = undefined, .zir = undefined, .pkg = undefined, .root_decl = null, }; - - const source = try arena.allocSentinel(u8, stat.size, 0); - const amt = try f.readAll(source); - if (amt != stat.size) - return error.UnexpectedEndOfFile; - file.source = source; - file.source_loaded = true; + if (zig_source_file) |file_name| { + var f = try fs.cwd().openFile(file_name, .{}); + defer f.close(); + + const stat = try f.stat(); + + if (stat.size > max_src_size) + return error.FileTooBig; + + const source = try arena.allocSentinel(u8, stat.size, 0); + const amt = try f.readAll(source); + if (amt != stat.size) + return error.UnexpectedEndOfFile; + + file.sub_file_path = file_name; + file.source = source; + file.source_loaded = true; + file.stat_size = stat.size; + file.stat_inode = stat.inode; + file.stat_mtime = stat.mtime; + } else { + const stdin = io.getStdIn(); + const source = readSourceFileToEndAlloc(arena, &stdin, null) catch |err| { + fatal("unable to read stdin: {s}", .{err}); + }; + file.sub_file_path = "<stdin>"; + file.source = source; + file.source_loaded = true; + file.stat_size = source.len; + } file.tree = try std.zig.parse(gpa, file.source); file.tree_loaded = true; defer file.tree.deinit(gpa); for (file.tree.errors) |parse_error| { - try printErrMsgToFile(gpa, parse_error, file.tree, zig_source_file, io.getStdErr(), .auto); + try printErrMsgToFile(gpa, parse_error, file.tree, file.sub_file_path, io.getStdErr(), color); } if (file.tree.errors.len != 0) { process.exit(1); @@ -3620,6 +3685,27 @@ pub fn cmdAstgen( file.zir_loaded = true; defer file.zir.deinit(gpa); + if (file.zir.hasCompileErrors()) { + var errors = std.ArrayList(Compilation.AllErrors.Message).init(arena); + try Compilation.AllErrors.addZir(arena, &errors, &file); + const ttyconf: std.debug.TTY.Config = switch (color) { + .auto => std.debug.detectTTYConfig(), + .on => .escape_codes, + .off => .no_color, + }; + for (errors.items) |full_err_msg| { + full_err_msg.renderToStdErr(ttyconf); + } + process.exit(1); + } + + if (!want_output_text) { + return cleanExit(); + } + if (!debug_extensions_enabled) { + fatal("-t option only available in debug builds of zig", .{}); + } + { const token_bytes = @sizeOf(std.zig.ast.TokenList) + file.tree.tokens.len * (@sizeOf(std.zig.Token.Tag) + @sizeOf(std.zig.ast.ByteOffset)); @@ -3647,7 +3733,7 @@ pub fn cmdAstgen( \\# Extra Data Items: {d} ({}) \\ , .{ - fmtIntSizeBin(source.len), + fmtIntSizeBin(file.source.len), file.tree.tokens.len, fmtIntSizeBin(token_bytes), file.tree.nodes.len, fmtIntSizeBin(tree_bytes), fmtIntSizeBin(total_bytes), @@ -3658,16 +3744,6 @@ pub fn cmdAstgen( // zig fmt: on } - if (file.zir.hasCompileErrors()) { - var errors = std.ArrayList(Compilation.AllErrors.Message).init(arena); - try Compilation.AllErrors.addZir(arena, &errors, &file); - const ttyconf = std.debug.detectTTYConfig(); - for (errors.items) |full_err_msg| { - full_err_msg.renderToStdErr(ttyconf); - } - process.exit(1); - } - return Zir.renderAsTextToFile(gpa, &file, io.getStdOut()); } |
