Commit 5103053977

Andrew Kelley <andrew@ziglang.org>
2021-07-02 22:26:10
compile errors test harness: support unknown file/line/column
This gets us 2 more passing compile error test cases.
1 parent c5c23db
Changed files (3)
lib/std/fmt.zig
@@ -362,8 +362,8 @@ pub fn format(
         const missing_count = arg_state.args_len - @popCount(ArgSetType, arg_state.used_args);
         switch (missing_count) {
             0 => unreachable,
-            1 => @compileError("Unused argument in \"" ++ fmt ++ "\""),
-            else => @compileError((comptime comptimePrint("{d}", .{missing_count})) ++ " unused arguments in \"" ++ fmt ++ "\""),
+            1 => @compileError("Unused argument in '" ++ fmt ++ "'"),
+            else => @compileError((comptime comptimePrint("{d}", .{missing_count})) ++ " unused arguments in '" ++ fmt ++ "'"),
         }
     }
 }
src/test.zig
@@ -37,7 +37,11 @@ const ErrorMsg = union(enum) {
     src: struct {
         src_path: []const u8,
         msg: []const u8,
+        // maxint means match anything
+        // this is a workaround for stage1 compiler bug I ran into when making it ?u32
         line: u32,
+        // maxint means match anything
+        // this is a workaround for stage1 compiler bug I ran into when making it ?u32
         column: u32,
         kind: Kind,
     },
@@ -81,13 +85,23 @@ const ErrorMsg = union(enum) {
         _ = options;
         switch (self) {
             .src => |src| {
-                return writer.print("{s}:{d}:{d}: {s}: {s}", .{
-                    src.src_path,
-                    src.line + 1,
-                    src.column + 1,
-                    @tagName(src.kind),
-                    src.msg,
-                });
+                if (!std.mem.eql(u8, src.src_path, "?") or
+                    src.line != std.math.maxInt(u32) or
+                    src.column != std.math.maxInt(u32))
+                {
+                    try writer.print("{s}:", .{src.src_path});
+                    if (src.line != std.math.maxInt(u32)) {
+                        try writer.print("{d}:", .{src.line + 1});
+                    } else {
+                        try writer.writeAll("?:");
+                    }
+                    if (src.column != std.math.maxInt(u32)) {
+                        try writer.print("{d}: ", .{src.column + 1});
+                    } else {
+                        try writer.writeAll("?: ");
+                    }
+                }
+                return writer.print("{s}: {s}", .{ @tagName(src.kind), src.msg });
             },
             .plain => |plain| {
                 return writer.print("{s}: {s}", .{ @tagName(plain.kind), plain.msg });
@@ -220,8 +234,14 @@ pub const TestContext = struct {
                 const kind_text = it.next() orelse @panic("missing 'error'/'note'");
                 const msg = it.rest()[1..]; // skip over the space at end of "error: "
 
-                const line = std.fmt.parseInt(u32, line_text, 10) catch @panic("bad line number");
-                const column = std.fmt.parseInt(u32, col_text, 10) catch @panic("bad column number");
+                const line: ?u32 = if (std.mem.eql(u8, line_text, "?"))
+                    null
+                else
+                    std.fmt.parseInt(u32, line_text, 10) catch @panic("bad line number");
+                const column: ?u32 = if (std.mem.eql(u8, line_text, "?"))
+                    null
+                else
+                    std.fmt.parseInt(u32, col_text, 10) catch @panic("bad column number");
                 const kind: ErrorMsg.Kind = if (std.mem.eql(u8, kind_text, " error"))
                     .@"error"
                 else if (std.mem.eql(u8, kind_text, " note"))
@@ -229,16 +249,28 @@ pub const TestContext = struct {
                 else
                     @panic("expected 'error'/'note'");
 
-                if (line == 0 or column == 0) {
-                    @panic("line and column must be specified starting at one");
-                }
+                const line_0based: u32 = if (line) |n| blk: {
+                    if (n == 0) {
+                        print("{s}: line must be specified starting at one\n", .{self.name});
+                        return;
+                    }
+                    break :blk n - 1;
+                } else std.math.maxInt(u32);
+
+                const column_0based: u32 = if (column) |n| blk: {
+                    if (n == 0) {
+                        print("{s}: line must be specified starting at one\n", .{self.name});
+                        return;
+                    }
+                    break :blk n - 1;
+                } else std.math.maxInt(u32);
 
                 array[i] = .{
                     .src = .{
                         .src_path = src_path,
                         .msg = msg,
-                        .line = line - 1,
-                        .column = column - 1,
+                        .line = line_0based,
+                        .column = column_0based,
                         .kind = kind,
                     },
                 };
@@ -737,7 +769,7 @@ pub const TestContext = struct {
                     }
                     var ok = true;
                     if (case.expect_exact) {
-                        var err_iter = ErrLineIter.init(result.stderr);
+                        var err_iter = std.mem.split(result.stderr, "\n");
                         var i: usize = 0;
                         ok = while (err_iter.next()) |line| : (i += 1) {
                             if (i >= case_error_list.len) break false;
@@ -940,8 +972,10 @@ pub const TestContext = struct {
                                         std.mem.eql(u8, case_msg.src.src_path, actual_msg.src_path);
 
                                     if (src_path_ok and
-                                        actual_msg.line == case_msg.src.line and
-                                        actual_msg.column == case_msg.src.column and
+                                        (case_msg.src.line == std.math.maxInt(u32) or
+                                        actual_msg.line == case_msg.src.line) and
+                                        (case_msg.src.column == std.math.maxInt(u32) or
+                                        actual_msg.column == case_msg.src.column) and
                                         std.mem.eql(u8, case_msg.src.msg, actual_msg.msg) and
                                         case_msg.src.kind == .@"error")
                                     {
@@ -978,8 +1012,10 @@ pub const TestContext = struct {
                                     }
                                     if (ex_tag != .src) continue;
 
-                                    if (actual_msg.line == case_msg.src.line and
-                                        actual_msg.column == case_msg.src.column and
+                                    if ((case_msg.src.line == std.math.maxInt(u32) or
+                                        actual_msg.line == case_msg.src.line) and
+                                        (case_msg.src.column == std.math.maxInt(u32) or
+                                        actual_msg.column == case_msg.src.column) and
                                         std.mem.eql(u8, case_msg.src.msg, actual_msg.msg) and
                                         case_msg.src.kind == .note)
                                     {
@@ -1162,19 +1198,3 @@ fn dumpArgs(argv: []const []const u8) void {
 }
 
 const tmp_src_path = "tmp.zig";
-
-const ErrLineIter = struct {
-    lines: std.mem.SplitIterator,
-
-    fn init(input: []const u8) ErrLineIter {
-        return ErrLineIter{ .lines = std.mem.split(input, "\n") };
-    }
-
-    fn next(self: *ErrLineIter) ?[]const u8 {
-        while (self.lines.next()) |line| {
-            if (std.mem.indexOf(u8, line, tmp_src_path) != null)
-                return line;
-        }
-        return null;
-    }
-};
test/compile_errors.zig
@@ -2,6 +2,14 @@ const std = @import("std");
 const TestContext = @import("../src/test.zig").TestContext;
 
 pub fn addCases(ctx: *TestContext) !void {
+    ctx.exeErrStage1("std.fmt error for unused arguments",
+        \\pub fn main() !void {
+        \\    @import("std").debug.print("{d} {d} {d} {d} {d}", .{1,2,3,4,5,6,7,8,9,10,11,12,13,14,15});
+        \\}
+    , &.{
+        "?:?:?: error: 10 unused arguments in '{d} {d} {d} {d} {d}'",
+    });
+
     ctx.objErrStage1("lazy pointer with undefined element type",
         \\export fn foo() void {
         \\    comptime var T: type = undefined;
@@ -6120,7 +6128,7 @@ pub fn addCases(ctx: *TestContext) !void {
         \\
         \\export fn entry() usize { return @sizeOf(@TypeOf(resource)); }
     , &[_][]const u8{
-        "tmp.zig:1:29: error: unable to find 'bogus.txt'",
+        "tmp.zig:1:29: error: unable to find '",
     });
 
     ctx.objErrStage1("non-const expression in struct literal outside function",