From 60308550978be821515323256af17e0b8a610b1c Mon Sep 17 00:00:00 2001 From: Chadwain Holness <39384757+chadwain@users.noreply.github.com> Date: Sat, 3 Feb 2024 16:47:55 -0500 Subject: [PATCH] std.fmt: fix formatting slices of structs with custom formatting `hasMethod` doesn't make sense for pointers to more than one item. --- lib/std/fmt.zig | 18 ++++++++++++++++ lib/std/meta.zig | 53 +++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 70 insertions(+), 1 deletion(-) diff --git a/lib/std/fmt.zig b/lib/std/fmt.zig index f89a205064..f1f38e2db6 100644 --- a/lib/std/fmt.zig +++ b/lib/std/fmt.zig @@ -2255,6 +2255,24 @@ test "slice" { try expectFmt("int: { 1, 1000, 5fad3, 423a35c7 }", "int: {x}", .{int_slice[runtime_zero..]}); try expectFmt("int: { 00001, 01000, 5fad3, 423a35c7 }", "int: {x:0>5}", .{int_slice[runtime_zero..]}); } + { + const S1 = struct { + x: u8, + }; + const struct_slice: []const S1 = &[_]S1{ S1{ .x = 8 }, S1{ .x = 42 } }; + try expectFmt("slice: { fmt.test.slice.S1{ .x = 8 }, fmt.test.slice.S1{ .x = 42 } }", "slice: {any}", .{struct_slice}); + } + { + const S2 = struct { + x: u8, + + pub fn format(s: @This(), comptime _: []const u8, _: std.fmt.FormatOptions, writer: anytype) !void { + try writer.print("S2({})", .{s.x}); + } + }; + const struct_slice: []const S2 = &[_]S2{ S2{ .x = 8 }, S2{ .x = 42 } }; + try expectFmt("slice: { S2(8), S2(42) }", "slice: {any}", .{struct_slice}); + } } test "escape non-printable" { diff --git a/lib/std/meta.zig b/lib/std/meta.zig index 922324c8c3..0df80359ac 100644 --- a/lib/std/meta.zig +++ b/lib/std/meta.zig @@ -1122,15 +1122,66 @@ pub inline fn hasFn(comptime T: type, comptime name: []const u8) bool { return @typeInfo(@TypeOf(@field(T, name))) == .Fn; } +test "hasFn" { + const S1 = struct { + pub fn foo() void {} + }; + + try std.testing.expect(hasFn(S1, "foo")); + try std.testing.expect(!hasFn(S1, "bar")); + try std.testing.expect(!hasFn(*S1, "foo")); + + const S2 = struct { + foo: fn () void, + }; + + try std.testing.expect(!hasFn(S2, "foo")); +} + /// Returns true if a type has a `name` method; `false` otherwise. /// Result is always comptime-known. pub inline fn hasMethod(comptime T: type, comptime name: []const u8) bool { return switch (@typeInfo(T)) { - .Pointer => |P| hasFn(P.child, name), + .Pointer => |P| switch (P.size) { + .One => hasFn(P.child, name), + .Many, .Slice, .C => false, + }, else => hasFn(T, name), }; } +test "hasMethod" { + try std.testing.expect(!hasMethod(u32, "foo")); + try std.testing.expect(!hasMethod([]u32, "len")); + try std.testing.expect(!hasMethod(struct { u32, u64 }, "len")); + + const S1 = struct { + pub fn foo() void {} + }; + + try std.testing.expect(hasMethod(S1, "foo")); + try std.testing.expect(hasMethod(*S1, "foo")); + + try std.testing.expect(!hasMethod(S1, "bar")); + try std.testing.expect(!hasMethod(*[1]S1, "foo")); + try std.testing.expect(!hasMethod(*[10]S1, "foo")); + try std.testing.expect(!hasMethod([]S1, "foo")); + + const S2 = struct { + foo: fn () void, + }; + + try std.testing.expect(!hasMethod(S2, "foo")); + + const U = union { + pub fn foo() void {} + }; + + try std.testing.expect(hasMethod(U, "foo")); + try std.testing.expect(hasMethod(*U, "foo")); + try std.testing.expect(!hasMethod(U, "bar")); +} + /// True if every value of the type `T` has a unique bit pattern representing it. /// In other words, `T` has no unused bits and no padding. /// Result is always comptime-known.