authorgravatar for goon.pri.low@gmail.comKendall Condon <goon.pri.low@gmail.com> 2026-03-22 17:39:43-04:00
committergravatar for goon.pri.low@gmail.comKendall Condon <goon.pri.low@gmail.com> 2026-03-25 17:30:51-04:00
log92915a42b55f2916f36ed6b1a2d51363331f47f8
tree39d42e8f5e65576285c49aeacbf5a2de9c8c2e70
parentab237855b8df3f91f232c7e63eba7d21f166f75f

zig fmt: do not reorder pointer casts with comments

This was especially bad as it could crash or duplicate them due to them being moved around "zig fmt: on/off". As a bonus this fixes a crash from reordering at the start of the file.

2 files changed, 46 insertions(+), 12 deletions(-)

lib/std/zig/Ast/Render.zig+25-12
...@@ -800,34 +800,47 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {...@@ -800,34 +800,47 @@ fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {
800 canonicalize: {800 canonicalize: {
801 if (params.len != 1) break :canonicalize;801 if (params.len != 1) break :canonicalize;
802802
803 const CastKind = enum {803 const CastKind = enum(u8) {
804 ptrCast,804 ptrCast,
805 alignCast,805 alignCast,
806 addrSpaceCast,806 addrSpaceCast,
807 constCast,807 constCast,
808 volatileCast,808 volatileCast,
809 };809 };
810 const kind = meta.stringToEnum(CastKind, tree.tokenSlice(builtin_token)[1..]) orelse break :canonicalize;810 const kind = meta.stringToEnum(
811 CastKind,
812 tree.tokenSlice(builtin_token)[1..],
813 ) orelse break :canonicalize;
811814
812 var cast_map = std.EnumMap(CastKind, Ast.TokenIndex).init(.{});815 var cast_map = std.EnumMap(CastKind, Ast.TokenIndex).init(.{});
813 cast_map.put(kind, builtin_token);816 cast_map.put(kind, builtin_token);
814817
815 var casts_before: usize = 0;818 var casts_before: usize = 0;
816 if (builtin_token >= 2) {819 var prev_builtin_token = builtin_token;
817 var prev_builtin_token = builtin_token - 2;820 while (prev_builtin_token >= 2) {
818 while (tree.tokenTag(prev_builtin_token) == .builtin) : (prev_builtin_token -= 2) {821 prev_builtin_token -= 2;
819 const prev_kind = meta.stringToEnum(CastKind, tree.tokenSlice(prev_builtin_token)[1..]) orelse break;822 if (tree.tokenTag(prev_builtin_token) != .builtin) break;
820 if (cast_map.contains(prev_kind)) break :canonicalize;823 const builtin_name = tree.tokenSlice(prev_builtin_token)[1..];
821 cast_map.put(prev_kind, prev_builtin_token);824 const prev_kind = meta.stringToEnum(CastKind, builtin_name) orelse break;
822 casts_before += 1;825 if (cast_map.contains(prev_kind)) break :canonicalize;
823 }826 // This must be checked after so that cast builtins as arguments to other
827 // builtins containing comments are reordered.
828 if (hasComment(tree, prev_builtin_token, prev_builtin_token + 2))
829 break :canonicalize;
830 cast_map.put(prev_kind, prev_builtin_token);
831 casts_before += 1;
824 }832 }
825833
826 var next_builtin_token = builtin_token + 2;834 var next_builtin_token = builtin_token + 2;
827 while (tree.tokenTag(next_builtin_token) == .builtin) : (next_builtin_token += 2) {835 while (true) {
828 const next_kind = meta.stringToEnum(CastKind, tree.tokenSlice(next_builtin_token)[1..]) orelse break;836 if (hasComment(tree, next_builtin_token - 2, next_builtin_token))
837 break :canonicalize;
838 if (tree.tokenTag(next_builtin_token) != .builtin) break;
839 const builtin_name = tree.tokenSlice(next_builtin_token)[1..];
840 const next_kind = meta.stringToEnum(CastKind, builtin_name) orelse break;
829 if (cast_map.contains(next_kind)) break :canonicalize;841 if (cast_map.contains(next_kind)) break :canonicalize;
830 cast_map.put(next_kind, next_builtin_token);842 cast_map.put(next_kind, next_builtin_token);
843 next_builtin_token += 2;
831 }844 }
832845
833 var it = cast_map.iterator();846 var it = cast_map.iterator();
lib/std/zig/parser_test.zig+21
...@@ -6133,6 +6133,16 @@ test "zig fmt: do not canonicalize invalid cast builtins" {...@@ -6133,6 +6133,16 @@ test "zig fmt: do not canonicalize invalid cast builtins" {
6133 );6133 );
6134}6134}
61356135
6136test "zig fmt: canonicalize cast builtins at file start" {
6137 try testTransform(
6138 \\@alignCast(@ptrCast(a)),
6139 \\
6140 ,
6141 \\@ptrCast(@alignCast(a)),
6142 \\
6143 );
6144}
6145
6136test "zig fmt: extern addrspace in struct" {6146test "zig fmt: extern addrspace in struct" {
6137 try testCanonical(6147 try testCanonical(
6138 \\const namespace = struct {6148 \\const namespace = struct {
...@@ -6909,6 +6919,17 @@ test "zig fmt: render extra colons with comments" {...@@ -6909,6 +6919,17 @@ test "zig fmt: render extra colons with comments" {
6909 );6919 );
6910}6920}
69116921
6922test "zig fmt: cast builtins are not reordered with comments" {
6923 try testCanonical(
6924 \\const a = @volatileCast(@constCast( // ...
6925 \\ @alignCast(@ptrCast(a))));
6926 \\
6927 \\const b = @alignCast(@ptrCast( // zig fmt: off
6928 \\ c));
6929 \\
6930 );
6931}
6932
6912test "recovery: top level" {6933test "recovery: top level" {
6913 try testError(6934 try testError(
6914 \\test "" {inline}6935 \\test "" {inline}