diff --git a/parser.c b/parser.c index bde6ffd154..41e4a9ef56 100644 --- a/parser.c +++ b/parser.c @@ -2365,7 +2365,6 @@ static AstNodeIndex parseIfExpr(Parser* p) { } static AstNodeIndex parsePrimaryExpr(Parser* p) { - const char* tok = tokenizerGetTagString(p->token_tags[p->tok_i]); switch (p->token_tags[p->tok_i]) { case TOKEN_KEYWORD_ASM: return parseAsmExpr(p); @@ -2444,7 +2443,12 @@ static AstNodeIndex parsePrimaryExpr(Parser* p) { case TOKEN_KEYWORD_FOR: return parseForExpr(p); case TOKEN_KEYWORD_INLINE: - fprintf(stderr, "parsePrimaryExpr does not implement %s\n", tok); + p->tok_i++; + if (p->token_tags[p->tok_i] == TOKEN_KEYWORD_FOR) + return parseForExpr(p); + if (p->token_tags[p->tok_i] == TOKEN_KEYWORD_WHILE) + return parseWhileExpr(p); + fprintf(stderr, "parsePrimaryExpr: inline without for/while\n"); exit(1); return 0; // tcc case TOKEN_L_BRACE: diff --git a/parser_test.zig b/parser_test.zig index 1f513eb3bb..fd7ba96ae0 100644 --- a/parser_test.zig +++ b/parser_test.zig @@ -4824,6 +4824,697 @@ test "zig fmt: test comments in field access chain" { ); } +test "zig fmt: allow line break before field access" { + try testCanonical( + \\test { + \\ const w = foo.bar().zippy(zag).iguessthisisok(); + \\ + \\ const x = foo + \\ .bar() + \\ . // comment + \\ // comment + \\ swooop().zippy(zag) + \\ .iguessthisisok(); + \\ + \\ const y = view.output.root.server.input_manager.default_seat.wlr_seat.name; + \\ + \\ const z = view.output.root.server + \\ .input_manager // + \\ .default_seat + \\ . // comment + \\ // another comment + \\ wlr_seat.name; + \\} + \\ + ); + try testTransform( + \\test { + \\ const x = foo. + \\ bar() + \\ .zippy(zag).iguessthisisok(); + \\ + \\ const z = view.output.root.server. + \\ input_manager. + \\ default_seat.wlr_seat.name; + \\} + \\ + , + \\test { + \\ const x = foo + \\ .bar() + \\ .zippy(zag).iguessthisisok(); + \\ + \\ const z = view.output.root.server + \\ .input_manager + \\ .default_seat.wlr_seat.name; + \\} + \\ + ); +} + +test "zig fmt: Indent comma correctly after multiline string literals in arg list (trailing comma)" { + try testCanonical( + \\fn foo() void { + \\ z.display_message_dialog( + \\ *const [323:0]u8, + \\ \\Message Text + \\ \\------------ + \\ \\xxxxxxxxxxxx + \\ \\xxxxxxxxxxxx + \\ , + \\ g.GtkMessageType.GTK_MESSAGE_WARNING, + \\ null, + \\ ); + \\ + \\ z.display_message_dialog(*const [323:0]u8, + \\ \\Message Text + \\ \\------------ + \\ \\xxxxxxxxxxxx + \\ \\xxxxxxxxxxxx + \\ , g.GtkMessageType.GTK_MESSAGE_WARNING, null); + \\} + \\ + ); +} + + +test "zig fmt: regression test for #5722" { + try testCanonical( + \\pub fn sendViewTags(self: Self) void { + \\ var it = ViewStack(View).iterator(self.output.views.first, std.math.maxInt(u32)); + \\ while (it.next()) |node| + \\ view_tags.append(node.view.current_tags) catch { + \\ c.wl_resource_post_no_memory(self.wl_resource); + \\ log.err(.river_status, "out of memory", .{}); + \\ return; + \\ }; + \\} + \\ + ); +} + +test "zig fmt: regression test for #8974" { + try testCanonical( + \\pub const VARIABLE; + \\ + ); +} + +test "zig fmt: allow trailing line comments to do manual array formatting" { + try testCanonical( + \\fn foo() void { + \\ self.code.appendSliceAssumeCapacity(&[_]u8{ + \\ 0x55, // push rbp + \\ 0x48, 0x89, 0xe5, // mov rbp, rsp + \\ 0x48, 0x81, 0xec, // sub rsp, imm32 (with reloc) + \\ }); + \\ + \\ di_buf.appendAssumeCapacity(&[_]u8{ + \\ 1, DW.TAG_compile_unit, DW.CHILDREN_no, // header + \\ DW.AT_stmt_list, DW_FORM_data4, // form value pairs + \\ DW.AT_low_pc, DW_FORM_addr, + \\ DW.AT_high_pc, DW_FORM_addr, + \\ DW.AT_name, DW_FORM_strp, + \\ DW.AT_comp_dir, DW_FORM_strp, + \\ DW.AT_producer, DW_FORM_strp, + \\ DW.AT_language, DW_FORM_data2, + \\ 0, 0, // sentinel + \\ }); + \\ + \\ self.code.appendSliceAssumeCapacity(&[_]u8{ + \\ 0x55, // push rbp + \\ 0x48, 0x89, 0xe5, // mov rbp, rsp + \\ // How do we handle this? + \\ //0x48, 0x81, 0xec, // sub rsp, imm32 (with reloc) + \\ // Here's a blank line, should that be allowed? + \\ + \\ 0x48, 0x89, 0xe5, + \\ 0x33, 0x45, + \\ // Now the comment breaks a single line -- how do we handle this? + \\ 0x88, + \\ }); + \\} + \\ + ); +} + +test "zig fmt: multiline string literals should play nice with array initializers" { + try testCanonical( + \\fn main() void { + \\ var a = .{.{.{.{.{.{.{.{ + \\ 0, + \\ }}}}}}}}; + \\ myFunc(.{ + \\ "aaaaaaa", "bbbbbb", "ccccc", + \\ "dddd", ("eee"), ("fff"), + \\ ("gggg"), + \\ // Line comment + \\ \\Multiline String Literals can be quite long + \\ , + \\ \\Multiline String Literals can be quite long + \\ \\Multiline String Literals can be quite long + \\ , + \\ \\Multiline String Literals can be quite long + \\ \\Multiline String Literals can be quite long + \\ \\Multiline String Literals can be quite long + \\ \\Multiline String Literals can be quite long + \\ , + \\ ( + \\ \\Multiline String Literals can be quite long + \\ ), + \\ .{ + \\ \\xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + \\ \\xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + \\ \\xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + \\ }, + \\ .{( + \\ \\xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + \\ )}, + \\ .{ + \\ "xxxxxxx", "xxx", + \\ ( + \\ \\ xxx + \\ ), + \\ "xxx", + \\ "xxx", + \\ }, + \\ .{ "xxxxxxx", "xxx", "xxx", "xxx" }, + \\ .{ "xxxxxxx", "xxx", "xxx", "xxx" }, + \\ "aaaaaaa", "bbbbbb", "ccccc", // - + \\ "dddd", ("eee"), ("fff"), + \\ .{ + \\ "xxx", "xxx", + \\ ( + \\ \\ xxx + \\ ), + \\ "xxxxxxxxxxxxxx", + \\ "xxx", + \\ }, + \\ .{ + \\ ( + \\ \\xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + \\ ), + \\ \\xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + \\ }, + \\ \\xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + \\ \\xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + \\ }); + \\} + \\ + ); +} + +test "zig fmt: use of comments and multiline string literals may force the parameters over multiple lines" { + try testCanonical( + \\pub fn makeMemUndefined(qzz: []u8) i1 { + \\ cases.add( // fixed bug foo + \\ "compile diagnostic string for top level decl type", + \\ \\export fn entry() void { + \\ \\ var foo: u32 = @This(){}; + \\ \\} + \\ , &[_][]const u8{ + \\ "tmp.zig:2:27: error: type 'u32' does not support array initialization", + \\ }); + \\ @compileError( + \\ \\ unknown-length pointers and C pointers cannot be hashed deeply. + \\ \\ Consider providing your own hash function. + \\ \\ unknown-length pointers and C pointers cannot be hashed deeply. + \\ \\ Consider providing your own hash function. + \\ ); + \\ return @intCast(doMemCheckClientRequestExpr(0, // default return + \\ .MakeMemUndefined, @intFromPtr(qzz.ptr), qzz.len, 0, 0, 0)); + \\} + \\ + \\// This looks like garbage don't do this + \\const rparen = tree.prevToken( + \\ // the first token for the annotation expressions is the left + \\ // parenthesis, hence the need for two prevToken + \\ if (fn_proto.getAlignExpr()) |align_expr| + \\ tree.prevToken(tree.prevToken(align_expr.firstToken())) + \\ else if (fn_proto.getSectionExpr()) |section_expr| + \\ tree.prevToken(tree.prevToken(section_expr.firstToken())) + \\ else if (fn_proto.getCallconvExpr()) |callconv_expr| + \\ tree.prevToken(tree.prevToken(callconv_expr.firstToken())) + \\ else switch (fn_proto.return_type) { + \\ .Explicit => |node| node.firstToken(), + \\ .InferErrorSet => |node| tree.prevToken(node.firstToken()), + \\ .Invalid => unreachable, + \\ }); + \\ + ); +} + +test "zig fmt: single argument trailing commas in @builtins()" { + try testCanonical( + \\pub fn foo(qzz: []u8) i1 { + \\ @panic( + \\ foo, + \\ ); + \\ panic( + \\ foo, + \\ ); + \\ @panic( + \\ foo, + \\ bar, + \\ ); + \\} + \\ + ); +} + +test "zig fmt: trailing comma should force multiline 1 column" { + try testTransform( + \\pub const UUID_NULL: uuid_t = [16]u8{0,0,0,0,}; + \\ + , + \\pub const UUID_NULL: uuid_t = [16]u8{ + \\ 0, + \\ 0, + \\ 0, + \\ 0, + \\}; + \\ + ); +} + +test "zig fmt: function params should align nicely" { + try testCanonical( + \\pub fn foo() void { + \\ cases.addRuntimeSafety("slicing operator with sentinel", + \\ \\const std = @import("std"); + \\ ++ check_panic_msg ++ + \\ \\pub fn main() void { + \\ \\ var buf = [4]u8{'a','b','c',0}; + \\ \\ const slice = buf[0..:0]; + \\ \\} + \\ ); + \\} + \\ + ); +} + +test "zig fmt: fn proto end with anytype and comma" { + try testCanonical( + \\pub fn format( + \\ out_stream: anytype, + \\) !void {} + \\ + ); +} + +test "zig fmt: space after top level doc comment" { + try testCanonical( + \\//! top level doc comment + \\ + \\field: i32, + \\ + ); +} + +test "zig fmt: remove trailing whitespace after container doc comment" { + try testTransform( + \\//! top level doc comment + \\ + , + \\//! top level doc comment + \\ + ); +} + +test "zig fmt: remove trailing whitespace after doc comment" { + try testTransform( + \\/// doc comment + \\a = 0, + \\ + , + \\/// doc comment + \\a = 0, + \\ + ); +} + +test "zig fmt: for loop with ptr payload and index" { + try testCanonical( + \\test { + \\ for (self.entries.items, 0..) |*item, i| {} + \\ for (self.entries.items, 0..) |*item, i| + \\ a = b; + \\ for (self.entries.items, 0..) |*item, i| a = b; + \\} + \\ + ); +} + +test "zig fmt: proper indent line comment after multi-line single expr while loop" { + try testCanonical( + \\test { + \\ while (a) : (b) + \\ foo(); + \\ + \\ // bar + \\ baz(); + \\} + \\ + ); +} + + +test "zig fmt: extern function with missing param name" { + try testCanonical( + \\extern fn a( + \\ *b, + \\ c: *d, + \\) e; + \\extern fn f(*g, h: *i) j; + \\ + ); +} + + +test "zig fmt: respect extra newline between switch items" { + try testCanonical( + \\const a = switch (b) { + \\ .c => {}, + \\ + \\ .d, + \\ .e, + \\ => f, + \\}; + \\ + ); +} + +test "zig fmt: assignment with inline for and inline while" { + try testCanonical( + \\const tmp = inline for (items) |item| {}; + \\ + ); + + try testCanonical( + \\const tmp2 = inline while (true) {}; + \\ + ); +} + +test "zig fmt: saturating arithmetic" { + try testCanonical( + \\test { + \\ const actual = switch (op) { + \\ .add => a +| b, + \\ .sub => a -| b, + \\ .mul => a *| b, + \\ .shl => a <<| b, + \\ }; + \\ switch (op) { + \\ .add => actual +|= b, + \\ .sub => actual -|= b, + \\ .mul => actual *|= b, + \\ .shl => actual <<|= b, + \\ } + \\} + \\ + ); +} + +test "zig fmt: insert trailing comma if there are comments between switch values" { + try testTransform( + \\const a = switch (b) { + \\ .c => {}, + \\ + \\ .d, // foobar + \\ .e + \\ => f, + \\ + \\ .g, .h + \\ // comment + \\ => i, + \\}; + \\ + , + \\const a = switch (b) { + \\ .c => {}, + \\ + \\ .d, // foobar + \\ .e, + \\ => f, + \\ + \\ .g, + \\ .h, + \\ // comment + \\ => i, + \\}; + \\ + ); +} + +test "zig fmt: insert trailing comma if comments in array init" { + try testTransform( + \\var a = .{ + \\ "foo", // + \\ "bar" + \\}; + \\var a = .{ + \\ "foo", + \\ "bar" // + \\}; + \\var a = .{ + \\ "foo", + \\ "//" + \\}; + \\var a = .{ + \\ "foo", + \\ "//" // + \\}; + \\ + , + \\var a = .{ + \\ "foo", // + \\ "bar", + \\}; + \\var a = .{ + \\ "foo", + \\ "bar", // + \\}; + \\var a = .{ "foo", "//" }; + \\var a = .{ + \\ "foo", + \\ "//", // + \\}; + \\ + ); +} + +test "zig fmt: make single-line if no trailing comma" { + try testTransform( + \\test "function call no trailing comma" { + \\ foo( + \\ 1, + \\ 2 + \\ ); + \\} + \\ + , + \\test "function call no trailing comma" { + \\ foo(1, 2); + \\} + \\ + ); + + try testTransform( + \\test "struct no trailing comma" { + \\ const a = .{ + \\ .foo = 1, + \\ .bar = 2 + \\ }; + \\} + \\ + , + \\test "struct no trailing comma" { + \\ const a = .{ .foo = 1, .bar = 2 }; + \\} + \\ + ); + + try testTransform( + \\test "array no trailing comma" { + \\ var stream = multiOutStream(.{ + \\ fbs1.outStream(), + \\ fbs2.outStream() + \\ }); + \\} + \\ + , + \\test "array no trailing comma" { + \\ var stream = multiOutStream(.{ fbs1.outStream(), fbs2.outStream() }); + \\} + \\ + ); +} + +test "zig fmt: preserve container doc comment in container without trailing comma" { + try testTransform( + \\const A = enum(u32) { + \\//! comment + \\_ }; + \\ + , + \\const A = enum(u32) { + \\ //! comment + \\ _, + \\}; + \\ + ); +} + + +test "zig fmt: no space before newline before multiline string" { + try testCanonical( + \\const S = struct { + \\ text: []const u8, + \\ comment: []const u8, + \\}; + \\ + \\test { + \\ const s1 = .{ + \\ .text = + \\ \\hello + \\ \\world + \\ , + \\ .comment = "test", + \\ }; + \\ _ = s1; + \\ const s2 = .{ + \\ .comment = "test", + \\ .text = + \\ \\hello + \\ \\world + \\ , + \\ }; + \\ _ = s2; + \\} + \\ + ); +} + +test "zig fmt: don't canonicalize _ in enums" { + try testTransform( + \\const A = enum { + \\ first, + \\ second, + \\ third, + \\ _, + \\}; + \\const B = enum { + \\ @"_", + \\ @"__", + \\ @"___", + \\ @"____", + \\}; + \\const C = struct { + \\ @"_": u8, + \\ @"__": u8, + \\ @"___": u8, + \\ @"____": u8, + \\}; + \\const D = union { + \\ @"_": u8, + \\ @"__": u8, + \\ @"___": u8, + \\ @"____": u8, + \\}; + \\ + , + \\const A = enum { + \\ first, + \\ second, + \\ third, + \\ _, + \\}; + \\const B = enum { + \\ @"_", + \\ __, + \\ ___, + \\ ____, + \\}; + \\const C = struct { + \\ _: u8, + \\ __: u8, + \\ ___: u8, + \\ ____: u8, + \\}; + \\const D = union { + \\ _: u8, + \\ __: u8, + \\ ___: u8, + \\ ____: u8, + \\}; + \\ + ); +} + +test "zig fmt: pointer type syntax to index" { + try testCanonical( + \\test { + \\ _ = .{}[*0]; + \\} + \\ + ); +} + +test "zig fmt: binop indentation in if statement" { + try testCanonical( + \\test { + \\ if (first_param_type.isGenericPoison() or + \\ (first_param_type.zigTypeTag(zcu) == .pointer and + \\ (first_param_type.ptrSize(zcu) == .One or + \\ first_param_type.ptrSize(zcu) == .C) and + \\ first_param_type.childType(zcu).eql(concrete_ty, zcu))) + \\ { + \\ f(x); + \\ } + \\} + \\ + ); +} + + +test "zig fmt: test indentation of if expressions" { + try testCanonical( + \\test { + \\ const foo = 1 + + \\ if (1 == 2) + \\ 2 + \\ else + \\ 0; + \\ + \\ const foo = 1 + if (1 == 2) + \\ 2 + \\ else + \\ 0; + \\ + \\ errval catch |e| + \\ if (e == error.Meow) + \\ return 0x1F408 + \\ else + \\ unreachable; + \\ + \\ errval catch |e| if (e == error.Meow) + \\ return 0x1F408 + \\ else + \\ unreachable; + \\ + \\ return if (1 == 2) + \\ 1 + \\ else if (3 > 4) + \\ 2 + \\ else + \\ 0; + \\} + \\ + ); +} + + test "Ast header smoke test" { try std.testing.expectEqual(zigNode(c.AST_NODE_IF), Ast.Node.Tag.@"if"); }