authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-10-02 17:00:45-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-10-02 17:02:25-07:00
log21181181bf1060d5e55738651c109d7c47647633
treeee868ceb21ddb959808b7619e0597b3f546ca1ae
parentef9966c9855dd855afda767f212abec6e5a36307

zig fetch: enhanced error reporting

* Package: use std.tar diagnostics to give detailed error messages * std.tar: add diagnostic for unsupported file type

4 files changed, 148 insertions(+), 55 deletions(-)

lib/std/tar.zig+24-12
......@@ -29,6 +29,10 @@ pub const Options = struct {
2929 file_name: []const u8,
3030 link_name: []const u8,
3131 },
32 unsupported_file_type: struct {
33 file_name: []const u8,
34 file_type: Header.FileType,
35 },
3236 };
3337
3438 pub fn deinit(d: *Diagnostics) void {
......@@ -38,6 +42,9 @@ pub const Options = struct {
3842 d.allocator.free(info.file_name);
3943 d.allocator.free(info.link_name);
4044 },
45 .unsupported_file_type => |info| {
46 d.allocator.free(info.file_name);
47 },
4148 }
4249 }
4350 d.errors.deinit(d.allocator);
......@@ -50,6 +57,7 @@ pub const Header = struct {
5057 bytes: *const [512]u8,
5158
5259 pub const FileType = enum(u8) {
60 normal_alias = 0,
5361 normal = '0',
5462 hard_link = '1',
5563 symbolic_link = '2',
......@@ -105,8 +113,9 @@ pub const Header = struct {
105113 }
106114
107115 pub fn fileType(header: Header) FileType {
108 const result = @as(FileType, @enumFromInt(header.bytes[156]));
109 return if (result == @as(FileType, @enumFromInt(0))) .normal else result;
116 const result: FileType = @enumFromInt(header.bytes[156]);
117 if (result == .normal_alias) return .normal;
118 return result;
110119 }
111120
112121 fn str(header: Header, start: usize, end: usize) []const u8 {
......@@ -268,18 +277,21 @@ pub fn pipeToFileSystem(dir: std.fs.Dir, reader: anytype, options: Options) !voi
268277 const link_name = header.linkName();
269278
270279 dir.symLink(link_name, file_name, .{}) catch |err| {
271 if (options.diagnostics) |d| {
272 try d.errors.append(d.allocator, .{ .unable_to_create_sym_link = .{
273 .code = err,
274 .file_name = try d.allocator.dupe(u8, file_name),
275 .link_name = try d.allocator.dupe(u8, link_name),
276 } });
277 } else {
278 return error.UnableToCreateSymLink;
279 }
280 const d = options.diagnostics orelse return error.UnableToCreateSymLink;
281 try d.errors.append(d.allocator, .{ .unable_to_create_sym_link = .{
282 .code = err,
283 .file_name = try d.allocator.dupe(u8, file_name),
284 .link_name = try d.allocator.dupe(u8, link_name),
285 } });
280286 };
281287 },
282 else => return error.TarUnsupportedFileType,
288 else => |file_type| {
289 const d = options.diagnostics orelse return error.TarUnsupportedFileType;
290 try d.errors.append(d.allocator, .{ .unsupported_file_type = .{
291 .file_name = try d.allocator.dupe(u8, unstripped_file_name),
292 .file_type = file_type,
293 } });
294 },
283295 }
284296 }
285297}
lib/std/zig/ErrorBundle.zig+12-12
......@@ -202,7 +202,7 @@ fn renderErrorMessageToWriter(
202202 try counting_stderr.writeAll(": ");
203203 // This is the length of the part before the error message:
204204 // e.g. "file.zig:4:5: error: "
205 const prefix_len = @as(usize, @intCast(counting_stderr.context.bytes_written));
205 const prefix_len: usize = @intCast(counting_stderr.context.bytes_written);
206206 try ttyconf.setColor(stderr, .reset);
207207 try ttyconf.setColor(stderr, .bold);
208208 if (err_msg.count == 1) {
......@@ -356,7 +356,7 @@ pub const Wip = struct {
356356 }
357357
358358 const compile_log_str_index = if (compile_log_text.len == 0) 0 else str: {
359 const str = @as(u32, @intCast(wip.string_bytes.items.len));
359 const str: u32 = @intCast(wip.string_bytes.items.len);
360360 try wip.string_bytes.ensureUnusedCapacity(gpa, compile_log_text.len + 1);
361361 wip.string_bytes.appendSliceAssumeCapacity(compile_log_text);
362362 wip.string_bytes.appendAssumeCapacity(0);
......@@ -364,8 +364,8 @@ pub const Wip = struct {
364364 };
365365
366366 wip.setExtra(0, ErrorMessageList{
367 .len = @as(u32, @intCast(wip.root_list.items.len)),
368 .start = @as(u32, @intCast(wip.extra.items.len)),
367 .len = @intCast(wip.root_list.items.len),
368 .start = @intCast(wip.extra.items.len),
369369 .compile_log_text = compile_log_str_index,
370370 });
371371 try wip.extra.appendSlice(gpa, @as([]const u32, @ptrCast(wip.root_list.items)));
......@@ -385,7 +385,7 @@ pub const Wip = struct {
385385
386386 pub fn addString(wip: *Wip, s: []const u8) !u32 {
387387 const gpa = wip.gpa;
388 const index = @as(u32, @intCast(wip.string_bytes.items.len));
388 const index: u32 = @intCast(wip.string_bytes.items.len);
389389 try wip.string_bytes.ensureUnusedCapacity(gpa, s.len + 1);
390390 wip.string_bytes.appendSliceAssumeCapacity(s);
391391 wip.string_bytes.appendAssumeCapacity(0);
......@@ -394,7 +394,7 @@ pub const Wip = struct {
394394
395395 pub fn printString(wip: *Wip, comptime fmt: []const u8, args: anytype) !u32 {
396396 const gpa = wip.gpa;
397 const index = @as(u32, @intCast(wip.string_bytes.items.len));
397 const index: u32 = @intCast(wip.string_bytes.items.len);
398398 try wip.string_bytes.writer(gpa).print(fmt, args);
399399 try wip.string_bytes.append(gpa, 0);
400400 return index;
......@@ -406,15 +406,15 @@ pub const Wip = struct {
406406 }
407407
408408 pub fn addErrorMessage(wip: *Wip, em: ErrorMessage) !MessageIndex {
409 return @as(MessageIndex, @enumFromInt(try addExtra(wip, em)));
409 return @enumFromInt(try addExtra(wip, em));
410410 }
411411
412412 pub fn addErrorMessageAssumeCapacity(wip: *Wip, em: ErrorMessage) MessageIndex {
413 return @as(MessageIndex, @enumFromInt(addExtraAssumeCapacity(wip, em)));
413 return @enumFromInt(addExtraAssumeCapacity(wip, em));
414414 }
415415
416416 pub fn addSourceLocation(wip: *Wip, sl: SourceLocation) !SourceLocationIndex {
417 return @as(SourceLocationIndex, @enumFromInt(try addExtra(wip, sl)));
417 return @enumFromInt(try addExtra(wip, sl));
418418 }
419419
420420 pub fn addReferenceTrace(wip: *Wip, rt: ReferenceTrace) !void {
......@@ -430,7 +430,7 @@ pub const Wip = struct {
430430 const other_list = other.getMessages();
431431
432432 // The ensureUnusedCapacity call above guarantees this.
433 const notes_start = wip.reserveNotes(@as(u32, @intCast(other_list.len))) catch unreachable;
433 const notes_start = wip.reserveNotes(@intCast(other_list.len)) catch unreachable;
434434 for (notes_start.., other_list) |note, message| {
435435 wip.extra.items[note] = @intFromEnum(wip.addOtherMessage(other, message) catch unreachable);
436436 }
......@@ -455,7 +455,7 @@ pub const Wip = struct {
455455 try wip.extra.ensureUnusedCapacity(wip.gpa, notes_len +
456456 notes_len * @typeInfo(ErrorBundle.ErrorMessage).Struct.fields.len);
457457 wip.extra.items.len += notes_len;
458 return @as(u32, @intCast(wip.extra.items.len - notes_len));
458 return @intCast(wip.extra.items.len - notes_len);
459459 }
460460
461461 fn addOtherMessage(wip: *Wip, other: ErrorBundle, msg_index: MessageIndex) !MessageIndex {
......@@ -510,7 +510,7 @@ pub const Wip = struct {
510510
511511 fn addExtraAssumeCapacity(wip: *Wip, extra: anytype) u32 {
512512 const fields = @typeInfo(@TypeOf(extra)).Struct.fields;
513 const result = @as(u32, @intCast(wip.extra.items.len));
513 const result: u32 = @intCast(wip.extra.items.len);
514514 wip.extra.items.len += fields.len;
515515 setExtra(wip, result, extra);
516516 return result;
src/Package.zig+92-27
......@@ -285,7 +285,8 @@ pub fn fetchAndAddDependencies(
285285 if (manifest.errors.len > 0) {
286286 const file_path = try directory.join(arena, &.{Manifest.basename});
287287 for (manifest.errors) |msg| {
288 try Report.addErrorMessage(ast, file_path, error_bundle, 0, msg);
288 const str = try error_bundle.addString(msg.msg);
289 try Report.addErrorMessage(&ast, file_path, error_bundle, 0, str, msg.tok, msg.off);
289290 }
290291 return error.PackageFetchFailed;
291292 }
......@@ -465,20 +466,31 @@ pub const Report = struct {
465466 comptime fmt_string: []const u8,
466467 fmt_args: anytype,
467468 ) error{ PackageFetchFailed, OutOfMemory } {
468 const ast = report.ast orelse main.fatal(fmt_string, fmt_args);
469 const msg = try report.error_bundle.printString(fmt_string, fmt_args);
470 return failMsg(report, tok, msg);
471 }
472
473 fn failMsg(
474 report: Report,
475 tok: std.zig.Ast.TokenIndex,
476 msg: u32,
477 ) error{ PackageFetchFailed, OutOfMemory } {
469478 const gpa = report.error_bundle.gpa;
470479
471480 const file_path = try report.directory.join(gpa, &.{Manifest.basename});
472481 defer gpa.free(file_path);
473482
474 const msg = try std.fmt.allocPrint(gpa, fmt_string, fmt_args);
475 defer gpa.free(msg);
483 const eb = report.error_bundle;
476484
477 try addErrorMessage(ast.*, file_path, report.error_bundle, 0, .{
478 .tok = tok,
479 .off = 0,
480 .msg = msg,
481 });
485 if (report.ast) |ast| {
486 try addErrorMessage(ast, file_path, eb, 0, msg, tok, 0);
487 } else {
488 try eb.addRootErrorMessage(.{
489 .msg = msg,
490 .src_loc = .none,
491 .notes_len = 0,
492 });
493 }
482494
483495 return error.PackageFetchFailed;
484496 }
......@@ -488,31 +500,42 @@ pub const Report = struct {
488500 notes_len: u32,
489501 msg: Manifest.ErrorMessage,
490502 ) error{OutOfMemory}!void {
491 const ast = report.ast orelse main.fatal("{s}", .{msg.msg});
492 const gpa = report.error_bundle.gpa;
493 const file_path = try report.directory.join(gpa, &.{Manifest.basename});
494 defer gpa.free(file_path);
495 return addErrorMessage(ast.*, file_path, report.error_bundle, notes_len, msg);
503 const eb = report.error_bundle;
504 const msg_str = try eb.addString(msg.msg);
505 if (report.ast) |ast| {
506 const gpa = eb.gpa;
507 const file_path = try report.directory.join(gpa, &.{Manifest.basename});
508 defer gpa.free(file_path);
509 return addErrorMessage(ast, file_path, eb, notes_len, msg_str, msg.tok, msg.off);
510 } else {
511 return eb.addRootErrorMessage(.{
512 .msg = msg_str,
513 .src_loc = .none,
514 .notes_len = notes_len,
515 });
516 }
496517 }
497518
498519 fn addErrorMessage(
499 ast: std.zig.Ast,
520 ast: *const std.zig.Ast,
500521 file_path: []const u8,
501522 eb: *std.zig.ErrorBundle.Wip,
502523 notes_len: u32,
503 msg: Manifest.ErrorMessage,
524 msg_str: u32,
525 msg_tok: std.zig.Ast.TokenIndex,
526 msg_off: u32,
504527 ) error{OutOfMemory}!void {
505528 const token_starts = ast.tokens.items(.start);
506 const start_loc = ast.tokenLocation(0, msg.tok);
529 const start_loc = ast.tokenLocation(0, msg_tok);
507530
508531 try eb.addRootErrorMessage(.{
509 .msg = try eb.addString(msg.msg),
532 .msg = msg_str,
510533 .src_loc = try eb.addSourceLocation(.{
511534 .src_path = try eb.addString(file_path),
512 .span_start = token_starts[msg.tok],
513 .span_end = @as(u32, @intCast(token_starts[msg.tok] + ast.tokenSlice(msg.tok).len)),
514 .span_main = token_starts[msg.tok] + msg.off,
515 .line = @as(u32, @intCast(start_loc.line)),
535 .span_start = token_starts[msg_tok],
536 .span_end = @as(u32, @intCast(token_starts[msg_tok] + ast.tokenSlice(msg_tok).len)),
537 .span_main = token_starts[msg_tok] + msg_off,
538 .line = @intCast(start_loc.line),
516539 .column = @as(u32, @intCast(start_loc.column)),
517540 .source_line = try eb.addString(ast.source[start_loc.line_start..start_loc.line_end]),
518541 }),
......@@ -752,9 +775,9 @@ pub const ReadableResource = struct {
752775 };
753776
754777 switch (try rr.getFileType(dep_location_tok, report)) {
755 .tar => try unpackTarball(prog_reader.reader(), tmp_directory.handle),
756 .@"tar.gz" => try unpackTarballCompressed(allocator, prog_reader, tmp_directory.handle, std.compress.gzip),
757 .@"tar.xz" => try unpackTarballCompressed(allocator, prog_reader, tmp_directory.handle, std.compress.xz),
778 .tar => try unpackTarball(allocator, prog_reader.reader(), tmp_directory.handle, dep_location_tok, report),
779 .@"tar.gz" => try unpackTarballCompressed(allocator, prog_reader, tmp_directory.handle, dep_location_tok, report, std.compress.gzip),
780 .@"tar.xz" => try unpackTarballCompressed(allocator, prog_reader, tmp_directory.handle, dep_location_tok, report, std.compress.xz),
758781 .git_pack => try unpackGitPack(allocator, &prog_reader, git.parseOid(rr.path) catch unreachable, tmp_directory.handle),
759782 }
760783 } else {
......@@ -1128,6 +1151,8 @@ fn unpackTarballCompressed(
11281151 gpa: Allocator,
11291152 reader: anytype,
11301153 out_dir: fs.Dir,
1154 dep_location_tok: std.zig.Ast.TokenIndex,
1155 report: Report,
11311156 comptime Compression: type,
11321157) !void {
11331158 var br = std.io.bufferedReaderSize(std.crypto.tls.max_ciphertext_record_len, reader);
......@@ -1135,11 +1160,21 @@ fn unpackTarballCompressed(
11351160 var decompress = try Compression.decompress(gpa, br.reader());
11361161 defer decompress.deinit();
11371162
1138 return unpackTarball(decompress.reader(), out_dir);
1163 return unpackTarball(gpa, decompress.reader(), out_dir, dep_location_tok, report);
11391164}
11401165
1141fn unpackTarball(reader: anytype, out_dir: fs.Dir) !void {
1166fn unpackTarball(
1167 gpa: Allocator,
1168 reader: anytype,
1169 out_dir: fs.Dir,
1170 dep_location_tok: std.zig.Ast.TokenIndex,
1171 report: Report,
1172) !void {
1173 var diagnostics: std.tar.Options.Diagnostics = .{ .allocator = gpa };
1174 defer diagnostics.deinit();
1175
11421176 try std.tar.pipeToFileSystem(out_dir, reader, .{
1177 .diagnostics = &diagnostics,
11431178 .strip_components = 1,
11441179 // TODO: we would like to set this to executable_bit_only, but two
11451180 // things need to happen before that:
......@@ -1148,6 +1183,36 @@ fn unpackTarball(reader: anytype, out_dir: fs.Dir) !void {
11481183 // bit on Windows from the ACLs (see the isExecutable function).
11491184 .mode_mode = .ignore,
11501185 });
1186
1187 if (diagnostics.errors.items.len > 0) {
1188 const notes_len: u32 = @intCast(diagnostics.errors.items.len);
1189 try report.addErrorWithNotes(notes_len, .{
1190 .tok = dep_location_tok,
1191 .off = 0,
1192 .msg = "unable to unpack tarball",
1193 });
1194 const eb = report.error_bundle;
1195 const notes_start = try eb.reserveNotes(notes_len);
1196 for (diagnostics.errors.items, notes_start..) |item, note_i| {
1197 switch (item) {
1198 .unable_to_create_sym_link => |info| {
1199 eb.extra.items[note_i] = @intFromEnum(try eb.addErrorMessage(.{
1200 .msg = try eb.printString("unable to create symlink from '{s}' to '{s}': {s}", .{
1201 info.file_name, info.link_name, @errorName(info.code),
1202 }),
1203 }));
1204 },
1205 .unsupported_file_type => |info| {
1206 eb.extra.items[note_i] = @intFromEnum(try eb.addErrorMessage(.{
1207 .msg = try eb.printString("file '{s}' has unsupported type '{c}'", .{
1208 info.file_name, @intFromEnum(info.file_type),
1209 }),
1210 }));
1211 },
1212 }
1213 }
1214 return error.InvalidTarball;
1215 }
11511216}
11521217
11531218fn unpackGitPack(
src/main.zig+20-4
......@@ -6610,6 +6610,7 @@ fn cmdFetch(
66106610 arena: Allocator,
66116611 args: []const []const u8,
66126612) !void {
6613 const color: Color = .auto;
66136614 var opt_url: ?[]const u8 = null;
66146615 var override_global_cache_dir: ?[]const u8 = try optionalStringEnvVar(arena, "ZIG_GLOBAL_CACHE_DIR");
66156616
......@@ -6651,10 +6652,17 @@ fn cmdFetch(
66516652 const root_prog_node = progress.start("Fetch", 0);
66526653 defer root_prog_node.end();
66536654
6655 var wip_errors: std.zig.ErrorBundle.Wip = undefined;
6656 try wip_errors.init(gpa);
6657 defer wip_errors.deinit();
6658
66546659 var report: Package.Report = .{
66556660 .ast = null,
6656 .directory = undefined,
6657 .error_bundle = undefined,
6661 .directory = .{
6662 .handle = fs.cwd(),
6663 .path = null,
6664 },
6665 .error_bundle = &wip_errors,
66586666 };
66596667
66606668 var global_cache_directory: Compilation.Directory = l: {
......@@ -6697,14 +6705,22 @@ fn cmdFetch(
66976705 };
66986706 defer readable_resource.deinit(gpa);
66996707
6700 var package_location = try readable_resource.unpack(
6708 var package_location = readable_resource.unpack(
67016709 gpa,
67026710 &thread_pool,
67036711 global_cache_directory,
67046712 0,
67056713 report,
67066714 root_prog_node,
6707 );
6715 ) catch |err| {
6716 if (wip_errors.root_list.items.len > 0) {
6717 var errors = try wip_errors.toOwnedBundle("");
6718 defer errors.deinit(gpa);
6719 errors.renderToStdErr(renderOptions(color));
6720 process.exit(1);
6721 }
6722 fatal("unable to unpack '{s}': {s}", .{ url, @errorName(err) });
6723 };
67086724 defer package_location.deinit(gpa);
67096725
67106726 const hex_digest = Package.Manifest.hexDigest(package_location.hash);