authorgravatar for ian@ianjohnson.devIan Johnson <ian@ianjohnson.dev> 2025-02-15 15:59:32-05:00
committergravatar for ian@ianjohnson.devIan Johnson <ian@ianjohnson.dev> 2025-02-15 17:29:31-05:00
log293603f04029e2b1edf235a80417f3bb9651482f
tree9bb0c7a86fc06915823ebcd7366a00231b95d2ba
parente5174c7441e42a1ecc4d31ed505301f6d2c26d50

Autodoc: report syntax errors to user

Additionally, this commit streamlines the way unparseable files are handled, by giving them the AST of an empty file. This avoids bugs in the rest of the Autodoc logic trying to work with invalid ASTs.

1 files changed, 27 insertions(+), 10 deletions(-)

lib/docs/wasm/Walk.zig+27-10
......@@ -406,15 +406,11 @@ pub const ModuleIndex = enum(u32) {
406406};
407407
408408pub fn add_file(file_name: []const u8, bytes: []u8) !File.Index {
409 const ast = try parse(bytes);
409 const ast = try parse(file_name, bytes);
410 assert(ast.errors.len == 0);
410411 const file_index: File.Index = @enumFromInt(files.entries.len);
411412 try files.put(gpa, file_name, .{ .ast = ast });
412413
413 if (ast.errors.len > 0) {
414 log.err("can't index '{s}' because it has syntax errors", .{file_index.path()});
415 return file_index;
416 }
417
418414 var w: Walk = .{
419415 .file = file_index,
420416 };
......@@ -434,20 +430,41 @@ pub fn add_file(file_name: []const u8, bytes: []u8) !File.Index {
434430 return file_index;
435431}
436432
437fn parse(source: []u8) Oom!Ast {
433/// Parses a file and returns its `Ast`. If the file cannot be parsed, returns
434/// the `Ast` of an empty file, so that the rest of the Autodoc logic does not
435/// need to handle parse errors.
436fn parse(file_name: []const u8, source: []u8) Oom!Ast {
438437 // Require every source file to end with a newline so that Zig's tokenizer
439438 // can continue to require null termination and Autodoc implementation can
440439 // avoid copying source bytes from the decompressed tar file buffer.
441440 const adjusted_source: [:0]const u8 = s: {
442441 if (source.len == 0)
443442 break :s "";
444
445 assert(source[source.len - 1] == '\n');
443 if (source[source.len - 1] != '\n') {
444 log.err("{s}: expected newline at end of file", .{file_name});
445 break :s "";
446 }
446447 source[source.len - 1] = 0;
447448 break :s source[0 .. source.len - 1 :0];
448449 };
449450
450 return Ast.parse(gpa, adjusted_source, .zig);
451 var ast = try Ast.parse(gpa, adjusted_source, .zig);
452 if (ast.errors.len > 0) {
453 defer ast.deinit(gpa);
454
455 const token_offsets = ast.tokens.items(.start);
456 var rendered_err: std.ArrayListUnmanaged(u8) = .{};
457 defer rendered_err.deinit(gpa);
458 for (ast.errors) |err| {
459 const err_offset = token_offsets[err.token] + ast.errorOffset(err);
460 const err_loc = std.zig.findLineColumn(ast.source, err_offset);
461 rendered_err.clearRetainingCapacity();
462 try ast.renderError(err, rendered_err.writer(gpa));
463 log.err("{s}:{}:{}: {s}", .{ file_name, err_loc.line + 1, err_loc.column + 1, rendered_err.items });
464 }
465 return Ast.parse(gpa, "", .zig);
466 }
467 return ast;
451468}
452469
453470pub const Scope = struct {