| author | |
| committer | |
| log | fcf8d5ada28c02722575cc78d41171692a227061 |
| tree | 038d8ee507ce30836196db0bd951e3b18b8fcc2e |
| parent | 7ef345f342534f93a7e61d9ecd096523f048871d |
| signature |
Also modifies all incremental cases using `#expect_error` to include the
errors and notes which are expected.13 files changed, 162 insertions(+), 35 deletions(-)
test/incremental/add_decl+1-1| ... | ... | @@ -39,7 +39,7 @@ pub fn main() !void { |
| 39 | 39 | } |
| 40 | 40 | const foo = "good morning\n"; |
| 41 | 41 | const bar = "good evening\n"; |
| 42 | #expect_error=ignored | |
| 42 | #expect_error=main.zig:3:37: error: use of undeclared identifier 'qux' | |
| 43 | 43 | |
| 44 | 44 | #update=add missing declaration |
| 45 | 45 | #file=main.zig |
test/incremental/add_decl_namespaced+2-1| ... | ... | @@ -39,7 +39,8 @@ pub fn main() !void { |
| 39 | 39 | } |
| 40 | 40 | const foo = "good morning\n"; |
| 41 | 41 | const bar = "good evening\n"; |
| 42 | #expect_error=ignored | |
| 42 | #expect_error=main.zig:3:44: error: root source file struct 'main' has no member named 'qux' | |
| 43 | #expect_error=main.zig:1:1: note: struct declared here | |
| 43 | 44 | |
| 44 | 45 | #update=add missing declaration |
| 45 | 46 | #file=main.zig |
test/incremental/change_embed_file+2-2| ... | ... | @@ -20,7 +20,7 @@ Hello again, World! |
| 20 | 20 | |
| 21 | 21 | #update=delete file |
| 22 | 22 | #rm_file=string.txt |
| 23 | #expect_error=ignored | |
| 23 | #expect_error=main.zig:2:27: error: unable to open 'string.txt': FileNotFound | |
| 24 | 24 | |
| 25 | 25 | #update=remove reference to file |
| 26 | 26 | #file=main.zig |
| ... | ... | @@ -38,7 +38,7 @@ const string = @embedFile("string.txt"); |
| 38 | 38 | pub fn main() !void { |
| 39 | 39 | try std.io.getStdOut().writeAll(string); |
| 40 | 40 | } |
| 41 | #expect_error=ignore | |
| 41 | #expect_error=main.zig:2:27: error: unable to open 'string.txt': FileNotFound | |
| 42 | 42 | |
| 43 | 43 | #update=recreate file |
| 44 | 44 | #file=string.txt |
test/incremental/change_enum_tag_type+1-1| ... | ... | @@ -39,7 +39,7 @@ comptime { |
| 39 | 39 | std.debug.assert(@TypeOf(@intFromEnum(Foo.e)) == Tag); |
| 40 | 40 | } |
| 41 | 41 | const std = @import("std"); |
| 42 | #expect_error=ignored | |
| 42 | #expect_error=main.zig:7:5: error: enumeration value '4' too large for type 'u2' | |
| 43 | 43 | #update=increase tag size |
| 44 | 44 | #file=main.zig |
| 45 | 45 | const Tag = u3; |
test/incremental/compile_error_then_log+5-2| ... | ... | @@ -4,19 +4,22 @@ |
| 4 | 4 | #target=wasm32-wasi-selfhosted |
| 5 | 5 | #update=initial version with compile error |
| 6 | 6 | #file=main.zig |
| 7 | pub fn main() void {} | |
| 7 | 8 | comptime { |
| 8 | 9 | @compileError("this is an error"); |
| 9 | 10 | } |
| 10 | 11 | comptime { |
| 11 | 12 | @compileLog("this is a log"); |
| 12 | 13 | } |
| 13 | #expect_error=ignored | |
| 14 | #expect_error=main.zig:3:5: error: this is an error | |
| 15 | ||
| 14 | 16 | #update=remove the compile error |
| 15 | 17 | #file=main.zig |
| 18 | pub fn main() void {} | |
| 16 | 19 | comptime { |
| 17 | 20 | //@compileError("this is an error"); |
| 18 | 21 | } |
| 19 | 22 | comptime { |
| 20 | 23 | @compileLog("this is a log"); |
| 21 | 24 | } |
| 22 | #expect_error=ignored | |
| 25 | #expect_error=main.zig:6:5: error: found compile log statement |
test/incremental/delete_comptime_decls+5-2| ... | ... | @@ -26,7 +26,10 @@ comptime { |
| 26 | 26 | const slice = array[3..2]; |
| 27 | 27 | _ = slice; |
| 28 | 28 | } |
| 29 | #expect_error=ignored | |
| 29 | #expect_error=main.zig:5:32: error: end index 6 out of bounds for slice of length 4 +1 (sentinel) | |
| 30 | #expect_error=main.zig:10:28: error: end index 6 out of bounds for array of length 4 +1 (sentinel) | |
| 31 | #expect_error=main.zig:15:28: error: end index 5 out of bounds for array of length 4 | |
| 32 | #expect_error=main.zig:20:25: error: start index 3 is larger than end index 2 | |
| 30 | 33 | |
| 31 | 34 | #update=delete and modify comptime decls |
| 32 | 35 | #file=main.zig |
| ... | ... | @@ -38,4 +41,4 @@ comptime { |
| 38 | 41 | const y = x[0..runtime_len]; |
| 39 | 42 | _ = y; |
| 40 | 43 | } |
| 41 | #expect_error=ignored | |
| 44 | #expect_error=main.zig:6:16: error: slice of null pointer |
test/incremental/fix_astgen_failure+2-2| ... | ... | @@ -11,7 +11,7 @@ pub fn main() !void { |
| 11 | 11 | pub fn hello() !void { |
| 12 | 12 | try std.io.getStdOut().writeAll("Hello, World!\n"); |
| 13 | 13 | } |
| 14 | #expect_error=ignored | |
| 14 | #expect_error=foo.zig:2:9: error: use of undeclared identifier 'std' | |
| 15 | 15 | #update=fix the error |
| 16 | 16 | #file=foo.zig |
| 17 | 17 | const std = @import("std"); |
| ... | ... | @@ -25,7 +25,7 @@ const std = @import("std"); |
| 25 | 25 | pub fn hello() !void { |
| 26 | 26 | try std.io.getStdOut().writeAll(hello_str); |
| 27 | 27 | } |
| 28 | #expect_error=ignored | |
| 28 | #expect_error=foo.zig:3:37: error: use of undeclared identifier 'hello_str' | |
| 29 | 29 | #update=fix the new error |
| 30 | 30 | #file=foo.zig |
| 31 | 31 | const std = @import("std"); |
test/incremental/fix_many_errors+20-1| ... | ... | @@ -24,7 +24,26 @@ export fn f6() void { @compileError("f6"); } |
| 24 | 24 | export fn f7() void { @compileError("f7"); } |
| 25 | 25 | export fn f8() void { @compileError("f8"); } |
| 26 | 26 | export fn f9() void { @compileError("f9"); } |
| 27 | #expect_error=ignored | |
| 27 | #expect_error=main.zig:2:12: error: c0 | |
| 28 | #expect_error=main.zig:3:12: error: c1 | |
| 29 | #expect_error=main.zig:4:12: error: c2 | |
| 30 | #expect_error=main.zig:5:12: error: c3 | |
| 31 | #expect_error=main.zig:6:12: error: c4 | |
| 32 | #expect_error=main.zig:7:12: error: c5 | |
| 33 | #expect_error=main.zig:8:12: error: c6 | |
| 34 | #expect_error=main.zig:9:12: error: c7 | |
| 35 | #expect_error=main.zig:10:12: error: c8 | |
| 36 | #expect_error=main.zig:11:12: error: c9 | |
| 37 | #expect_error=main.zig:12:23: error: f0 | |
| 38 | #expect_error=main.zig:13:23: error: f1 | |
| 39 | #expect_error=main.zig:14:23: error: f2 | |
| 40 | #expect_error=main.zig:15:23: error: f3 | |
| 41 | #expect_error=main.zig:16:23: error: f4 | |
| 42 | #expect_error=main.zig:17:23: error: f5 | |
| 43 | #expect_error=main.zig:18:23: error: f6 | |
| 44 | #expect_error=main.zig:19:23: error: f7 | |
| 45 | #expect_error=main.zig:20:23: error: f8 | |
| 46 | #expect_error=main.zig:21:23: error: f9 | |
| 28 | 47 | #update=fix all the errors |
| 29 | 48 | #file=main.zig |
| 30 | 49 | pub fn main() !void {} |
test/incremental/remove_enum_field+2-1| ... | ... | @@ -23,4 +23,5 @@ pub fn main() !void { |
| 23 | 23 | try std.io.getStdOut().writer().print("{}\n", .{@intFromEnum(MyEnum.foo)}); |
| 24 | 24 | } |
| 25 | 25 | const std = @import("std"); |
| 26 | #expect_error=ignored | |
| 26 | #expect_error=main.zig:6:73: error: enum 'main.MyEnum' has no member named 'foo' | |
| 27 | #expect_error=main.zig:1:16: note: enum declared here |
test/incremental/remove_invalid_union_backing_enum+2-1| ... | ... | @@ -15,7 +15,8 @@ pub fn main() void { |
| 15 | 15 | const u: U = .{ .a = 123 }; |
| 16 | 16 | _ = u; |
| 17 | 17 | } |
| 18 | #expect_error=ignored | |
| 18 | #expect_error=main.zig:6:5: error: no field named 'd' in enum 'main.E' | |
| 19 | #expect_error=main.zig:1:11: note: enum declared here | |
| 19 | 20 | #update=remove invalid backing enum |
| 20 | 21 | #file=main.zig |
| 21 | 22 | const U = union { |
test/incremental/temporary_parse_error+1-1| ... | ... | @@ -11,7 +11,7 @@ pub fn main() !void {} |
| 11 | 11 | #update=introduce parse error |
| 12 | 12 | #file=main.zig |
| 13 | 13 | pub fn main() !void { |
| 14 | #expect_error=ignored | |
| 14 | #expect_error=main.zig:2:1: error: expected statement, found 'EOF' | |
| 15 | 15 | |
| 16 | 16 | #update=fix parse error |
| 17 | 17 | #file=main.zig |
test/incremental/unreferenced_error+1-1| ... | ... | @@ -18,7 +18,7 @@ pub fn main() !void { |
| 18 | 18 | try std.io.getStdOut().writeAll(a); |
| 19 | 19 | } |
| 20 | 20 | const a = @compileError("bad a"); |
| 21 | #expect_error=ignored | |
| 21 | #expect_error=main.zig:5:11: error: bad a | |
| 22 | 22 | |
| 23 | 23 | #update=remove error reference |
| 24 | 24 | #file=main.zig |
tools/incr-check.zig+118-19| ... | ... | @@ -340,19 +340,63 @@ const Eval = struct { |
| 340 | 340 | } |
| 341 | 341 | |
| 342 | 342 | fn checkErrorOutcome(eval: *Eval, update: Case.Update, error_bundle: std.zig.ErrorBundle) !void { |
| 343 | switch (update.outcome) { | |
| 343 | const expected_errors = switch (update.outcome) { | |
| 344 | 344 | .unknown => return, |
| 345 | .compile_errors => |expected_errors| { | |
| 346 | for (expected_errors) |expected_error| { | |
| 347 | _ = expected_error; | |
| 348 | @panic("TODO check if the expected error matches the compile errors"); | |
| 349 | } | |
| 350 | }, | |
| 345 | .compile_errors => |expected_errors| expected_errors, | |
| 351 | 346 | .stdout, .exit_code => { |
| 352 | 347 | const color: std.zig.Color = .auto; |
| 353 | 348 | error_bundle.renderToStdErr(color.renderOptions()); |
| 354 | 349 | eval.fatal("update '{s}': unexpected compile errors", .{update.name}); |
| 355 | 350 | }, |
| 351 | }; | |
| 352 | ||
| 353 | var expected_idx: usize = 0; | |
| 354 | ||
| 355 | for (error_bundle.getMessages()) |err_idx| { | |
| 356 | if (expected_idx == expected_errors.len) { | |
| 357 | const color: std.zig.Color = .auto; | |
| 358 | error_bundle.renderToStdErr(color.renderOptions()); | |
| 359 | eval.fatal("update '{s}': more errors than expected", .{update.name}); | |
| 360 | } | |
| 361 | eval.checkOneError(update, error_bundle, expected_errors[expected_idx], false, err_idx); | |
| 362 | expected_idx += 1; | |
| 363 | ||
| 364 | for (error_bundle.getNotes(err_idx)) |note_idx| { | |
| 365 | if (expected_idx == expected_errors.len) { | |
| 366 | const color: std.zig.Color = .auto; | |
| 367 | error_bundle.renderToStdErr(color.renderOptions()); | |
| 368 | eval.fatal("update '{s}': more error notes than expected", .{update.name}); | |
| 369 | } | |
| 370 | eval.checkOneError(update, error_bundle, expected_errors[expected_idx], true, note_idx); | |
| 371 | expected_idx += 1; | |
| 372 | } | |
| 373 | } | |
| 374 | } | |
| 375 | ||
| 376 | fn checkOneError( | |
| 377 | eval: *Eval, | |
| 378 | update: Case.Update, | |
| 379 | eb: std.zig.ErrorBundle, | |
| 380 | expected: Case.ExpectedError, | |
| 381 | is_note: bool, | |
| 382 | err_idx: std.zig.ErrorBundle.MessageIndex, | |
| 383 | ) void { | |
| 384 | const err = eb.getErrorMessage(err_idx); | |
| 385 | if (err.src_loc == .none) @panic("TODO error message with no source location"); | |
| 386 | if (err.count != 1) @panic("TODO error message with count>1"); | |
| 387 | const msg = eb.nullTerminatedString(err.msg); | |
| 388 | const src = eb.getSourceLocation(err.src_loc); | |
| 389 | const filename = eb.nullTerminatedString(src.src_path); | |
| 390 | ||
| 391 | if (expected.is_note != is_note or | |
| 392 | !std.mem.eql(u8, expected.filename, filename) or | |
| 393 | expected.line != src.line + 1 or | |
| 394 | expected.column != src.column + 1 or | |
| 395 | !std.mem.eql(u8, expected.msg, msg)) | |
| 396 | { | |
| 397 | const color: std.zig.Color = .auto; | |
| 398 | eb.renderToStdErr(color.renderOptions()); | |
| 399 | eval.fatal("update '{s}': compile error did not match expected error", .{update.name}); | |
| 356 | 400 | } |
| 357 | 401 | } |
| 358 | 402 | |
| ... | ... | @@ -595,11 +639,11 @@ const Case = struct { |
| 595 | 639 | }; |
| 596 | 640 | |
| 597 | 641 | const ExpectedError = struct { |
| 598 | file_name: ?[]const u8 = null, | |
| 599 | line: ?u32 = null, | |
| 600 | column: ?u32 = null, | |
| 601 | msg_exact: ?[]const u8 = null, | |
| 602 | msg_substring: ?[]const u8 = null, | |
| 642 | is_note: bool, | |
| 643 | filename: []const u8, | |
| 644 | line: u32, | |
| 645 | column: u32, | |
| 646 | msg: []const u8, | |
| 603 | 647 | }; |
| 604 | 648 | |
| 605 | 649 | fn parse(arena: Allocator, bytes: []const u8) !Case { |
| ... | ... | @@ -665,13 +709,11 @@ const Case = struct { |
| 665 | 709 | var src: std.ArrayListUnmanaged(u8) = .empty; |
| 666 | 710 | |
| 667 | 711 | while (true) { |
| 668 | const old = it; | |
| 669 | const next_line_raw = it.next() orelse fatal("line {d}: unexpected EOF", .{line_n}); | |
| 712 | const next_line_raw = it.peek() orelse fatal("line {d}: unexpected EOF", .{line_n}); | |
| 670 | 713 | const next_line = std.mem.trimRight(u8, next_line_raw, "\r"); |
| 671 | if (std.mem.startsWith(u8, next_line, "#")) { | |
| 672 | it = old; | |
| 673 | break; | |
| 674 | } | |
| 714 | if (std.mem.startsWith(u8, next_line, "#")) break; | |
| 715 | ||
| 716 | _ = it.next(); | |
| 675 | 717 | line_n += 1; |
| 676 | 718 | |
| 677 | 719 | try src.ensureUnusedCapacity(arena, next_line.len + 1); |
| ... | ... | @@ -699,7 +741,24 @@ const Case = struct { |
| 699 | 741 | if (updates.items.len == 0) fatal("line {d}: expect directive before update", .{line_n}); |
| 700 | 742 | const last_update = &updates.items[updates.items.len - 1]; |
| 701 | 743 | if (last_update.outcome != .unknown) fatal("line {d}: conflicting expect directive", .{line_n}); |
| 702 | last_update.outcome = .{ .compile_errors = &.{} }; | |
| 744 | ||
| 745 | var errors: std.ArrayListUnmanaged(ExpectedError) = .empty; | |
| 746 | try errors.append(arena, parseExpectedError(val, line_n)); | |
| 747 | while (true) { | |
| 748 | const next_line = it.peek() orelse break; | |
| 749 | if (!std.mem.startsWith(u8, next_line, "#")) break; | |
| 750 | var new_line_it = std.mem.splitScalar(u8, next_line, '='); | |
| 751 | const new_key = new_line_it.first()[1..]; | |
| 752 | const new_val = std.mem.trimRight(u8, new_line_it.rest(), "\r"); | |
| 753 | if (new_val.len == 0) break; | |
| 754 | if (!std.mem.eql(u8, new_key, "expect_error")) break; | |
| 755 | ||
| 756 | _ = it.next(); | |
| 757 | line_n += 1; | |
| 758 | try errors.append(arena, parseExpectedError(new_val, line_n)); | |
| 759 | } | |
| 760 | ||
| 761 | last_update.outcome = .{ .compile_errors = errors.items }; | |
| 703 | 762 | } else { |
| 704 | 763 | fatal("line {d}: unrecognized key '{s}'", .{ line_n, key }); |
| 705 | 764 | } |
| ... | ... | @@ -749,3 +808,43 @@ fn waitChild(child: *std.process.Child, eval: *Eval) void { |
| 749 | 808 | .Signal, .Stopped, .Unknown => eval.fatal("compiler terminated unexpectedly", .{}), |
| 750 | 809 | } |
| 751 | 810 | } |
| 811 | ||
| 812 | fn parseExpectedError(str: []const u8, l: usize) Case.ExpectedError { | |
| 813 | // #expect_error=foo.zig:1:2: error: the error message | |
| 814 | // #expect_error=foo.zig:1:2: note: and a note | |
| 815 | ||
| 816 | const fatal = std.process.fatal; | |
| 817 | ||
| 818 | var it = std.mem.splitScalar(u8, str, ':'); | |
| 819 | const filename = it.first(); | |
| 820 | const line_str = it.next() orelse fatal("line {d}: incomplete error specification", .{l}); | |
| 821 | const column_str = it.next() orelse fatal("line {d}: incomplete error specification", .{l}); | |
| 822 | const error_or_note_str = std.mem.trim( | |
| 823 | u8, | |
| 824 | it.next() orelse fatal("line {d}: incomplete error specification", .{l}), | |
| 825 | " ", | |
| 826 | ); | |
| 827 | const message = std.mem.trim(u8, it.rest(), " "); | |
| 828 | if (filename.len == 0) fatal("line {d}: empty filename", .{l}); | |
| 829 | if (message.len == 0) fatal("line {d}: empty error message", .{l}); | |
| 830 | const is_note = if (std.mem.eql(u8, error_or_note_str, "error")) | |
| 831 | false | |
| 832 | else if (std.mem.eql(u8, error_or_note_str, "note")) | |
| 833 | true | |
| 834 | else | |
| 835 | fatal("line {d}: expeted 'error' or 'note', found '{s}'", .{ l, error_or_note_str }); | |
| 836 | ||
| 837 | const line = std.fmt.parseInt(u32, line_str, 10) catch | |
| 838 | fatal("line {d}: invalid line number '{s}'", .{ l, line_str }); | |
| 839 | ||
| 840 | const column = std.fmt.parseInt(u32, column_str, 10) catch | |
| 841 | fatal("line {d}: invalid column number '{s}'", .{ l, column_str }); | |
| 842 | ||
| 843 | return .{ | |
| 844 | .is_note = is_note, | |
| 845 | .filename = filename, | |
| 846 | .line = line, | |
| 847 | .column = column, | |
| 848 | .msg = message, | |
| 849 | }; | |
| 850 | } |