mirror of
https://github.com/ziglang/zig.git
synced 2026-02-21 16:54:52 +00:00
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
This commit is contained in:
parent
51701fb2da
commit
b750e074c6
154
src/main.zig
154
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 color: Color = .auto;
|
||||
var want_output_text = false;
|
||||
var have_zig_source_file = false;
|
||||
var zig_source_file: ?[]const u8 = null;
|
||||
|
||||
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 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,
|
||||
};
|
||||
if (zig_source_file) |file_name| {
|
||||
var f = try fs.cwd().openFile(file_name, .{});
|
||||
defer f.close();
|
||||
|
||||
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;
|
||||
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());
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user