authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-02-26 22:26:19-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-02-26 22:26:19-07:00
logd661f0f35ba5c5600c3547b52e6fbca34991702b
tree76d76dbd62943e749a73936631e62159784e2a02
parentb116063e02bf2bb1975f5ae862fcd25f8fbeda09

compiler: JIT zig fmt

See #19063

9 files changed, 778 insertions(+), 670 deletions(-)

lib/build_runner.zig+1-1
......@@ -13,7 +13,7 @@ const Step = std.Build.Step;
1313pub const dependencies = @import("@dependencies");
1414
1515pub fn main() !void {
16 // Here we use an ArenaAllocator backed by a DirectAllocator because a build is a short-lived,
16 // Here we use an ArenaAllocator backed by a page allocator because a build is a short-lived,
1717 // one shot program. We don't need to waste time freeing memory and finding places to squish
1818 // bytes into. So we free everything all at once at the very end.
1919 var single_threaded_arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
lib/std/zig.zig+106-4
......@@ -1,6 +1,3 @@
1/// Implementation of `zig fmt`.
2pub const fmt = @import("zig/fmt.zig");
3
41pub const ErrorBundle = @import("zig/ErrorBundle.zig");
52pub const Server = @import("zig/Server.zig");
63pub const Client = @import("zig/Client.zig");
......@@ -30,6 +27,36 @@ pub const c_translation = @import("zig/c_translation.zig");
3027pub const SrcHasher = std.crypto.hash.Blake3;
3128pub const SrcHash = [16]u8;
3229
30pub const Color = enum {
31 /// Determine whether stderr is a terminal or not automatically.
32 auto,
33 /// Assume stderr is not a terminal.
34 off,
35 /// Assume stderr is a terminal.
36 on,
37
38 pub fn get_tty_conf(color: Color) std.io.tty.Config {
39 return switch (color) {
40 .auto => std.io.tty.detectConfig(std.io.getStdErr()),
41 .on => .escape_codes,
42 .off => .no_color,
43 };
44 }
45
46 pub fn renderOptions(color: Color) std.zig.ErrorBundle.RenderOptions {
47 const ttyconf = get_tty_conf(color);
48 return .{
49 .ttyconf = ttyconf,
50 .include_source_line = ttyconf != .no_color,
51 .include_reference_trace = ttyconf != .no_color,
52 };
53 }
54};
55
56/// There are many assumptions in the entire codebase that Zig source files can
57/// be byte-indexed with a u32 integer.
58pub const max_src_size = std.math.maxInt(u32);
59
3360pub fn hashSrc(src: []const u8) SrcHash {
3461 var out: SrcHash = undefined;
3562 SrcHasher.hash(src, &out, .{});
......@@ -801,6 +828,78 @@ test isValidId {
801828 try std.testing.expect(isValidId("i386"));
802829}
803830
831pub fn readSourceFileToEndAlloc(
832 allocator: Allocator,
833 input: std.fs.File,
834 size_hint: ?usize,
835) ![:0]u8 {
836 const source_code = input.readToEndAllocOptions(
837 allocator,
838 max_src_size,
839 size_hint,
840 @alignOf(u16),
841 0,
842 ) catch |err| switch (err) {
843 error.ConnectionResetByPeer => unreachable,
844 error.ConnectionTimedOut => unreachable,
845 error.NotOpenForReading => unreachable,
846 else => |e| return e,
847 };
848 errdefer allocator.free(source_code);
849
850 // Detect unsupported file types with their Byte Order Mark
851 const unsupported_boms = [_][]const u8{
852 "\xff\xfe\x00\x00", // UTF-32 little endian
853 "\xfe\xff\x00\x00", // UTF-32 big endian
854 "\xfe\xff", // UTF-16 big endian
855 };
856 for (unsupported_boms) |bom| {
857 if (std.mem.startsWith(u8, source_code, bom)) {
858 return error.UnsupportedEncoding;
859 }
860 }
861
862 // If the file starts with a UTF-16 little endian BOM, translate it to UTF-8
863 if (std.mem.startsWith(u8, source_code, "\xff\xfe")) {
864 const source_code_utf16_le = std.mem.bytesAsSlice(u16, source_code);
865 const source_code_utf8 = std.unicode.utf16LeToUtf8AllocZ(allocator, source_code_utf16_le) catch |err| switch (err) {
866 error.DanglingSurrogateHalf => error.UnsupportedEncoding,
867 error.ExpectedSecondSurrogateHalf => error.UnsupportedEncoding,
868 error.UnexpectedSecondSurrogateHalf => error.UnsupportedEncoding,
869 else => |e| return e,
870 };
871
872 allocator.free(source_code);
873 return source_code_utf8;
874 }
875
876 return source_code;
877}
878
879pub fn printAstErrorsToStderr(gpa: Allocator, tree: Ast, path: []const u8, color: Color) !void {
880 var wip_errors: std.zig.ErrorBundle.Wip = undefined;
881 try wip_errors.init(gpa);
882 defer wip_errors.deinit();
883
884 try putAstErrorsIntoBundle(gpa, tree, path, &wip_errors);
885
886 var error_bundle = try wip_errors.toOwnedBundle("");
887 defer error_bundle.deinit(gpa);
888 error_bundle.renderToStdErr(color.renderOptions());
889}
890
891pub fn putAstErrorsIntoBundle(
892 gpa: Allocator,
893 tree: Ast,
894 path: []const u8,
895 wip_errors: *std.zig.ErrorBundle.Wip,
896) Allocator.Error!void {
897 var zir = try AstGen.generate(gpa, tree);
898 defer zir.deinit(gpa);
899
900 try wip_errors.addZirErrorMessages(zir, tree, tree.source, path);
901}
902
804903test {
805904 _ = Ast;
806905 _ = AstRlAnnotate;
......@@ -808,9 +907,12 @@ test {
808907 _ = Client;
809908 _ = ErrorBundle;
810909 _ = Server;
811 _ = fmt;
812910 _ = number_literal;
813911 _ = primitives;
814912 _ = string_literal;
815913 _ = system;
914
915 // This is not standard library API; it is the standalone executable
916 // implementation of `zig fmt`.
917 _ = @import("zig/fmt.zig");
816918}
lib/std/zig/Ast.zig+41-1
......@@ -32,6 +32,12 @@ pub const Location = struct {
3232 line_end: usize,
3333};
3434
35pub const Span = struct {
36 start: u32,
37 end: u32,
38 main: u32,
39};
40
3541pub fn deinit(tree: *Ast, gpa: Allocator) void {
3642 tree.tokens.deinit(gpa);
3743 tree.nodes.deinit(gpa);
......@@ -3533,6 +3539,39 @@ pub const Node = struct {
35333539 };
35343540};
35353541
3542pub fn nodeToSpan(tree: *const Ast, node: u32) Span {
3543 return tokensToSpan(
3544 tree,
3545 tree.firstToken(node),
3546 tree.lastToken(node),
3547 tree.nodes.items(.main_token)[node],
3548 );
3549}
3550
3551pub fn tokenToSpan(tree: *const Ast, token: Ast.TokenIndex) Span {
3552 return tokensToSpan(tree, token, token, token);
3553}
3554
3555pub fn tokensToSpan(tree: *const Ast, start: Ast.TokenIndex, end: Ast.TokenIndex, main: Ast.TokenIndex) Span {
3556 const token_starts = tree.tokens.items(.start);
3557 var start_tok = start;
3558 var end_tok = end;
3559
3560 if (tree.tokensOnSameLine(start, end)) {
3561 // do nothing
3562 } else if (tree.tokensOnSameLine(start, main)) {
3563 end_tok = main;
3564 } else if (tree.tokensOnSameLine(main, end)) {
3565 start_tok = main;
3566 } else {
3567 start_tok = main;
3568 end_tok = main;
3569 }
3570 const start_off = token_starts[start_tok];
3571 const end_off = token_starts[end_tok] + @as(u32, @intCast(tree.tokenSlice(end_tok).len));
3572 return Span{ .start = start_off, .end = end_off, .main = token_starts[main] };
3573}
3574
35363575const std = @import("../std.zig");
35373576const assert = std.debug.assert;
35383577const testing = std.testing;
......@@ -3544,5 +3583,6 @@ const Parse = @import("Parse.zig");
35443583const private_render = @import("./render.zig");
35453584
35463585test {
3547 testing.refAllDecls(@This());
3586 _ = Parse;
3587 _ = private_render;
35483588}
lib/std/zig/ErrorBundle.zig+84
......@@ -459,6 +459,90 @@ pub const Wip = struct {
459459 return @intCast(wip.extra.items.len - notes_len);
460460 }
461461
462 pub fn addZirErrorMessages(
463 eb: *ErrorBundle.Wip,
464 zir: std.zig.Zir,
465 tree: std.zig.Ast,
466 source: [:0]const u8,
467 src_path: []const u8,
468 ) !void {
469 const Zir = std.zig.Zir;
470 const payload_index = zir.extra[@intFromEnum(Zir.ExtraIndex.compile_errors)];
471 assert(payload_index != 0);
472
473 const header = zir.extraData(Zir.Inst.CompileErrors, payload_index);
474 const items_len = header.data.items_len;
475 var extra_index = header.end;
476 for (0..items_len) |_| {
477 const item = zir.extraData(Zir.Inst.CompileErrors.Item, extra_index);
478 extra_index = item.end;
479 const err_span = blk: {
480 if (item.data.node != 0) {
481 break :blk tree.nodeToSpan(item.data.node);
482 }
483 const token_starts = tree.tokens.items(.start);
484 const start = token_starts[item.data.token] + item.data.byte_offset;
485 const end = start + @as(u32, @intCast(tree.tokenSlice(item.data.token).len)) - item.data.byte_offset;
486 break :blk std.zig.Ast.Span{ .start = start, .end = end, .main = start };
487 };
488 const err_loc = std.zig.findLineColumn(source, err_span.main);
489
490 {
491 const msg = zir.nullTerminatedString(item.data.msg);
492 try eb.addRootErrorMessage(.{
493 .msg = try eb.addString(msg),
494 .src_loc = try eb.addSourceLocation(.{
495 .src_path = try eb.addString(src_path),
496 .span_start = err_span.start,
497 .span_main = err_span.main,
498 .span_end = err_span.end,
499 .line = @intCast(err_loc.line),
500 .column = @intCast(err_loc.column),
501 .source_line = try eb.addString(err_loc.source_line),
502 }),
503 .notes_len = item.data.notesLen(zir),
504 });
505 }
506
507 if (item.data.notes != 0) {
508 const notes_start = try eb.reserveNotes(item.data.notes);
509 const block = zir.extraData(Zir.Inst.Block, item.data.notes);
510 const body = zir.extra[block.end..][0..block.data.body_len];
511 for (notes_start.., body) |note_i, body_elem| {
512 const note_item = zir.extraData(Zir.Inst.CompileErrors.Item, body_elem);
513 const msg = zir.nullTerminatedString(note_item.data.msg);
514 const span = blk: {
515 if (note_item.data.node != 0) {
516 break :blk tree.nodeToSpan(note_item.data.node);
517 }
518 const token_starts = tree.tokens.items(.start);
519 const start = token_starts[note_item.data.token] + note_item.data.byte_offset;
520 const end = start + @as(u32, @intCast(tree.tokenSlice(note_item.data.token).len)) - item.data.byte_offset;
521 break :blk std.zig.Ast.Span{ .start = start, .end = end, .main = start };
522 };
523 const loc = std.zig.findLineColumn(source, span.main);
524
525 eb.extra.items[note_i] = @intFromEnum(try eb.addErrorMessage(.{
526 .msg = try eb.addString(msg),
527 .src_loc = try eb.addSourceLocation(.{
528 .src_path = try eb.addString(src_path),
529 .span_start = span.start,
530 .span_main = span.main,
531 .span_end = span.end,
532 .line = @intCast(loc.line),
533 .column = @intCast(loc.column),
534 .source_line = if (loc.eql(err_loc))
535 0
536 else
537 try eb.addString(loc.source_line),
538 }),
539 .notes_len = 0, // TODO rework this function to be recursive
540 }));
541 }
542 }
543 }
544 }
545
462546 fn addOtherMessage(wip: *Wip, other: ErrorBundle, msg_index: MessageIndex) !MessageIndex {
463547 const other_msg = other.getErrorMessage(msg_index);
464548 const src_loc = try wip.addOtherSourceLocation(other, other_msg.src_loc);
lib/std/zig/fmt.zig+342-1
......@@ -1 +1,342 @@
1const std = @import("../std.zig");
1const std = @import("std");
2const mem = std.mem;
3const fs = std.fs;
4const process = std.process;
5const Allocator = std.mem.Allocator;
6const warn = std.log.warn;
7const Color = std.zig.Color;
8
9const usage_fmt =
10 \\Usage: zig fmt [file]...
11 \\
12 \\ Formats the input files and modifies them in-place.
13 \\ Arguments can be files or directories, which are searched
14 \\ recursively.
15 \\
16 \\Options:
17 \\ -h, --help Print this help and exit
18 \\ --color [auto|off|on] Enable or disable colored error messages
19 \\ --stdin Format code from stdin; output to stdout
20 \\ --check List non-conforming files and exit with an error
21 \\ if the list is non-empty
22 \\ --ast-check Run zig ast-check on every file
23 \\ --exclude [file] Exclude file or directory from formatting
24 \\
25 \\
26;
27
28const Fmt = struct {
29 seen: SeenMap,
30 any_error: bool,
31 check_ast: bool,
32 color: Color,
33 gpa: Allocator,
34 arena: Allocator,
35 out_buffer: std.ArrayList(u8),
36
37 const SeenMap = std.AutoHashMap(fs.File.INode, void);
38};
39
40pub fn main() !void {
41 var arena_instance = std.heap.ArenaAllocator.init(std.heap.page_allocator);
42 defer arena_instance.deinit();
43 const arena = arena_instance.allocator();
44 const gpa = arena;
45
46 const args = try process.argsAlloc(arena);
47
48 var color: Color = .auto;
49 var stdin_flag: bool = false;
50 var check_flag: bool = false;
51 var check_ast_flag: bool = false;
52 var input_files = std.ArrayList([]const u8).init(gpa);
53 defer input_files.deinit();
54 var excluded_files = std.ArrayList([]const u8).init(gpa);
55 defer excluded_files.deinit();
56
57 {
58 var i: usize = 1;
59 while (i < args.len) : (i += 1) {
60 const arg = args[i];
61 if (mem.startsWith(u8, arg, "-")) {
62 if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) {
63 const stdout = std.io.getStdOut().writer();
64 try stdout.writeAll(usage_fmt);
65 return process.cleanExit();
66 } else if (mem.eql(u8, arg, "--color")) {
67 if (i + 1 >= args.len) {
68 fatal("expected [auto|on|off] after --color", .{});
69 }
70 i += 1;
71 const next_arg = args[i];
72 color = std.meta.stringToEnum(Color, next_arg) orelse {
73 fatal("expected [auto|on|off] after --color, found '{s}'", .{next_arg});
74 };
75 } else if (mem.eql(u8, arg, "--stdin")) {
76 stdin_flag = true;
77 } else if (mem.eql(u8, arg, "--check")) {
78 check_flag = true;
79 } else if (mem.eql(u8, arg, "--ast-check")) {
80 check_ast_flag = true;
81 } else if (mem.eql(u8, arg, "--exclude")) {
82 if (i + 1 >= args.len) {
83 fatal("expected parameter after --exclude", .{});
84 }
85 i += 1;
86 const next_arg = args[i];
87 try excluded_files.append(next_arg);
88 } else {
89 fatal("unrecognized parameter: '{s}'", .{arg});
90 }
91 } else {
92 try input_files.append(arg);
93 }
94 }
95 }
96
97 if (stdin_flag) {
98 if (input_files.items.len != 0) {
99 fatal("cannot use --stdin with positional arguments", .{});
100 }
101
102 const stdin = std.io.getStdIn();
103 const source_code = std.zig.readSourceFileToEndAlloc(gpa, stdin, null) catch |err| {
104 fatal("unable to read stdin: {}", .{err});
105 };
106 defer gpa.free(source_code);
107
108 var tree = std.zig.Ast.parse(gpa, source_code, .zig) catch |err| {
109 fatal("error parsing stdin: {}", .{err});
110 };
111 defer tree.deinit(gpa);
112
113 if (check_ast_flag) {
114 var zir = try std.zig.AstGen.generate(gpa, tree);
115
116 if (zir.hasCompileErrors()) {
117 var wip_errors: std.zig.ErrorBundle.Wip = undefined;
118 try wip_errors.init(gpa);
119 defer wip_errors.deinit();
120 try wip_errors.addZirErrorMessages(zir, tree, source_code, "<stdin>");
121 var error_bundle = try wip_errors.toOwnedBundle("");
122 defer error_bundle.deinit(gpa);
123 error_bundle.renderToStdErr(color.renderOptions());
124 process.exit(2);
125 }
126 } else if (tree.errors.len != 0) {
127 try std.zig.printAstErrorsToStderr(gpa, tree, "<stdin>", color);
128 process.exit(2);
129 }
130 const formatted = try tree.render(gpa);
131 defer gpa.free(formatted);
132
133 if (check_flag) {
134 const code: u8 = @intFromBool(mem.eql(u8, formatted, source_code));
135 process.exit(code);
136 }
137
138 return std.io.getStdOut().writeAll(formatted);
139 }
140
141 if (input_files.items.len == 0) {
142 fatal("expected at least one source file argument", .{});
143 }
144
145 var fmt = Fmt{
146 .gpa = gpa,
147 .arena = arena,
148 .seen = Fmt.SeenMap.init(gpa),
149 .any_error = false,
150 .check_ast = check_ast_flag,
151 .color = color,
152 .out_buffer = std.ArrayList(u8).init(gpa),
153 };
154 defer fmt.seen.deinit();
155 defer fmt.out_buffer.deinit();
156
157 // Mark any excluded files/directories as already seen,
158 // so that they are skipped later during actual processing
159 for (excluded_files.items) |file_path| {
160 const stat = fs.cwd().statFile(file_path) catch |err| switch (err) {
161 error.FileNotFound => continue,
162 // On Windows, statFile does not work for directories
163 error.IsDir => dir: {
164 var dir = try fs.cwd().openDir(file_path, .{});
165 defer dir.close();
166 break :dir try dir.stat();
167 },
168 else => |e| return e,
169 };
170 try fmt.seen.put(stat.inode, {});
171 }
172
173 for (input_files.items) |file_path| {
174 try fmtPath(&fmt, file_path, check_flag, fs.cwd(), file_path);
175 }
176 if (fmt.any_error) {
177 process.exit(1);
178 }
179}
180
181const FmtError = error{
182 SystemResources,
183 OperationAborted,
184 IoPending,
185 BrokenPipe,
186 Unexpected,
187 WouldBlock,
188 FileClosed,
189 DestinationAddressRequired,
190 DiskQuota,
191 FileTooBig,
192 InputOutput,
193 NoSpaceLeft,
194 AccessDenied,
195 OutOfMemory,
196 RenameAcrossMountPoints,
197 ReadOnlyFileSystem,
198 LinkQuotaExceeded,
199 FileBusy,
200 EndOfStream,
201 Unseekable,
202 NotOpenForWriting,
203 UnsupportedEncoding,
204 ConnectionResetByPeer,
205 SocketNotConnected,
206 LockViolation,
207 NetNameDeleted,
208 InvalidArgument,
209} || fs.File.OpenError;
210
211fn fmtPath(fmt: *Fmt, file_path: []const u8, check_mode: bool, dir: fs.Dir, sub_path: []const u8) FmtError!void {
212 fmtPathFile(fmt, file_path, check_mode, dir, sub_path) catch |err| switch (err) {
213 error.IsDir, error.AccessDenied => return fmtPathDir(fmt, file_path, check_mode, dir, sub_path),
214 else => {
215 warn("unable to format '{s}': {s}", .{ file_path, @errorName(err) });
216 fmt.any_error = true;
217 return;
218 },
219 };
220}
221
222fn fmtPathDir(
223 fmt: *Fmt,
224 file_path: []const u8,
225 check_mode: bool,
226 parent_dir: fs.Dir,
227 parent_sub_path: []const u8,
228) FmtError!void {
229 var dir = try parent_dir.openDir(parent_sub_path, .{ .iterate = true });
230 defer dir.close();
231
232 const stat = try dir.stat();
233 if (try fmt.seen.fetchPut(stat.inode, {})) |_| return;
234
235 var dir_it = dir.iterate();
236 while (try dir_it.next()) |entry| {
237 const is_dir = entry.kind == .directory;
238
239 if (is_dir and (mem.eql(u8, entry.name, "zig-cache") or mem.eql(u8, entry.name, "zig-out"))) continue;
240
241 if (is_dir or entry.kind == .file and (mem.endsWith(u8, entry.name, ".zig") or mem.endsWith(u8, entry.name, ".zon"))) {
242 const full_path = try fs.path.join(fmt.gpa, &[_][]const u8{ file_path, entry.name });
243 defer fmt.gpa.free(full_path);
244
245 if (is_dir) {
246 try fmtPathDir(fmt, full_path, check_mode, dir, entry.name);
247 } else {
248 fmtPathFile(fmt, full_path, check_mode, dir, entry.name) catch |err| {
249 warn("unable to format '{s}': {s}", .{ full_path, @errorName(err) });
250 fmt.any_error = true;
251 return;
252 };
253 }
254 }
255 }
256}
257
258fn fmtPathFile(
259 fmt: *Fmt,
260 file_path: []const u8,
261 check_mode: bool,
262 dir: fs.Dir,
263 sub_path: []const u8,
264) FmtError!void {
265 const source_file = try dir.openFile(sub_path, .{});
266 var file_closed = false;
267 errdefer if (!file_closed) source_file.close();
268
269 const stat = try source_file.stat();
270
271 if (stat.kind == .directory)
272 return error.IsDir;
273
274 const gpa = fmt.gpa;
275 const source_code = try std.zig.readSourceFileToEndAlloc(
276 gpa,
277 source_file,
278 std.math.cast(usize, stat.size) orelse return error.FileTooBig,
279 );
280 defer gpa.free(source_code);
281
282 source_file.close();
283 file_closed = true;
284
285 // Add to set after no longer possible to get error.IsDir.
286 if (try fmt.seen.fetchPut(stat.inode, {})) |_| return;
287
288 var tree = try std.zig.Ast.parse(gpa, source_code, .zig);
289 defer tree.deinit(gpa);
290
291 if (tree.errors.len != 0) {
292 try std.zig.printAstErrorsToStderr(gpa, tree, file_path, fmt.color);
293 fmt.any_error = true;
294 return;
295 }
296
297 if (fmt.check_ast) {
298 if (stat.size > std.zig.max_src_size)
299 return error.FileTooBig;
300
301 var zir = try std.zig.AstGen.generate(gpa, tree);
302 defer zir.deinit(gpa);
303
304 if (zir.hasCompileErrors()) {
305 var wip_errors: std.zig.ErrorBundle.Wip = undefined;
306 try wip_errors.init(gpa);
307 defer wip_errors.deinit();
308 try wip_errors.addZirErrorMessages(zir, tree, source_code, file_path);
309 var error_bundle = try wip_errors.toOwnedBundle("");
310 defer error_bundle.deinit(gpa);
311 error_bundle.renderToStdErr(fmt.color.renderOptions());
312 fmt.any_error = true;
313 }
314 }
315
316 // As a heuristic, we make enough capacity for the same as the input source.
317 fmt.out_buffer.shrinkRetainingCapacity(0);
318 try fmt.out_buffer.ensureTotalCapacity(source_code.len);
319
320 try tree.renderToArrayList(&fmt.out_buffer, .{});
321 if (mem.eql(u8, fmt.out_buffer.items, source_code))
322 return;
323
324 if (check_mode) {
325 const stdout = std.io.getStdOut().writer();
326 try stdout.print("{s}\n", .{file_path});
327 fmt.any_error = true;
328 } else {
329 var af = try dir.atomicFile(sub_path, .{ .mode = stat.mode });
330 defer af.deinit();
331
332 try af.file.writeAll(fmt.out_buffer.items);
333 try af.finish();
334 const stdout = std.io.getStdOut().writer();
335 try stdout.print("{s}\n", .{file_path});
336 }
337}
338
339fn fatal(comptime format: []const u8, args: anytype) noreturn {
340 std.log.err(format, args);
341 process.exit(1);
342}
src/Compilation.zig+3-78
......@@ -3322,85 +3322,10 @@ pub fn addZirErrorMessages(eb: *ErrorBundle.Wip, file: *Module.File) !void {
33223322 assert(file.zir_loaded);
33233323 assert(file.tree_loaded);
33243324 assert(file.source_loaded);
3325 const payload_index = file.zir.extra[@intFromEnum(Zir.ExtraIndex.compile_errors)];
3326 assert(payload_index != 0);
33273325 const gpa = eb.gpa;
3328
3329 const header = file.zir.extraData(Zir.Inst.CompileErrors, payload_index);
3330 const items_len = header.data.items_len;
3331 var extra_index = header.end;
3332 for (0..items_len) |_| {
3333 const item = file.zir.extraData(Zir.Inst.CompileErrors.Item, extra_index);
3334 extra_index = item.end;
3335 const err_span = blk: {
3336 if (item.data.node != 0) {
3337 break :blk Module.SrcLoc.nodeToSpan(&file.tree, item.data.node);
3338 }
3339 const token_starts = file.tree.tokens.items(.start);
3340 const start = token_starts[item.data.token] + item.data.byte_offset;
3341 const end = start + @as(u32, @intCast(file.tree.tokenSlice(item.data.token).len)) - item.data.byte_offset;
3342 break :blk Module.SrcLoc.Span{ .start = start, .end = end, .main = start };
3343 };
3344 const err_loc = std.zig.findLineColumn(file.source, err_span.main);
3345
3346 {
3347 const msg = file.zir.nullTerminatedString(item.data.msg);
3348 const src_path = try file.fullPath(gpa);
3349 defer gpa.free(src_path);
3350 try eb.addRootErrorMessage(.{
3351 .msg = try eb.addString(msg),
3352 .src_loc = try eb.addSourceLocation(.{
3353 .src_path = try eb.addString(src_path),
3354 .span_start = err_span.start,
3355 .span_main = err_span.main,
3356 .span_end = err_span.end,
3357 .line = @as(u32, @intCast(err_loc.line)),
3358 .column = @as(u32, @intCast(err_loc.column)),
3359 .source_line = try eb.addString(err_loc.source_line),
3360 }),
3361 .notes_len = item.data.notesLen(file.zir),
3362 });
3363 }
3364
3365 if (item.data.notes != 0) {
3366 const notes_start = try eb.reserveNotes(item.data.notes);
3367 const block = file.zir.extraData(Zir.Inst.Block, item.data.notes);
3368 const body = file.zir.extra[block.end..][0..block.data.body_len];
3369 for (notes_start.., body) |note_i, body_elem| {
3370 const note_item = file.zir.extraData(Zir.Inst.CompileErrors.Item, body_elem);
3371 const msg = file.zir.nullTerminatedString(note_item.data.msg);
3372 const span = blk: {
3373 if (note_item.data.node != 0) {
3374 break :blk Module.SrcLoc.nodeToSpan(&file.tree, note_item.data.node);
3375 }
3376 const token_starts = file.tree.tokens.items(.start);
3377 const start = token_starts[note_item.data.token] + note_item.data.byte_offset;
3378 const end = start + @as(u32, @intCast(file.tree.tokenSlice(note_item.data.token).len)) - item.data.byte_offset;
3379 break :blk Module.SrcLoc.Span{ .start = start, .end = end, .main = start };
3380 };
3381 const loc = std.zig.findLineColumn(file.source, span.main);
3382 const src_path = try file.fullPath(gpa);
3383 defer gpa.free(src_path);
3384
3385 eb.extra.items[note_i] = @intFromEnum(try eb.addErrorMessage(.{
3386 .msg = try eb.addString(msg),
3387 .src_loc = try eb.addSourceLocation(.{
3388 .src_path = try eb.addString(src_path),
3389 .span_start = span.start,
3390 .span_main = span.main,
3391 .span_end = span.end,
3392 .line = @as(u32, @intCast(loc.line)),
3393 .column = @as(u32, @intCast(loc.column)),
3394 .source_line = if (loc.eql(err_loc))
3395 0
3396 else
3397 try eb.addString(loc.source_line),
3398 }),
3399 .notes_len = 0, // TODO rework this function to be recursive
3400 }));
3401 }
3402 }
3403 }
3326 const src_path = try file.fullPath(gpa);
3327 defer gpa.free(src_path);
3328 return eb.addZirErrorMessages(file.zir, file.tree, file.source, src_path);
34043329}
34053330
34063331pub fn performAllTheWork(
src/Module.zig+66-108
......@@ -1255,11 +1255,7 @@ pub const SrcLoc = struct {
12551255 return @bitCast(offset + @as(i32, @bitCast(src_loc.parent_decl_node)));
12561256 }
12571257
1258 pub const Span = struct {
1259 start: u32,
1260 end: u32,
1261 main: u32,
1262 };
1258 pub const Span = Ast.Span;
12631259
12641260 pub fn span(src_loc: SrcLoc, gpa: Allocator) !Span {
12651261 switch (src_loc.lazy) {
......@@ -1276,7 +1272,7 @@ pub const SrcLoc = struct {
12761272 },
12771273 .node_abs => |node| {
12781274 const tree = try src_loc.file_scope.getTree(gpa);
1279 return nodeToSpan(tree, node);
1275 return tree.nodeToSpan(node);
12801276 },
12811277 .byte_offset => |byte_off| {
12821278 const tree = try src_loc.file_scope.getTree(gpa);
......@@ -1297,25 +1293,24 @@ pub const SrcLoc = struct {
12971293 const tree = try src_loc.file_scope.getTree(gpa);
12981294 const node = src_loc.declRelativeToNodeIndex(node_off);
12991295 assert(src_loc.file_scope.tree_loaded);
1300 return nodeToSpan(tree, node);
1296 return tree.nodeToSpan(node);
13011297 },
13021298 .node_offset_main_token => |node_off| {
13031299 const tree = try src_loc.file_scope.getTree(gpa);
13041300 const node = src_loc.declRelativeToNodeIndex(node_off);
13051301 const main_token = tree.nodes.items(.main_token)[node];
1306 return tokensToSpan(tree, main_token, main_token, main_token);
1302 return tree.tokensToSpan(main_token, main_token, main_token);
13071303 },
13081304 .node_offset_bin_op => |node_off| {
13091305 const tree = try src_loc.file_scope.getTree(gpa);
13101306 const node = src_loc.declRelativeToNodeIndex(node_off);
13111307 assert(src_loc.file_scope.tree_loaded);
1312 return nodeToSpan(tree, node);
1308 return tree.nodeToSpan(node);
13131309 },
13141310 .node_offset_initializer => |node_off| {
13151311 const tree = try src_loc.file_scope.getTree(gpa);
13161312 const node = src_loc.declRelativeToNodeIndex(node_off);
1317 return tokensToSpan(
1318 tree,
1313 return tree.tokensToSpan(
13191314 tree.firstToken(node) - 3,
13201315 tree.lastToken(node),
13211316 tree.nodes.items(.main_token)[node] - 2,
......@@ -1333,12 +1328,12 @@ pub const SrcLoc = struct {
13331328 => tree.fullVarDecl(node).?,
13341329 .@"usingnamespace" => {
13351330 const node_data = tree.nodes.items(.data);
1336 return nodeToSpan(tree, node_data[node].lhs);
1331 return tree.nodeToSpan(node_data[node].lhs);
13371332 },
13381333 else => unreachable,
13391334 };
13401335 if (full.ast.type_node != 0) {
1341 return nodeToSpan(tree, full.ast.type_node);
1336 return tree.nodeToSpan(full.ast.type_node);
13421337 }
13431338 const tok_index = full.ast.mut_token + 1; // the name token
13441339 const start = tree.tokens.items(.start)[tok_index];
......@@ -1349,25 +1344,25 @@ pub const SrcLoc = struct {
13491344 const tree = try src_loc.file_scope.getTree(gpa);
13501345 const node = src_loc.declRelativeToNodeIndex(node_off);
13511346 const full = tree.fullVarDecl(node).?;
1352 return nodeToSpan(tree, full.ast.align_node);
1347 return tree.nodeToSpan(full.ast.align_node);
13531348 },
13541349 .node_offset_var_decl_section => |node_off| {
13551350 const tree = try src_loc.file_scope.getTree(gpa);
13561351 const node = src_loc.declRelativeToNodeIndex(node_off);
13571352 const full = tree.fullVarDecl(node).?;
1358 return nodeToSpan(tree, full.ast.section_node);
1353 return tree.nodeToSpan(full.ast.section_node);
13591354 },
13601355 .node_offset_var_decl_addrspace => |node_off| {
13611356 const tree = try src_loc.file_scope.getTree(gpa);
13621357 const node = src_loc.declRelativeToNodeIndex(node_off);
13631358 const full = tree.fullVarDecl(node).?;
1364 return nodeToSpan(tree, full.ast.addrspace_node);
1359 return tree.nodeToSpan(full.ast.addrspace_node);
13651360 },
13661361 .node_offset_var_decl_init => |node_off| {
13671362 const tree = try src_loc.file_scope.getTree(gpa);
13681363 const node = src_loc.declRelativeToNodeIndex(node_off);
13691364 const full = tree.fullVarDecl(node).?;
1370 return nodeToSpan(tree, full.ast.init_node);
1365 return tree.nodeToSpan(full.ast.init_node);
13711366 },
13721367 .node_offset_builtin_call_arg0 => |n| return src_loc.byteOffsetBuiltinCallArg(gpa, n, 0),
13731368 .node_offset_builtin_call_arg1 => |n| return src_loc.byteOffsetBuiltinCallArg(gpa, n, 1),
......@@ -1408,13 +1403,13 @@ pub const SrcLoc = struct {
14081403 node = node_datas[node].lhs;
14091404 }
14101405
1411 return nodeToSpan(tree, node);
1406 return tree.nodeToSpan(node);
14121407 },
14131408 .node_offset_array_access_index => |node_off| {
14141409 const tree = try src_loc.file_scope.getTree(gpa);
14151410 const node_datas = tree.nodes.items(.data);
14161411 const node = src_loc.declRelativeToNodeIndex(node_off);
1417 return nodeToSpan(tree, node_datas[node].rhs);
1412 return tree.nodeToSpan(node_datas[node].rhs);
14181413 },
14191414 .node_offset_slice_ptr,
14201415 .node_offset_slice_start,
......@@ -1431,14 +1426,14 @@ pub const SrcLoc = struct {
14311426 .node_offset_slice_sentinel => full.ast.sentinel,
14321427 else => unreachable,
14331428 };
1434 return nodeToSpan(tree, part_node);
1429 return tree.nodeToSpan(part_node);
14351430 },
14361431 .node_offset_call_func => |node_off| {
14371432 const tree = try src_loc.file_scope.getTree(gpa);
14381433 const node = src_loc.declRelativeToNodeIndex(node_off);
14391434 var buf: [1]Ast.Node.Index = undefined;
14401435 const full = tree.fullCall(&buf, node).?;
1441 return nodeToSpan(tree, full.ast.fn_expr);
1436 return tree.nodeToSpan(full.ast.fn_expr);
14421437 },
14431438 .node_offset_field_name => |node_off| {
14441439 const tree = try src_loc.file_scope.getTree(gpa);
......@@ -1477,13 +1472,13 @@ pub const SrcLoc = struct {
14771472 .node_offset_deref_ptr => |node_off| {
14781473 const tree = try src_loc.file_scope.getTree(gpa);
14791474 const node = src_loc.declRelativeToNodeIndex(node_off);
1480 return nodeToSpan(tree, node);
1475 return tree.nodeToSpan(node);
14811476 },
14821477 .node_offset_asm_source => |node_off| {
14831478 const tree = try src_loc.file_scope.getTree(gpa);
14841479 const node = src_loc.declRelativeToNodeIndex(node_off);
14851480 const full = tree.fullAsm(node).?;
1486 return nodeToSpan(tree, full.ast.template);
1481 return tree.nodeToSpan(full.ast.template);
14871482 },
14881483 .node_offset_asm_ret_ty => |node_off| {
14891484 const tree = try src_loc.file_scope.getTree(gpa);
......@@ -1491,7 +1486,7 @@ pub const SrcLoc = struct {
14911486 const full = tree.fullAsm(node).?;
14921487 const asm_output = full.outputs[0];
14931488 const node_datas = tree.nodes.items(.data);
1494 return nodeToSpan(tree, node_datas[asm_output].lhs);
1489 return tree.nodeToSpan(node_datas[asm_output].lhs);
14951490 },
14961491
14971492 .node_offset_if_cond => |node_off| {
......@@ -1514,21 +1509,21 @@ pub const SrcLoc = struct {
15141509 const inputs = tree.fullFor(node).?.ast.inputs;
15151510 const start = tree.firstToken(inputs[0]);
15161511 const end = tree.lastToken(inputs[inputs.len - 1]);
1517 return tokensToSpan(tree, start, end, start);
1512 return tree.tokensToSpan(start, end, start);
15181513 },
15191514
15201515 .@"orelse" => node,
15211516 .@"catch" => node,
15221517 else => unreachable,
15231518 };
1524 return nodeToSpan(tree, src_node);
1519 return tree.nodeToSpan(src_node);
15251520 },
15261521 .for_input => |for_input| {
15271522 const tree = try src_loc.file_scope.getTree(gpa);
15281523 const node = src_loc.declRelativeToNodeIndex(for_input.for_node_offset);
15291524 const for_full = tree.fullFor(node).?;
15301525 const src_node = for_full.ast.inputs[for_input.input_index];
1531 return nodeToSpan(tree, src_node);
1526 return tree.nodeToSpan(src_node);
15321527 },
15331528 .for_capture_from_input => |node_off| {
15341529 const tree = try src_loc.file_scope.getTree(gpa);
......@@ -1554,12 +1549,12 @@ pub const SrcLoc = struct {
15541549 },
15551550 .identifier => {
15561551 if (count == 0)
1557 return tokensToSpan(tree, tok, tok + 1, tok);
1552 return tree.tokensToSpan(tok, tok + 1, tok);
15581553 tok += 1;
15591554 },
15601555 .asterisk => {
15611556 if (count == 0)
1562 return tokensToSpan(tree, tok, tok + 2, tok);
1557 return tree.tokensToSpan(tok, tok + 2, tok);
15631558 tok += 1;
15641559 },
15651560 else => unreachable,
......@@ -1591,7 +1586,7 @@ pub const SrcLoc = struct {
15911586 .array_init_comma,
15921587 => {
15931588 const full = tree.fullArrayInit(&buf, call_args_node).?.ast.elements;
1594 return nodeToSpan(tree, full[call_arg.arg_index]);
1589 return tree.nodeToSpan(full[call_arg.arg_index]);
15951590 },
15961591 .struct_init_one,
15971592 .struct_init_one_comma,
......@@ -1603,12 +1598,12 @@ pub const SrcLoc = struct {
16031598 .struct_init_comma,
16041599 => {
16051600 const full = tree.fullStructInit(&buf, call_args_node).?.ast.fields;
1606 return nodeToSpan(tree, full[call_arg.arg_index]);
1601 return tree.nodeToSpan(full[call_arg.arg_index]);
16071602 },
1608 else => return nodeToSpan(tree, call_args_node),
1603 else => return tree.nodeToSpan(call_args_node),
16091604 }
16101605 };
1611 return nodeToSpan(tree, call_full.ast.params[call_arg.arg_index]);
1606 return tree.nodeToSpan(call_full.ast.params[call_arg.arg_index]);
16121607 },
16131608 .fn_proto_param => |fn_proto_param| {
16141609 const tree = try src_loc.file_scope.getTree(gpa);
......@@ -1619,12 +1614,11 @@ pub const SrcLoc = struct {
16191614 var i: usize = 0;
16201615 while (it.next()) |param| : (i += 1) {
16211616 if (i == fn_proto_param.param_index) {
1622 if (param.anytype_ellipsis3) |token| return tokenToSpan(tree, token);
1617 if (param.anytype_ellipsis3) |token| return tree.tokenToSpan(token);
16231618 const first_token = param.comptime_noalias orelse
16241619 param.name_token orelse
16251620 tree.firstToken(param.type_expr);
1626 return tokensToSpan(
1627 tree,
1621 return tree.tokensToSpan(
16281622 first_token,
16291623 tree.lastToken(param.type_expr),
16301624 first_token,
......@@ -1637,13 +1631,13 @@ pub const SrcLoc = struct {
16371631 const tree = try src_loc.file_scope.getTree(gpa);
16381632 const node = src_loc.declRelativeToNodeIndex(node_off);
16391633 const node_datas = tree.nodes.items(.data);
1640 return nodeToSpan(tree, node_datas[node].lhs);
1634 return tree.nodeToSpan(node_datas[node].lhs);
16411635 },
16421636 .node_offset_bin_rhs => |node_off| {
16431637 const tree = try src_loc.file_scope.getTree(gpa);
16441638 const node = src_loc.declRelativeToNodeIndex(node_off);
16451639 const node_datas = tree.nodes.items(.data);
1646 return nodeToSpan(tree, node_datas[node].rhs);
1640 return tree.nodeToSpan(node_datas[node].rhs);
16471641 },
16481642 .array_cat_lhs, .array_cat_rhs => |cat| {
16491643 const tree = try src_loc.file_scope.getTree(gpa);
......@@ -1667,9 +1661,9 @@ pub const SrcLoc = struct {
16671661 .array_init_comma,
16681662 => {
16691663 const full = tree.fullArrayInit(&buf, arr_node).?.ast.elements;
1670 return nodeToSpan(tree, full[cat.elem_index]);
1664 return tree.nodeToSpan(full[cat.elem_index]);
16711665 },
1672 else => return nodeToSpan(tree, arr_node),
1666 else => return tree.nodeToSpan(arr_node),
16731667 }
16741668 },
16751669
......@@ -1677,7 +1671,7 @@ pub const SrcLoc = struct {
16771671 const tree = try src_loc.file_scope.getTree(gpa);
16781672 const node = src_loc.declRelativeToNodeIndex(node_off);
16791673 const node_datas = tree.nodes.items(.data);
1680 return nodeToSpan(tree, node_datas[node].lhs);
1674 return tree.nodeToSpan(node_datas[node].lhs);
16811675 },
16821676
16831677 .node_offset_switch_special_prong => |node_off| {
......@@ -1696,7 +1690,7 @@ pub const SrcLoc = struct {
16961690 mem.eql(u8, tree.tokenSlice(main_tokens[case.ast.values[0]]), "_"));
16971691 if (!is_special) continue;
16981692
1699 return nodeToSpan(tree, case_node);
1693 return tree.nodeToSpan(case_node);
17001694 } else unreachable;
17011695 },
17021696
......@@ -1718,7 +1712,7 @@ pub const SrcLoc = struct {
17181712
17191713 for (case.ast.values) |item_node| {
17201714 if (node_tags[item_node] == .switch_range) {
1721 return nodeToSpan(tree, item_node);
1715 return tree.nodeToSpan(item_node);
17221716 }
17231717 }
17241718 } else unreachable;
......@@ -1754,28 +1748,28 @@ pub const SrcLoc = struct {
17541748 const node = src_loc.declRelativeToNodeIndex(node_off);
17551749 var buf: [1]Ast.Node.Index = undefined;
17561750 const full = tree.fullFnProto(&buf, node).?;
1757 return nodeToSpan(tree, full.ast.align_expr);
1751 return tree.nodeToSpan(full.ast.align_expr);
17581752 },
17591753 .node_offset_fn_type_addrspace => |node_off| {
17601754 const tree = try src_loc.file_scope.getTree(gpa);
17611755 const node = src_loc.declRelativeToNodeIndex(node_off);
17621756 var buf: [1]Ast.Node.Index = undefined;
17631757 const full = tree.fullFnProto(&buf, node).?;
1764 return nodeToSpan(tree, full.ast.addrspace_expr);
1758 return tree.nodeToSpan(full.ast.addrspace_expr);
17651759 },
17661760 .node_offset_fn_type_section => |node_off| {
17671761 const tree = try src_loc.file_scope.getTree(gpa);
17681762 const node = src_loc.declRelativeToNodeIndex(node_off);
17691763 var buf: [1]Ast.Node.Index = undefined;
17701764 const full = tree.fullFnProto(&buf, node).?;
1771 return nodeToSpan(tree, full.ast.section_expr);
1765 return tree.nodeToSpan(full.ast.section_expr);
17721766 },
17731767 .node_offset_fn_type_cc => |node_off| {
17741768 const tree = try src_loc.file_scope.getTree(gpa);
17751769 const node = src_loc.declRelativeToNodeIndex(node_off);
17761770 var buf: [1]Ast.Node.Index = undefined;
17771771 const full = tree.fullFnProto(&buf, node).?;
1778 return nodeToSpan(tree, full.ast.callconv_expr);
1772 return tree.nodeToSpan(full.ast.callconv_expr);
17791773 },
17801774
17811775 .node_offset_fn_type_ret_ty => |node_off| {
......@@ -1783,7 +1777,7 @@ pub const SrcLoc = struct {
17831777 const node = src_loc.declRelativeToNodeIndex(node_off);
17841778 var buf: [1]Ast.Node.Index = undefined;
17851779 const full = tree.fullFnProto(&buf, node).?;
1786 return nodeToSpan(tree, full.ast.return_type);
1780 return tree.nodeToSpan(full.ast.return_type);
17871781 },
17881782 .node_offset_param => |node_off| {
17891783 const tree = try src_loc.file_scope.getTree(gpa);
......@@ -1795,8 +1789,7 @@ pub const SrcLoc = struct {
17951789 .colon, .identifier, .keyword_comptime, .keyword_noalias => first_tok -= 1,
17961790 else => break,
17971791 };
1798 return tokensToSpan(
1799 tree,
1792 return tree.tokensToSpan(
18001793 first_tok,
18011794 tree.lastToken(node),
18021795 first_tok,
......@@ -1813,8 +1806,7 @@ pub const SrcLoc = struct {
18131806 .colon, .identifier, .keyword_comptime, .keyword_noalias => first_tok -= 1,
18141807 else => break,
18151808 };
1816 return tokensToSpan(
1817 tree,
1809 return tree.tokensToSpan(
18181810 first_tok,
18191811 tok_index,
18201812 first_tok,
......@@ -1825,7 +1817,7 @@ pub const SrcLoc = struct {
18251817 const tree = try src_loc.file_scope.getTree(gpa);
18261818 const node_datas = tree.nodes.items(.data);
18271819 const parent_node = src_loc.declRelativeToNodeIndex(node_off);
1828 return nodeToSpan(tree, node_datas[parent_node].rhs);
1820 return tree.nodeToSpan(node_datas[parent_node].rhs);
18291821 },
18301822
18311823 .node_offset_lib_name => |node_off| {
......@@ -1844,70 +1836,70 @@ pub const SrcLoc = struct {
18441836 const parent_node = src_loc.declRelativeToNodeIndex(node_off);
18451837
18461838 const full = tree.fullArrayType(parent_node).?;
1847 return nodeToSpan(tree, full.ast.elem_count);
1839 return tree.nodeToSpan(full.ast.elem_count);
18481840 },
18491841 .node_offset_array_type_sentinel => |node_off| {
18501842 const tree = try src_loc.file_scope.getTree(gpa);
18511843 const parent_node = src_loc.declRelativeToNodeIndex(node_off);
18521844
18531845 const full = tree.fullArrayType(parent_node).?;
1854 return nodeToSpan(tree, full.ast.sentinel);
1846 return tree.nodeToSpan(full.ast.sentinel);
18551847 },
18561848 .node_offset_array_type_elem => |node_off| {
18571849 const tree = try src_loc.file_scope.getTree(gpa);
18581850 const parent_node = src_loc.declRelativeToNodeIndex(node_off);
18591851
18601852 const full = tree.fullArrayType(parent_node).?;
1861 return nodeToSpan(tree, full.ast.elem_type);
1853 return tree.nodeToSpan(full.ast.elem_type);
18621854 },
18631855 .node_offset_un_op => |node_off| {
18641856 const tree = try src_loc.file_scope.getTree(gpa);
18651857 const node_datas = tree.nodes.items(.data);
18661858 const node = src_loc.declRelativeToNodeIndex(node_off);
18671859
1868 return nodeToSpan(tree, node_datas[node].lhs);
1860 return tree.nodeToSpan(node_datas[node].lhs);
18691861 },
18701862 .node_offset_ptr_elem => |node_off| {
18711863 const tree = try src_loc.file_scope.getTree(gpa);
18721864 const parent_node = src_loc.declRelativeToNodeIndex(node_off);
18731865
18741866 const full = tree.fullPtrType(parent_node).?;
1875 return nodeToSpan(tree, full.ast.child_type);
1867 return tree.nodeToSpan(full.ast.child_type);
18761868 },
18771869 .node_offset_ptr_sentinel => |node_off| {
18781870 const tree = try src_loc.file_scope.getTree(gpa);
18791871 const parent_node = src_loc.declRelativeToNodeIndex(node_off);
18801872
18811873 const full = tree.fullPtrType(parent_node).?;
1882 return nodeToSpan(tree, full.ast.sentinel);
1874 return tree.nodeToSpan(full.ast.sentinel);
18831875 },
18841876 .node_offset_ptr_align => |node_off| {
18851877 const tree = try src_loc.file_scope.getTree(gpa);
18861878 const parent_node = src_loc.declRelativeToNodeIndex(node_off);
18871879
18881880 const full = tree.fullPtrType(parent_node).?;
1889 return nodeToSpan(tree, full.ast.align_node);
1881 return tree.nodeToSpan(full.ast.align_node);
18901882 },
18911883 .node_offset_ptr_addrspace => |node_off| {
18921884 const tree = try src_loc.file_scope.getTree(gpa);
18931885 const parent_node = src_loc.declRelativeToNodeIndex(node_off);
18941886
18951887 const full = tree.fullPtrType(parent_node).?;
1896 return nodeToSpan(tree, full.ast.addrspace_node);
1888 return tree.nodeToSpan(full.ast.addrspace_node);
18971889 },
18981890 .node_offset_ptr_bitoffset => |node_off| {
18991891 const tree = try src_loc.file_scope.getTree(gpa);
19001892 const parent_node = src_loc.declRelativeToNodeIndex(node_off);
19011893
19021894 const full = tree.fullPtrType(parent_node).?;
1903 return nodeToSpan(tree, full.ast.bit_range_start);
1895 return tree.nodeToSpan(full.ast.bit_range_start);
19041896 },
19051897 .node_offset_ptr_hostsize => |node_off| {
19061898 const tree = try src_loc.file_scope.getTree(gpa);
19071899 const parent_node = src_loc.declRelativeToNodeIndex(node_off);
19081900
19091901 const full = tree.fullPtrType(parent_node).?;
1910 return nodeToSpan(tree, full.ast.bit_range_end);
1902 return tree.nodeToSpan(full.ast.bit_range_end);
19111903 },
19121904 .node_offset_container_tag => |node_off| {
19131905 const tree = try src_loc.file_scope.getTree(gpa);
......@@ -1917,13 +1909,12 @@ pub const SrcLoc = struct {
19171909 switch (node_tags[parent_node]) {
19181910 .container_decl_arg, .container_decl_arg_trailing => {
19191911 const full = tree.containerDeclArg(parent_node);
1920 return nodeToSpan(tree, full.ast.arg);
1912 return tree.nodeToSpan(full.ast.arg);
19211913 },
19221914 .tagged_union_enum_tag, .tagged_union_enum_tag_trailing => {
19231915 const full = tree.taggedUnionEnumTag(parent_node);
19241916
1925 return tokensToSpan(
1926 tree,
1917 return tree.tokensToSpan(
19271918 tree.firstToken(full.ast.arg) - 2,
19281919 tree.lastToken(full.ast.arg) + 1,
19291920 tree.nodes.items(.main_token)[full.ast.arg],
......@@ -1942,7 +1933,7 @@ pub const SrcLoc = struct {
19421933 .container_field_init => tree.containerFieldInit(parent_node),
19431934 else => unreachable,
19441935 };
1945 return nodeToSpan(tree, full.ast.value_expr);
1936 return tree.nodeToSpan(full.ast.value_expr);
19461937 },
19471938 .node_offset_init_ty => |node_off| {
19481939 const tree = try src_loc.file_scope.getTree(gpa);
......@@ -1950,7 +1941,7 @@ pub const SrcLoc = struct {
19501941
19511942 var buf: [2]Ast.Node.Index = undefined;
19521943 const full = tree.fullArrayInit(&buf, parent_node).?;
1953 return nodeToSpan(tree, full.ast.type_expr);
1944 return tree.nodeToSpan(full.ast.type_expr);
19541945 },
19551946 .node_offset_store_ptr => |node_off| {
19561947 const tree = try src_loc.file_scope.getTree(gpa);
......@@ -1960,9 +1951,9 @@ pub const SrcLoc = struct {
19601951
19611952 switch (node_tags[node]) {
19621953 .assign => {
1963 return nodeToSpan(tree, node_datas[node].lhs);
1954 return tree.nodeToSpan(node_datas[node].lhs);
19641955 },
1965 else => return nodeToSpan(tree, node),
1956 else => return tree.nodeToSpan(node),
19661957 }
19671958 },
19681959 .node_offset_store_operand => |node_off| {
......@@ -1973,9 +1964,9 @@ pub const SrcLoc = struct {
19731964
19741965 switch (node_tags[node]) {
19751966 .assign => {
1976 return nodeToSpan(tree, node_datas[node].rhs);
1967 return tree.nodeToSpan(node_datas[node].rhs);
19771968 },
1978 else => return nodeToSpan(tree, node),
1969 else => return tree.nodeToSpan(node),
19791970 }
19801971 },
19811972 .node_offset_return_operand => |node_off| {
......@@ -1984,9 +1975,9 @@ pub const SrcLoc = struct {
19841975 const node_tags = tree.nodes.items(.tag);
19851976 const node_datas = tree.nodes.items(.data);
19861977 if (node_tags[node] == .@"return" and node_datas[node].lhs != 0) {
1987 return nodeToSpan(tree, node_datas[node].lhs);
1978 return tree.nodeToSpan(node_datas[node].lhs);
19881979 }
1989 return nodeToSpan(tree, node);
1980 return tree.nodeToSpan(node);
19901981 },
19911982 }
19921983 }
......@@ -2010,40 +2001,7 @@ pub const SrcLoc = struct {
20102001 .builtin_call, .builtin_call_comma => tree.extra_data[node_datas[node].lhs + arg_index],
20112002 else => unreachable,
20122003 };
2013 return nodeToSpan(tree, param);
2014 }
2015
2016 pub fn nodeToSpan(tree: *const Ast, node: u32) Span {
2017 return tokensToSpan(
2018 tree,
2019 tree.firstToken(node),
2020 tree.lastToken(node),
2021 tree.nodes.items(.main_token)[node],
2022 );
2023 }
2024
2025 fn tokenToSpan(tree: *const Ast, token: Ast.TokenIndex) Span {
2026 return tokensToSpan(tree, token, token, token);
2027 }
2028
2029 fn tokensToSpan(tree: *const Ast, start: Ast.TokenIndex, end: Ast.TokenIndex, main: Ast.TokenIndex) Span {
2030 const token_starts = tree.tokens.items(.start);
2031 var start_tok = start;
2032 var end_tok = end;
2033
2034 if (tree.tokensOnSameLine(start, end)) {
2035 // do nothing
2036 } else if (tree.tokensOnSameLine(start, main)) {
2037 end_tok = main;
2038 } else if (tree.tokensOnSameLine(main, end)) {
2039 start_tok = main;
2040 } else {
2041 start_tok = main;
2042 end_tok = main;
2043 }
2044 const start_off = token_starts[start_tok];
2045 const end_off = token_starts[end_tok] + @as(u32, @intCast(tree.tokenSlice(end_tok).len));
2046 return Span{ .start = start_off, .end = end_off, .main = token_starts[main] };
2004 return tree.nodeToSpan(param);
20472005 }
20482006};
20492007
src/Package/Fetch.zig+1-2
......@@ -592,7 +592,7 @@ fn loadManifest(f: *Fetch, pkg_root: Package.Path) RunError!void {
592592
593593 if (ast.errors.len > 0) {
594594 const file_path = try std.fmt.allocPrint(arena, "{}" ++ Manifest.basename, .{pkg_root});
595 try main.putAstErrorsIntoBundle(arena, ast.*, file_path, eb);
595 try std.zig.putAstErrorsIntoBundle(arena, ast.*, file_path, eb);
596596 return error.FetchFailed;
597597 }
598598
......@@ -1690,7 +1690,6 @@ const Cache = std.Build.Cache;
16901690const ThreadPool = std.Thread.Pool;
16911691const WaitGroup = std.Thread.WaitGroup;
16921692const Fetch = @This();
1693const main = @import("../main.zig");
16941693const git = @import("Fetch/git.zig");
16951694const Package = @import("../Package.zig");
16961695const Manifest = Package.Manifest;
src/main.zig+134-475
......@@ -8,6 +8,7 @@ const process = std.process;
88const Allocator = mem.Allocator;
99const ArrayList = std.ArrayList;
1010const Ast = std.zig.Ast;
11const Color = std.zig.Color;
1112const warn = std.log.warn;
1213const ThreadPool = std.Thread.Pool;
1314const cleanExit = std.process.cleanExit;
......@@ -66,18 +67,8 @@ pub fn fatal(comptime format: []const u8, args: anytype) noreturn {
6667 process.exit(1);
6768}
6869
69/// There are many assumptions in the entire codebase that Zig source files can
70/// be byte-indexed with a u32 integer.
71const max_src_size = std.math.maxInt(u32);
72
7370const debug_extensions_enabled = builtin.mode == .Debug;
7471
75const Color = enum {
76 auto,
77 off,
78 on,
79};
80
8172const normal_usage =
8273 \\Usage: zig [command] [options]
8374 \\
......@@ -4501,7 +4492,7 @@ fn updateModule(comp: *Compilation, color: Color) !void {
45014492 defer errors.deinit(comp.gpa);
45024493
45034494 if (errors.errorMessageCount() > 0) {
4504 errors.renderToStdErr(renderOptions(color));
4495 errors.renderToStdErr(color.renderOptions());
45054496 return error.SemanticAnalyzeFail;
45064497 }
45074498}
......@@ -4601,7 +4592,7 @@ fn cmdTranslateC(comp: *Compilation, arena: Allocator, fancy_output: ?*Compilati
46014592 p.errors = errors;
46024593 return;
46034594 } else {
4604 errors.renderToStdErr(renderOptions(color));
4595 errors.renderToStdErr(color.renderOptions());
46054596 process.exit(1);
46064597 }
46074598 },
......@@ -5528,7 +5519,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
55285519
55295520 if (fetch.error_bundle.root_list.items.len > 0) {
55305521 var errors = try fetch.error_bundle.toOwnedBundle("");
5531 errors.renderToStdErr(renderOptions(color));
5522 errors.renderToStdErr(color.renderOptions());
55325523 process.exit(1);
55335524 }
55345525
......@@ -5719,470 +5710,155 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
57195710 }
57205711}
57215712
5722fn readSourceFileToEndAlloc(
5723 allocator: Allocator,
5724 input: *const fs.File,
5725 size_hint: ?usize,
5726) ![:0]u8 {
5727 const source_code = input.readToEndAllocOptions(
5728 allocator,
5729 max_src_size,
5730 size_hint,
5731 @alignOf(u16),
5732 0,
5733 ) catch |err| switch (err) {
5734 error.ConnectionResetByPeer => unreachable,
5735 error.ConnectionTimedOut => unreachable,
5736 error.NotOpenForReading => unreachable,
5737 else => |e| return e,
5738 };
5739 errdefer allocator.free(source_code);
5713fn cmdFmt(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
5714 const color: Color = .auto;
57405715
5741 // Detect unsupported file types with their Byte Order Mark
5742 const unsupported_boms = [_][]const u8{
5743 "\xff\xfe\x00\x00", // UTF-32 little endian
5744 "\xfe\xff\x00\x00", // UTF-32 big endian
5745 "\xfe\xff", // UTF-16 big endian
5716 const target_query: std.Target.Query = .{};
5717 const resolved_target: Package.Module.ResolvedTarget = .{
5718 .result = resolveTargetQueryOrFatal(target_query),
5719 .is_native_os = true,
5720 .is_native_abi = true,
57465721 };
5747 for (unsupported_boms) |bom| {
5748 if (mem.startsWith(u8, source_code, bom)) {
5749 return error.UnsupportedEncoding;
5750 }
5751 }
57525722
5753 // If the file starts with a UTF-16 little endian BOM, translate it to UTF-8
5754 if (mem.startsWith(u8, source_code, "\xff\xfe")) {
5755 const source_code_utf16_le = mem.bytesAsSlice(u16, source_code);
5756 const source_code_utf8 = std.unicode.utf16LeToUtf8AllocZ(allocator, source_code_utf16_le) catch |err| switch (err) {
5757 error.DanglingSurrogateHalf => error.UnsupportedEncoding,
5758 error.ExpectedSecondSurrogateHalf => error.UnsupportedEncoding,
5759 error.UnexpectedSecondSurrogateHalf => error.UnsupportedEncoding,
5760 else => |e| return e,
5761 };
5723 const exe_basename = try std.zig.binNameAlloc(arena, .{
5724 .root_name = "fmt",
5725 .target = resolved_target.result,
5726 .output_mode = .Exe,
5727 });
5728 const emit_bin: Compilation.EmitLoc = .{
5729 .directory = null, // Use the global zig-cache.
5730 .basename = exe_basename,
5731 };
57625732
5763 allocator.free(source_code);
5764 return source_code_utf8;
5765 }
5733 const self_exe_path = introspect.findZigExePath(arena) catch |err| {
5734 fatal("unable to find self exe path: {s}", .{@errorName(err)});
5735 };
57665736
5767 return source_code;
5768}
5737 const override_lib_dir: ?[]const u8 = try EnvVar.ZIG_LIB_DIR.get(arena);
5738 const override_global_cache_dir: ?[]const u8 = try EnvVar.ZIG_GLOBAL_CACHE_DIR.get(arena);
57695739
5770const usage_fmt =
5771 \\Usage: zig fmt [file]...
5772 \\
5773 \\ Formats the input files and modifies them in-place.
5774 \\ Arguments can be files or directories, which are searched
5775 \\ recursively.
5776 \\
5777 \\Options:
5778 \\ -h, --help Print this help and exit
5779 \\ --color [auto|off|on] Enable or disable colored error messages
5780 \\ --stdin Format code from stdin; output to stdout
5781 \\ --check List non-conforming files and exit with an error
5782 \\ if the list is non-empty
5783 \\ --ast-check Run zig ast-check on every file
5784 \\ --exclude [file] Exclude file or directory from formatting
5785 \\
5786 \\
5787;
5740 var zig_lib_directory: Compilation.Directory = if (override_lib_dir) |lib_dir| .{
5741 .path = lib_dir,
5742 .handle = fs.cwd().openDir(lib_dir, .{}) catch |err| {
5743 fatal("unable to open zig lib directory from 'zig-lib-dir' argument: '{s}': {s}", .{ lib_dir, @errorName(err) });
5744 },
5745 } else introspect.findZigLibDirFromSelfExe(arena, self_exe_path) catch |err| {
5746 fatal("unable to find zig installation directory '{s}': {s}", .{ self_exe_path, @errorName(err) });
5747 };
5748 defer zig_lib_directory.handle.close();
57885749
5789const Fmt = struct {
5790 seen: SeenMap,
5791 any_error: bool,
5792 check_ast: bool,
5793 color: Color,
5794 gpa: Allocator,
5795 arena: Allocator,
5796 out_buffer: std.ArrayList(u8),
5750 var global_cache_directory: Compilation.Directory = l: {
5751 const p = override_global_cache_dir orelse try introspect.resolveGlobalCacheDir(arena);
5752 break :l .{
5753 .handle = try fs.cwd().makeOpenPath(p, .{}),
5754 .path = p,
5755 };
5756 };
5757 defer global_cache_directory.handle.close();
57975758
5798 const SeenMap = std.AutoHashMap(fs.File.INode, void);
5799};
5759 var thread_pool: ThreadPool = undefined;
5760 try thread_pool.init(.{ .allocator = gpa });
5761 defer thread_pool.deinit();
58005762
5801fn cmdFmt(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
5802 var color: Color = .auto;
5803 var stdin_flag: bool = false;
5804 var check_flag: bool = false;
5805 var check_ast_flag: bool = false;
5806 var input_files = ArrayList([]const u8).init(gpa);
5807 defer input_files.deinit();
5808 var excluded_files = ArrayList([]const u8).init(gpa);
5809 defer excluded_files.deinit();
5763 var child_argv: std.ArrayListUnmanaged([]const u8) = .{};
5764 try child_argv.ensureUnusedCapacity(arena, args.len + 1);
58105765
5766 // We want to release all the locks before executing the child process, so we make a nice
5767 // big block here to ensure the cleanup gets run when we extract out our argv.
58115768 {
5812 var i: usize = 0;
5813 while (i < args.len) : (i += 1) {
5814 const arg = args[i];
5815 if (mem.startsWith(u8, arg, "-")) {
5816 if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) {
5817 const stdout = io.getStdOut().writer();
5818 try stdout.writeAll(usage_fmt);
5819 return cleanExit();
5820 } else if (mem.eql(u8, arg, "--color")) {
5821 if (i + 1 >= args.len) {
5822 fatal("expected [auto|on|off] after --color", .{});
5823 }
5824 i += 1;
5825 const next_arg = args[i];
5826 color = std.meta.stringToEnum(Color, next_arg) orelse {
5827 fatal("expected [auto|on|off] after --color, found '{s}'", .{next_arg});
5828 };
5829 } else if (mem.eql(u8, arg, "--stdin")) {
5830 stdin_flag = true;
5831 } else if (mem.eql(u8, arg, "--check")) {
5832 check_flag = true;
5833 } else if (mem.eql(u8, arg, "--ast-check")) {
5834 check_ast_flag = true;
5835 } else if (mem.eql(u8, arg, "--exclude")) {
5836 if (i + 1 >= args.len) {
5837 fatal("expected parameter after --exclude", .{});
5838 }
5839 i += 1;
5840 const next_arg = args[i];
5841 try excluded_files.append(next_arg);
5842 } else {
5843 fatal("unrecognized parameter: '{s}'", .{arg});
5844 }
5845 } else {
5846 try input_files.append(arg);
5847 }
5848 }
5849 }
5850
5851 if (stdin_flag) {
5852 if (input_files.items.len != 0) {
5853 fatal("cannot use --stdin with positional arguments", .{});
5854 }
5855
5856 const stdin = io.getStdIn();
5857 const source_code = readSourceFileToEndAlloc(gpa, &stdin, null) catch |err| {
5858 fatal("unable to read stdin: {}", .{err});
5859 };
5860 defer gpa.free(source_code);
5861
5862 var tree = Ast.parse(gpa, source_code, .zig) catch |err| {
5863 fatal("error parsing stdin: {}", .{err});
5769 const main_mod_paths: Package.Module.CreateOptions.Paths = .{
5770 .root = .{
5771 .root_dir = zig_lib_directory,
5772 .sub_path = "std/zig",
5773 },
5774 .root_src_path = "fmt.zig",
58645775 };
5865 defer tree.deinit(gpa);
5866
5867 if (check_ast_flag) {
5868 var file: Module.File = .{
5869 .status = .never_loaded,
5870 .source_loaded = true,
5871 .zir_loaded = false,
5872 .sub_file_path = "<stdin>",
5873 .source = source_code,
5874 .stat = undefined,
5875 .tree = tree,
5876 .tree_loaded = true,
5877 .zir = undefined,
5878 .mod = undefined,
5879 .root_decl = .none,
5880 };
5881
5882 file.mod = try Package.Module.createLimited(arena, .{
5883 .root = Package.Path.cwd(),
5884 .root_src_path = file.sub_file_path,
5885 .fully_qualified_name = "root",
5886 });
5887
5888 file.zir = try AstGen.generate(gpa, file.tree);
5889 file.zir_loaded = true;
5890 defer file.zir.deinit(gpa);
5891
5892 if (file.zir.hasCompileErrors()) {
5893 var wip_errors: std.zig.ErrorBundle.Wip = undefined;
5894 try wip_errors.init(gpa);
5895 defer wip_errors.deinit();
5896 try Compilation.addZirErrorMessages(&wip_errors, &file);
5897 var error_bundle = try wip_errors.toOwnedBundle("");
5898 defer error_bundle.deinit(gpa);
5899 error_bundle.renderToStdErr(renderOptions(color));
5900 process.exit(2);
5901 }
5902 } else if (tree.errors.len != 0) {
5903 try printAstErrorsToStderr(gpa, tree, "<stdin>", color);
5904 process.exit(2);
5905 }
5906 const formatted = try tree.render(gpa);
5907 defer gpa.free(formatted);
5908
5909 if (check_flag) {
5910 const code: u8 = @intFromBool(mem.eql(u8, formatted, source_code));
5911 process.exit(code);
5912 }
59135776
5914 return io.getStdOut().writeAll(formatted);
5915 }
5777 const config = try Compilation.Config.resolve(.{
5778 .output_mode = .Exe,
5779 .root_optimize_mode = .ReleaseFast,
5780 .resolved_target = resolved_target,
5781 .have_zcu = true,
5782 .emit_bin = true,
5783 .is_test = false,
5784 });
59165785
5917 if (input_files.items.len == 0) {
5918 fatal("expected at least one source file argument", .{});
5919 }
5786 const root_mod = try Package.Module.create(arena, .{
5787 .global_cache_directory = global_cache_directory,
5788 .paths = main_mod_paths,
5789 .fully_qualified_name = "root",
5790 .cc_argv = &.{},
5791 .inherited = .{
5792 .resolved_target = resolved_target,
5793 .optimize_mode = .ReleaseFast,
5794 },
5795 .global = config,
5796 .parent = null,
5797 .builtin_mod = null,
5798 });
59205799
5921 var fmt = Fmt{
5922 .gpa = gpa,
5923 .arena = arena,
5924 .seen = Fmt.SeenMap.init(gpa),
5925 .any_error = false,
5926 .check_ast = check_ast_flag,
5927 .color = color,
5928 .out_buffer = std.ArrayList(u8).init(gpa),
5929 };
5930 defer fmt.seen.deinit();
5931 defer fmt.out_buffer.deinit();
5800 const comp = Compilation.create(gpa, arena, .{
5801 .zig_lib_directory = zig_lib_directory,
5802 .local_cache_directory = global_cache_directory,
5803 .global_cache_directory = global_cache_directory,
5804 .root_name = "fmt",
5805 .config = config,
5806 .root_mod = root_mod,
5807 .main_mod = root_mod,
5808 .emit_bin = emit_bin,
5809 .emit_h = null,
5810 .self_exe_path = self_exe_path,
5811 .thread_pool = &thread_pool,
5812 .cache_mode = .whole,
5813 }) catch |err| {
5814 fatal("unable to create compilation: {s}", .{@errorName(err)});
5815 };
5816 defer comp.destroy();
59325817
5933 // Mark any excluded files/directories as already seen,
5934 // so that they are skipped later during actual processing
5935 for (excluded_files.items) |file_path| {
5936 const stat = fs.cwd().statFile(file_path) catch |err| switch (err) {
5937 error.FileNotFound => continue,
5938 // On Windows, statFile does not work for directories
5939 error.IsDir => dir: {
5940 var dir = try fs.cwd().openDir(file_path, .{});
5941 defer dir.close();
5942 break :dir try dir.stat();
5943 },
5818 updateModule(comp, color) catch |err| switch (err) {
5819 error.SemanticAnalyzeFail => process.exit(2),
59445820 else => |e| return e,
59455821 };
5946 try fmt.seen.put(stat.inode, {});
5947 }
5948
5949 for (input_files.items) |file_path| {
5950 try fmtPath(&fmt, file_path, check_flag, fs.cwd(), file_path);
5951 }
5952 if (fmt.any_error) {
5953 process.exit(1);
5954 }
5955}
5956
5957const FmtError = error{
5958 SystemResources,
5959 OperationAborted,
5960 IoPending,
5961 BrokenPipe,
5962 Unexpected,
5963 WouldBlock,
5964 FileClosed,
5965 DestinationAddressRequired,
5966 DiskQuota,
5967 FileTooBig,
5968 InputOutput,
5969 NoSpaceLeft,
5970 AccessDenied,
5971 OutOfMemory,
5972 RenameAcrossMountPoints,
5973 ReadOnlyFileSystem,
5974 LinkQuotaExceeded,
5975 FileBusy,
5976 EndOfStream,
5977 Unseekable,
5978 NotOpenForWriting,
5979 UnsupportedEncoding,
5980 ConnectionResetByPeer,
5981 SocketNotConnected,
5982 LockViolation,
5983 NetNameDeleted,
5984 InvalidArgument,
5985} || fs.File.OpenError;
5986
5987fn fmtPath(fmt: *Fmt, file_path: []const u8, check_mode: bool, dir: fs.Dir, sub_path: []const u8) FmtError!void {
5988 fmtPathFile(fmt, file_path, check_mode, dir, sub_path) catch |err| switch (err) {
5989 error.IsDir, error.AccessDenied => return fmtPathDir(fmt, file_path, check_mode, dir, sub_path),
5990 else => {
5991 warn("unable to format '{s}': {s}", .{ file_path, @errorName(err) });
5992 fmt.any_error = true;
5993 return;
5994 },
5995 };
5996}
5997
5998fn fmtPathDir(
5999 fmt: *Fmt,
6000 file_path: []const u8,
6001 check_mode: bool,
6002 parent_dir: fs.Dir,
6003 parent_sub_path: []const u8,
6004) FmtError!void {
6005 var dir = try parent_dir.openDir(parent_sub_path, .{ .iterate = true });
6006 defer dir.close();
6007
6008 const stat = try dir.stat();
6009 if (try fmt.seen.fetchPut(stat.inode, {})) |_| return;
6010
6011 var dir_it = dir.iterate();
6012 while (try dir_it.next()) |entry| {
6013 const is_dir = entry.kind == .directory;
6014
6015 if (is_dir and (mem.eql(u8, entry.name, "zig-cache") or mem.eql(u8, entry.name, "zig-out"))) continue;
6016
6017 if (is_dir or entry.kind == .file and (mem.endsWith(u8, entry.name, ".zig") or mem.endsWith(u8, entry.name, ".zon"))) {
6018 const full_path = try fs.path.join(fmt.gpa, &[_][]const u8{ file_path, entry.name });
6019 defer fmt.gpa.free(full_path);
6020
6021 if (is_dir) {
6022 try fmtPathDir(fmt, full_path, check_mode, dir, entry.name);
6023 } else {
6024 fmtPathFile(fmt, full_path, check_mode, dir, entry.name) catch |err| {
6025 warn("unable to format '{s}': {s}", .{ full_path, @errorName(err) });
6026 fmt.any_error = true;
6027 return;
6028 };
6029 }
6030 }
6031 }
6032}
6033
6034fn fmtPathFile(
6035 fmt: *Fmt,
6036 file_path: []const u8,
6037 check_mode: bool,
6038 dir: fs.Dir,
6039 sub_path: []const u8,
6040) FmtError!void {
6041 const source_file = try dir.openFile(sub_path, .{});
6042 var file_closed = false;
6043 errdefer if (!file_closed) source_file.close();
6044
6045 const stat = try source_file.stat();
6046
6047 if (stat.kind == .directory)
6048 return error.IsDir;
6049
6050 const gpa = fmt.gpa;
6051 const source_code = try readSourceFileToEndAlloc(
6052 gpa,
6053 &source_file,
6054 std.math.cast(usize, stat.size) orelse return error.FileTooBig,
6055 );
6056 defer gpa.free(source_code);
6057
6058 source_file.close();
6059 file_closed = true;
6060
6061 // Add to set after no longer possible to get error.IsDir.
6062 if (try fmt.seen.fetchPut(stat.inode, {})) |_| return;
6063
6064 var tree = try Ast.parse(gpa, source_code, .zig);
6065 defer tree.deinit(gpa);
60665822
6067 if (tree.errors.len != 0) {
6068 try printAstErrorsToStderr(gpa, tree, file_path, fmt.color);
6069 fmt.any_error = true;
6070 return;
5823 const fmt_exe = try global_cache_directory.join(arena, &.{comp.cache_use.whole.bin_sub_path.?});
5824 child_argv.appendAssumeCapacity(fmt_exe);
60715825 }
60725826
6073 if (fmt.check_ast) {
6074 var file: Module.File = .{
6075 .status = .never_loaded,
6076 .source_loaded = true,
6077 .zir_loaded = false,
6078 .sub_file_path = file_path,
6079 .source = source_code,
6080 .stat = .{
6081 .size = stat.size,
6082 .inode = stat.inode,
6083 .mtime = stat.mtime,
6084 },
6085 .tree = tree,
6086 .tree_loaded = true,
6087 .zir = undefined,
6088 .mod = undefined,
6089 .root_decl = .none,
6090 };
5827 child_argv.appendSliceAssumeCapacity(args);
60915828
6092 file.mod = try Package.Module.createLimited(fmt.arena, .{
6093 .root = Package.Path.cwd(),
6094 .root_src_path = file.sub_file_path,
6095 .fully_qualified_name = "root",
5829 if (process.can_execv) {
5830 const err = process.execv(gpa, child_argv.items);
5831 const cmd = try std.mem.join(arena, " ", child_argv.items);
5832 fatal("the following command failed to execve with '{s}':\n{s}", .{
5833 @errorName(err),
5834 cmd,
60965835 });
6097
6098 if (stat.size > max_src_size)
6099 return error.FileTooBig;
6100
6101 file.zir = try AstGen.generate(gpa, file.tree);
6102 file.zir_loaded = true;
6103 defer file.zir.deinit(gpa);
6104
6105 if (file.zir.hasCompileErrors()) {
6106 var wip_errors: std.zig.ErrorBundle.Wip = undefined;
6107 try wip_errors.init(gpa);
6108 defer wip_errors.deinit();
6109 try Compilation.addZirErrorMessages(&wip_errors, &file);
6110 var error_bundle = try wip_errors.toOwnedBundle("");
6111 defer error_bundle.deinit(gpa);
6112 error_bundle.renderToStdErr(renderOptions(fmt.color));
6113 fmt.any_error = true;
6114 }
61155836 }
61165837
6117 // As a heuristic, we make enough capacity for the same as the input source.
6118 fmt.out_buffer.shrinkRetainingCapacity(0);
6119 try fmt.out_buffer.ensureTotalCapacity(source_code.len);
6120
6121 try tree.renderToArrayList(&fmt.out_buffer, .{});
6122 if (mem.eql(u8, fmt.out_buffer.items, source_code))
6123 return;
6124
6125 if (check_mode) {
6126 const stdout = io.getStdOut().writer();
6127 try stdout.print("{s}\n", .{file_path});
6128 fmt.any_error = true;
6129 } else {
6130 var af = try dir.atomicFile(sub_path, .{ .mode = stat.mode });
6131 defer af.deinit();
6132
6133 try af.file.writeAll(fmt.out_buffer.items);
6134 try af.finish();
6135 const stdout = io.getStdOut().writer();
6136 try stdout.print("{s}\n", .{file_path});
5838 if (!process.can_spawn) {
5839 const cmd = try std.mem.join(arena, " ", child_argv.items);
5840 fatal("the following command cannot be executed ({s} does not support spawning a child process):\n{s}", .{
5841 @tagName(builtin.os.tag), cmd,
5842 });
61375843 }
6138}
6139
6140fn printAstErrorsToStderr(gpa: Allocator, tree: Ast, path: []const u8, color: Color) !void {
6141 var wip_errors: std.zig.ErrorBundle.Wip = undefined;
6142 try wip_errors.init(gpa);
6143 defer wip_errors.deinit();
61445844
6145 try putAstErrorsIntoBundle(gpa, tree, path, &wip_errors);
5845 var child = std.ChildProcess.init(child_argv.items, gpa);
5846 child.stdin_behavior = .Inherit;
5847 child.stdout_behavior = .Inherit;
5848 child.stderr_behavior = .Inherit;
61465849
6147 var error_bundle = try wip_errors.toOwnedBundle("");
6148 defer error_bundle.deinit(gpa);
6149 error_bundle.renderToStdErr(renderOptions(color));
6150}
6151
6152pub fn putAstErrorsIntoBundle(
6153 gpa: Allocator,
6154 tree: Ast,
6155 path: []const u8,
6156 wip_errors: *std.zig.ErrorBundle.Wip,
6157) Allocator.Error!void {
6158 var file: Module.File = .{
6159 .status = .never_loaded,
6160 .source_loaded = true,
6161 .zir_loaded = false,
6162 .sub_file_path = path,
6163 .source = tree.source,
6164 .stat = .{
6165 .size = 0,
6166 .inode = 0,
6167 .mtime = 0,
5850 const term = try child.spawnAndWait();
5851 switch (term) {
5852 .Exited => |code| {
5853 if (code == 0) return cleanExit();
5854 const cmd = try std.mem.join(arena, " ", child_argv.items);
5855 fatal("the following build command failed with exit code {d}:\n{s}", .{ code, cmd });
61685856 },
6169 .tree = tree,
6170 .tree_loaded = true,
6171 .zir = undefined,
6172 .mod = try Package.Module.createLimited(gpa, .{
6173 .root = Package.Path.cwd(),
6174 .root_src_path = path,
6175 .fully_qualified_name = "root",
6176 }),
6177 .root_decl = .none,
6178 };
6179 defer gpa.destroy(file.mod);
6180
6181 file.zir = try AstGen.generate(gpa, file.tree);
6182 file.zir_loaded = true;
6183 defer file.zir.deinit(gpa);
6184
6185 try Compilation.addZirErrorMessages(wip_errors, &file);
5857 else => {
5858 const cmd = try std.mem.join(arena, " ", child_argv.items);
5859 fatal("the following build command crashed:\n{s}", .{cmd});
5860 },
5861 }
61865862}
61875863
61885864const info_zen =
......@@ -6710,7 +6386,7 @@ fn cmdAstCheck(
67106386
67116387 const stat = try f.stat();
67126388
6713 if (stat.size > max_src_size)
6389 if (stat.size > std.zig.max_src_size)
67146390 return error.FileTooBig;
67156391
67166392 const source = try arena.allocSentinel(u8, @as(usize, @intCast(stat.size)), 0);
......@@ -6728,7 +6404,7 @@ fn cmdAstCheck(
67286404 };
67296405 } else {
67306406 const stdin = io.getStdIn();
6731 const source = readSourceFileToEndAlloc(arena, &stdin, null) catch |err| {
6407 const source = std.zig.readSourceFileToEndAlloc(arena, stdin, null) catch |err| {
67326408 fatal("unable to read stdin: {}", .{err});
67336409 };
67346410 file.sub_file_path = "<stdin>";
......@@ -6758,7 +6434,7 @@ fn cmdAstCheck(
67586434 try Compilation.addZirErrorMessages(&wip_errors, &file);
67596435 var error_bundle = try wip_errors.toOwnedBundle("");
67606436 defer error_bundle.deinit(gpa);
6761 error_bundle.renderToStdErr(renderOptions(color));
6437 error_bundle.renderToStdErr(color.renderOptions());
67626438 process.exit(1);
67636439 }
67646440
......@@ -6889,7 +6565,7 @@ fn cmdChangelist(
68896565
68906566 const stat = try f.stat();
68916567
6892 if (stat.size > max_src_size)
6568 if (stat.size > std.zig.max_src_size)
68936569 return error.FileTooBig;
68946570
68956571 var file: Module.File = .{
......@@ -6938,7 +6614,7 @@ fn cmdChangelist(
69386614 try Compilation.addZirErrorMessages(&wip_errors, &file);
69396615 var error_bundle = try wip_errors.toOwnedBundle("");
69406616 defer error_bundle.deinit(gpa);
6941 error_bundle.renderToStdErr(renderOptions(color));
6617 error_bundle.renderToStdErr(color.renderOptions());
69426618 process.exit(1);
69436619 }
69446620
......@@ -6949,7 +6625,7 @@ fn cmdChangelist(
69496625
69506626 const new_stat = try new_f.stat();
69516627
6952 if (new_stat.size > max_src_size)
6628 if (new_stat.size > std.zig.max_src_size)
69536629 return error.FileTooBig;
69546630
69556631 const new_source = try arena.allocSentinel(u8, @as(usize, @intCast(new_stat.size)), 0);
......@@ -6973,7 +6649,7 @@ fn cmdChangelist(
69736649 try Compilation.addZirErrorMessages(&wip_errors, &file);
69746650 var error_bundle = try wip_errors.toOwnedBundle("");
69756651 defer error_bundle.deinit(gpa);
6976 error_bundle.renderToStdErr(renderOptions(color));
6652 error_bundle.renderToStdErr(color.renderOptions());
69776653 process.exit(1);
69786654 }
69796655
......@@ -7241,23 +6917,6 @@ const ClangSearchSanitizer = struct {
72416917 };
72426918};
72436919
7244fn get_tty_conf(color: Color) std.io.tty.Config {
7245 return switch (color) {
7246 .auto => std.io.tty.detectConfig(std.io.getStdErr()),
7247 .on => .escape_codes,
7248 .off => .no_color,
7249 };
7250}
7251
7252fn renderOptions(color: Color) std.zig.ErrorBundle.RenderOptions {
7253 const ttyconf = get_tty_conf(color);
7254 return .{
7255 .ttyconf = ttyconf,
7256 .include_source_line = ttyconf != .no_color,
7257 .include_reference_trace = ttyconf != .no_color,
7258 };
7259}
7260
72616920fn accessLibPath(
72626921 test_path: *std.ArrayList(u8),
72636922 checked_paths: *std.ArrayList(u8),
......@@ -7498,7 +7157,7 @@ fn cmdFetch(
74987157
74997158 if (fetch.error_bundle.root_list.items.len > 0) {
75007159 var errors = try fetch.error_bundle.toOwnedBundle("");
7501 errors.renderToStdErr(renderOptions(color));
7160 errors.renderToStdErr(color.renderOptions());
75027161 process.exit(1);
75037162 }
75047163
......@@ -7790,7 +7449,7 @@ fn loadManifest(
77907449 errdefer ast.deinit(gpa);
77917450
77927451 if (ast.errors.len > 0) {
7793 try printAstErrorsToStderr(gpa, ast, Package.Manifest.basename, options.color);
7452 try std.zig.printAstErrorsToStderr(gpa, ast, Package.Manifest.basename, options.color);
77947453 process.exit(2);
77957454 }
77967455
......@@ -7807,7 +7466,7 @@ fn loadManifest(
78077466
78087467 var error_bundle = try wip_errors.toOwnedBundle("");
78097468 defer error_bundle.deinit(gpa);
7810 error_bundle.renderToStdErr(renderOptions(options.color));
7469 error_bundle.renderToStdErr(options.color.renderOptions());
78117470
78127471 process.exit(2);
78137472 }