Commit baafb8a491

r00ster91 <r00ster91@proton.me>
2022-07-23 16:53:59
std.fmt: add more invalid format string errors
1 parent 4ef7d85
lib/std/fmt.zig
@@ -454,6 +454,10 @@ fn stripOptionalOrErrorUnionSpec(comptime fmt: []const u8) []const u8 {
         fmt[1..];
 }
 
+fn invalidFmtErr(comptime fmt: []const u8, value: anytype) void {
+    @compileError("invalid format string '" ++ fmt ++ "' for type '" ++ @typeName(@TypeOf(value)) ++ "'");
+}
+
 pub fn formatType(
     value: anytype,
     comptime fmt: []const u8,
@@ -482,9 +486,11 @@ pub fn formatType(
             return formatValue(value, actual_fmt, options, writer);
         },
         .Void => {
+            if (actual_fmt.len != 0) invalidFmtErr(fmt, value);
             return formatBuf("void", options, writer);
         },
         .Bool => {
+            if (actual_fmt.len != 0) invalidFmtErr(fmt, value);
             return formatBuf(if (value) "true" else "false", options, writer);
         },
         .Optional => {
@@ -504,16 +510,18 @@ pub fn formatType(
             if (value) |payload| {
                 return formatType(payload, remaining_fmt, options, writer, max_depth);
             } else |err| {
-                return formatType(err, remaining_fmt, options, writer, max_depth);
+                return formatType(err, "", options, writer, max_depth);
             }
         },
         .ErrorSet => {
+            if (actual_fmt.len != 0) invalidFmtErr(fmt, value);
             try writer.writeAll("error.");
             return writer.writeAll(@errorName(value));
         },
         .Enum => |enumInfo| {
             try writer.writeAll(@typeName(T));
             if (enumInfo.is_exhaustive) {
+                if (actual_fmt.len != 0) invalidFmtErr(fmt, value);
                 try writer.writeAll(".");
                 try writer.writeAll(@tagName(value));
                 return;
@@ -534,6 +542,7 @@ pub fn formatType(
             try writer.writeAll(")");
         },
         .Union => |info| {
+            if (actual_fmt.len != 0) invalidFmtErr(fmt, value);
             try writer.writeAll(@typeName(T));
             if (max_depth == 0) {
                 return writer.writeAll("{ ... }");
@@ -553,6 +562,7 @@ pub fn formatType(
             }
         },
         .Struct => |info| {
+            if (actual_fmt.len != 0) invalidFmtErr(fmt, value);
             if (info.is_tuple) {
                 // Skip the type and field names when formatting tuples.
                 if (max_depth == 0) {
@@ -608,7 +618,7 @@ pub fn formatType(
                         }
                         return;
                     }
-                    @compileError("unknown format string: '" ++ actual_fmt ++ "' for type '" ++ @typeName(T) ++ "'");
+                    invalidFmtErr(fmt, value);
                 },
                 .Enum, .Union, .Struct => {
                     return formatType(value.*, actual_fmt, options, writer, max_depth);
@@ -630,7 +640,7 @@ pub fn formatType(
                         else => {},
                     }
                 }
-                @compileError("unknown format string: '" ++ actual_fmt ++ "' for type '" ++ @typeName(T) ++ "'");
+                invalidFmtErr(fmt, value);
             },
             .Slice => {
                 if (actual_fmt.len == 0)
@@ -693,14 +703,22 @@ pub fn formatType(
             try writer.writeAll(" }");
         },
         .Fn => {
+            if (actual_fmt.len != 0) invalidFmtErr(fmt, value);
             return format(writer, "{s}@{x}", .{ @typeName(T), @ptrToInt(value) });
         },
-        .Type => return formatBuf(@typeName(value), options, writer),
+        .Type => {
+            if (actual_fmt.len != 0) invalidFmtErr(fmt, value);
+            return formatBuf(@typeName(value), options, writer);
+        },
         .EnumLiteral => {
+            if (actual_fmt.len != 0) invalidFmtErr(fmt, value);
             const buffer = [_]u8{'.'} ++ @tagName(value);
             return formatBuf(buffer, options, writer);
         },
-        .Null => return formatBuf("null", options, writer),
+        .Null => {
+            if (actual_fmt.len != 0) invalidFmtErr(fmt, value);
+            return formatBuf("null", options, writer);
+        },
         else => @compileError("unable to format type '" ++ @typeName(T) ++ "'"),
     }
 }
@@ -768,7 +786,7 @@ pub fn formatIntValue(
         radix = 8;
         case = .lower;
     } else {
-        @compileError("unsupported format string '" ++ fmt ++ "' for type '" ++ @typeName(@TypeOf(value)) ++ "'");
+        invalidFmtErr(fmt, value);
     }
 
     return formatInt(int_value, radix, case, options, writer);
@@ -797,7 +815,7 @@ fn formatFloatValue(
             error.NoSpaceLeft => unreachable,
         };
     } else {
-        @compileError("unsupported format string '" ++ fmt ++ "' for type '" ++ @typeName(@TypeOf(value)) ++ "'");
+        invalidFmtErr(fmt, value);
     }
 
     return formatBuf(buf_stream.getWritten(), options, writer);
@@ -2000,6 +2018,12 @@ test "optional" {
     {
         const value: ?i32 = 1234;
         try expectFmt("optional: 1234\n", "optional: {?}\n", .{value});
+        try expectFmt("optional: 1234\n", "optional: {?d}\n", .{value});
+        try expectFmt("optional: 4d2\n", "optional: {?x}\n", .{value});
+    }
+    {
+        const value: ?[]const u8 = "string";
+        try expectFmt("optional: string\n", "optional: {?s}\n", .{value});
     }
     {
         const value: ?i32 = null;
@@ -2015,6 +2039,12 @@ test "error" {
     {
         const value: anyerror!i32 = 1234;
         try expectFmt("error union: 1234\n", "error union: {!}\n", .{value});
+        try expectFmt("error union: 1234\n", "error union: {!d}\n", .{value});
+        try expectFmt("error union: 4d2\n", "error union: {!x}\n", .{value});
+    }
+    {
+        const value: anyerror![]const u8 = "string";
+        try expectFmt("error union: string\n", "error union: {!s}\n", .{value});
     }
     {
         const value: anyerror!i32 = error.InvalidChar;
@@ -2209,6 +2239,10 @@ test "struct" {
 }
 
 test "enum" {
+    if (builtin.zig_backend == .stage1) {
+        // stage1 starts the typename with 'std' which might also be desireable for stage2
+        return error.SkipZigTest;
+    }
     const Enum = enum {
         One,
         Two,
@@ -2216,8 +2250,8 @@ test "enum" {
     const value = Enum.Two;
     try expectFmt("enum: Enum.Two\n", "enum: {}\n", .{value});
     try expectFmt("enum: Enum.Two\n", "enum: {}\n", .{&value});
-    try expectFmt("enum: Enum.One\n", "enum: {x}\n", .{Enum.One});
-    try expectFmt("enum: Enum.Two\n", "enum: {X}\n", .{Enum.Two});
+    try expectFmt("enum: Enum.One\n", "enum: {}\n", .{Enum.One});
+    try expectFmt("enum: Enum.Two\n", "enum: {}\n", .{Enum.Two});
 
     // test very large enum to verify ct branch quota is large enough
     try expectFmt("enum: os.windows.win32error.Win32Error.INVALID_FUNCTION\n", "enum: {}\n", .{std.os.windows.Win32Error.INVALID_FUNCTION});
@@ -2675,7 +2709,7 @@ test "vector" {
 }
 
 test "enum-literal" {
-    try expectFmt(".hello_world", "{s}", .{.hello_world});
+    try expectFmt(".hello_world", "{}", .{.hello_world});
 }
 
 test "padding" {
src/arch/sparc64/bits.zig
@@ -1582,8 +1582,8 @@ test "Serialize formats" {
     for (testcases) |case| {
         const actual = case.inst.toU32();
         testing.expectEqual(case.expected, actual) catch |err| {
-            std.debug.print("error: {x}\n", .{err});
-            std.debug.print("case: {x}\n", .{case});
+            std.debug.print("error: {}\n", .{err});
+            std.debug.print("case: {}\n", .{case});
             return err;
         };
     }
src/arch/wasm/CodeGen.zig
@@ -1773,7 +1773,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
         } else if (func_val.castTag(.decl_ref)) |decl_ref| {
             break :blk module.declPtr(decl_ref.data);
         }
-        return self.fail("Expected a function, but instead found type '{s}'", .{func_val.tag()});
+        return self.fail("Expected a function, but instead found type '{}'", .{func_val.tag()});
     };
 
     const sret = if (first_param_sret) blk: {
@@ -2365,7 +2365,7 @@ fn lowerConstant(self: *Self, val: Value, ty: Type) InnerError!WValue {
             },
             .int_u64, .one => return WValue{ .imm32 = @intCast(u32, val.toUnsignedInt(target)) },
             .zero, .null_value => return WValue{ .imm32 = 0 },
-            else => return self.fail("Wasm TODO: lowerConstant for other const pointer tag {s}", .{val.tag()}),
+            else => return self.fail("Wasm TODO: lowerConstant for other const pointer tag {}", .{val.tag()}),
         },
         .Enum => {
             if (val.castTag(.enum_field_index)) |field_index| {
@@ -2421,7 +2421,7 @@ fn lowerConstant(self: *Self, val: Value, ty: Type) InnerError!WValue {
             const is_pl = val.tag() == .opt_payload;
             return WValue{ .imm32 = if (is_pl) @as(u32, 1) else 0 };
         },
-        else => |zig_type| return self.fail("Wasm TODO: LowerConstant for zigTypeTag {s}", .{zig_type}),
+        else => |zig_type| return self.fail("Wasm TODO: LowerConstant for zigTypeTag {}", .{zig_type}),
     }
 }
 
src/arch/x86_64/Emit.zig
@@ -202,7 +202,7 @@ pub fn lowerMir(emit: *Emit) InnerError!void {
             .pop_regs => try emit.mirPushPopRegisterList(.pop, inst),
 
             else => {
-                return emit.fail("Implement MIR->Emit lowering for x86_64 for pseudo-inst: {s}", .{tag});
+                return emit.fail("Implement MIR->Emit lowering for x86_64 for pseudo-inst: {}", .{tag});
             },
         }
     }
src/link/MachO/Atom.zig
@@ -246,7 +246,7 @@ pub fn parseRelocs(self: *Atom, relocs: []const macho.relocation_info, context:
                             else => {
                                 log.err("unexpected relocation type after ARM64_RELOC_ADDEND", .{});
                                 log.err("  expected ARM64_RELOC_PAGE21 or ARM64_RELOC_PAGEOFF12", .{});
-                                log.err("  found {s}", .{next});
+                                log.err("  found {}", .{next});
                                 return error.UnexpectedRelocationType;
                             },
                         }
@@ -285,7 +285,7 @@ pub fn parseRelocs(self: *Atom, relocs: []const macho.relocation_info, context:
                     else => {
                         log.err("unexpected relocation type after ARM64_RELOC_ADDEND", .{});
                         log.err("  expected ARM64_RELOC_UNSIGNED", .{});
-                        log.err("  found {s}", .{@intToEnum(macho.reloc_type_arm64, relocs[i + 1].r_type)});
+                        log.err("  found {}", .{@intToEnum(macho.reloc_type_arm64, relocs[i + 1].r_type)});
                         return error.UnexpectedRelocationType;
                     },
                 },
@@ -294,7 +294,7 @@ pub fn parseRelocs(self: *Atom, relocs: []const macho.relocation_info, context:
                     else => {
                         log.err("unexpected relocation type after X86_64_RELOC_ADDEND", .{});
                         log.err("  expected X86_64_RELOC_UNSIGNED", .{});
-                        log.err("  found {s}", .{@intToEnum(macho.reloc_type_x86_64, relocs[i + 1].r_type)});
+                        log.err("  found {}", .{@intToEnum(macho.reloc_type_x86_64, relocs[i + 1].r_type)});
                         return error.UnexpectedRelocationType;
                     },
                 },
src/link/MachO/Dylib.zig
@@ -167,7 +167,7 @@ pub fn parse(
     const this_arch: std.Target.Cpu.Arch = try fat.decodeArch(self.header.?.cputype, true);
 
     if (this_arch != cpu_arch) {
-        log.err("mismatched cpu architecture: expected {s}, found {s}", .{ cpu_arch, this_arch });
+        log.err("mismatched cpu architecture: expected {}, found {}", .{ cpu_arch, this_arch });
         return error.MismatchedCpuArchitecture;
     }
 
@@ -208,7 +208,7 @@ fn readLoadCommands(
                 }
             },
             else => {
-                log.debug("Unknown load command detected: 0x{x}.", .{cmd.cmd()});
+                log.debug("Unknown load command detected: 0x{x}.", .{@enumToInt(cmd.cmd())});
             },
         }
         self.load_commands.appendAssumeCapacity(cmd);
src/link/MachO/fat.zig
@@ -46,7 +46,7 @@ pub fn getLibraryOffset(reader: anytype, cpu_arch: std.Target.Cpu.Arch) !u64 {
             return fat_arch.offset;
         }
     } else {
-        log.err("Could not find matching cpu architecture in fat library: expected {s}", .{cpu_arch});
+        log.err("Could not find matching cpu architecture in fat library: expected {}", .{cpu_arch});
         return error.MismatchedCpuArchitecture;
     }
 }
src/link/MachO/Object.zig
@@ -110,7 +110,7 @@ pub fn parse(self: *Object, allocator: Allocator, cpu_arch: std.Target.Cpu.Arch)
         },
     };
     if (this_arch != cpu_arch) {
-        log.err("mismatched cpu architecture: expected {s}, found {s}", .{ cpu_arch, this_arch });
+        log.err("mismatched cpu architecture: expected {}, found {}", .{ cpu_arch, this_arch });
         return error.MismatchedCpuArchitecture;
     }
 
@@ -171,7 +171,7 @@ pub fn parse(self: *Object, allocator: Allocator, cpu_arch: std.Target.Cpu.Arch)
                 cmd.linkedit_data.dataoff += file_offset;
             },
             else => {
-                log.debug("Unknown load command detected: 0x{x}.", .{cmd.cmd()});
+                log.debug("Unknown load command detected: 0x{x}.", .{@enumToInt(cmd.cmd())});
             },
         }
         self.load_commands.appendAssumeCapacity(cmd);
src/main.zig
@@ -4076,12 +4076,12 @@ pub fn cmdFmt(gpa: Allocator, arena: Allocator, args: []const []const u8) !void
 
         const stdin = io.getStdIn();
         const source_code = readSourceFileToEndAlloc(gpa, &stdin, null) catch |err| {
-            fatal("unable to read stdin: {s}", .{err});
+            fatal("unable to read stdin: {}", .{err});
         };
         defer gpa.free(source_code);
 
         var tree = std.zig.parse(gpa, source_code) catch |err| {
-            fatal("error parsing stdin: {s}", .{err});
+            fatal("error parsing stdin: {}", .{err});
         };
         defer tree.deinit(gpa);
 
@@ -5011,7 +5011,7 @@ pub fn cmdAstCheck(
     } else {
         const stdin = io.getStdIn();
         const source = readSourceFileToEndAlloc(arena, &stdin, null) catch |err| {
-            fatal("unable to read stdin: {s}", .{err});
+            fatal("unable to read stdin: {}", .{err});
         };
         file.sub_file_path = "<stdin>";
         file.source = source;
src/print_zir.zig
@@ -551,7 +551,7 @@ const Writer = struct {
     fn writeElemTypeIndex(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
         const inst_data = self.code.instructions.items(.data)[inst].bin;
         try self.writeInstRef(stream, inst_data.lhs);
-        try stream.print(", {d})", .{inst_data.rhs});
+        try stream.print(", {d})", .{@enumToInt(inst_data.rhs)});
     }
 
     fn writeUnNode(
src/Sema.zig
@@ -2173,7 +2173,7 @@ fn coerceResultPtr(
             },
             else => {
                 if (std.debug.runtime_safety) {
-                    std.debug.panic("unexpected AIR tag for coerce_result_ptr: {s}", .{
+                    std.debug.panic("unexpected AIR tag for coerce_result_ptr: {}", .{
                         air_tags[trash_inst],
                     });
                 } else {
src/translate_c.zig
@@ -3279,7 +3279,7 @@ fn transConstantExpr(c: *Context, scope: *Scope, expr: *const clang.Expr, used:
             return maybeSuppressResult(c, scope, used, as_node);
         },
         else => |kind| {
-            return fail(c, error.UnsupportedTranslation, expr.getBeginLoc(), "unsupported constant expression kind '{s}'", .{kind});
+            return fail(c, error.UnsupportedTranslation, expr.getBeginLoc(), "unsupported constant expression kind '{}'", .{kind});
         },
     }
 }