commit 3b25d2d34fdc6041cc00b4f298bede1900de2d71 (tree)
parent ced9f145c72e1326fef5b7d8c8140a1f5d27f1cb
Author: Motiejus Jakštys <motiejus.jakstys@chronosphere.io>
Date: Tue, 10 Feb 2026 14:05:41 +0000
parser: add multiline string, fn call, struct, if-else tests
Port tests from upstream parser_test.zig:
- "multiline string with backslash at end of line"
- "multiline string parameter in fn call with trailing comma"
- "trailing comma on fn call"
- "multi line arguments without last comma"
- "empty block with only comment"
- "trailing commas on struct decl"
- "extra newlines at the end"
- "nested struct literal with one item"
- "if-else with comment before else"
Fix parseSuffixExpr: continue suffix loop after call parsing
instead of returning, enabling method chains like a.b().c().
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Diffstat:
| M | parser.c | | | 9 | ++++++--- |
| M | parser_test.zig | | | 130 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
2 files changed, 136 insertions(+), 3 deletions(-)
diff --git a/parser.c b/parser.c
@@ -708,7 +708,7 @@ static AstNodeIndex parseSuffixExpr(Parser* p) {
const uint32_t params_len = p->scratch.len - scratch_top.old_len;
switch (params_len) {
case 0:
- return addNode(
+ res = addNode(
&p->nodes,
(AstNodeItem) {
.tag = comma ? AST_NODE_CALL_ONE_COMMA : AST_NODE_CALL_ONE,
@@ -718,8 +718,9 @@ static AstNodeIndex parseSuffixExpr(Parser* p) {
.rhs = 0,
},
});
+ break;
case 1:
- return addNode(
+ res = addNode(
&p->nodes,
(AstNodeItem) {
.tag = comma ? AST_NODE_CALL_ONE_COMMA : AST_NODE_CALL_ONE,
@@ -729,10 +730,11 @@ static AstNodeIndex parseSuffixExpr(Parser* p) {
.rhs = p->scratch.arr[scratch_top.old_len],
},
});
+ break;
default:;
const AstSubRange span = listToSpan(
p, &p->scratch.arr[scratch_top.old_len], params_len);
- return addNode(
+ res = addNode(
&p->nodes,
(AstNodeItem) {
.tag = comma ? AST_NODE_CALL_COMMA : AST_NODE_CALL,
@@ -745,6 +747,7 @@ static AstNodeIndex parseSuffixExpr(Parser* p) {
}, 2),
},
});
+ break;
}
}
}
diff --git a/parser_test.zig b/parser_test.zig
@@ -1668,3 +1668,133 @@ test "zig fmt: remove empty lines at start/end of block" {
\\
);
}
+
+test "zig fmt: multiline string with backslash at end of line" {
+ try testCanonical(
+ \\comptime {
+ \\ err(
+ \\ \\\
+ \\ );
+ \\}
+ \\
+ );
+}
+
+test "zig fmt: multiline string parameter in fn call with trailing comma" {
+ try testCanonical(
+ \\fn foo() void {
+ \\ try stdout.print(
+ \\ \\ZIG_CMAKE_BINARY_DIR {s}
+ \\ \\ZIG_C_HEADER_FILES {s}
+ \\ \\ZIG_DIA_GUIDS_LIB {s}
+ \\ \\
+ \\ ,
+ \\ std.mem.sliceTo(c.ZIG_CMAKE_BINARY_DIR, 0),
+ \\ std.mem.sliceTo(c.ZIG_CXX_COMPILER, 0),
+ \\ std.mem.sliceTo(c.ZIG_DIA_GUIDS_LIB, 0),
+ \\ );
+ \\}
+ \\
+ );
+}
+
+test "zig fmt: trailing comma on fn call" {
+ try testCanonical(
+ \\comptime {
+ \\ var module = try Module.create(
+ \\ allocator,
+ \\ zig_lib_dir,
+ \\ full_cache_dir,
+ \\ );
+ \\}
+ \\
+ );
+}
+
+test "zig fmt: multi line arguments without last comma" {
+ try testTransform(
+ \\pub fn foo(
+ \\ a: usize,
+ \\ b: usize,
+ \\ c: usize,
+ \\ d: usize
+ \\) usize {
+ \\ return a + b + c + d;
+ \\}
+ \\
+ ,
+ \\pub fn foo(a: usize, b: usize, c: usize, d: usize) usize {
+ \\ return a + b + c + d;
+ \\}
+ \\
+ );
+}
+
+test "zig fmt: empty block with only comment" {
+ try testCanonical(
+ \\comptime {
+ \\ {
+ \\ // comment
+ \\ }
+ \\}
+ \\
+ );
+}
+
+test "zig fmt: trailing commas on struct decl" {
+ try testTransform(
+ \\const RoundParam = struct {
+ \\ k: usize, s: u32, t: u32
+ \\};
+ \\const RoundParam = struct {
+ \\ k: usize, s: u32, t: u32,
+ \\};
+ ,
+ \\const RoundParam = struct { k: usize, s: u32, t: u32 };
+ \\const RoundParam = struct {
+ \\ k: usize,
+ \\ s: u32,
+ \\ t: u32,
+ \\};
+ \\
+ );
+}
+
+test "zig fmt: extra newlines at the end" {
+ try testTransform(
+ \\const a = b;
+ \\
+ \\
+ \\
+ ,
+ \\const a = b;
+ \\
+ );
+}
+
+test "zig fmt: nested struct literal with one item" {
+ try testCanonical(
+ \\const a = foo{
+ \\ .item = bar{ .a = b },
+ \\};
+ \\
+ );
+}
+
+test "zig fmt: if-else with comment before else" {
+ try testCanonical(
+ \\comptime {
+ \\ // cexp(finite|nan +- i inf|nan) = nan + i nan
+ \\ if ((hx & 0x7fffffff) != 0x7f800000) {
+ \\ return Complex(f32).init(y - y, y - y);
+ \\ } // cexp(-inf +- i inf|nan) = 0 + i0
+ \\ else if (hx & 0x80000000 != 0) {
+ \\ return Complex(f32).init(0, 0);
+ \\ } // cexp(+inf +- i inf|nan) = inf + i nan
+ \\ else {
+ \\ return Complex(f32).init(x, y - y);
+ \\ }
+ \\}
+ \\
+ );
+}