diff --git a/parser.c b/parser.c index af27234f64..236486971b 100644 --- a/parser.c +++ b/parser.c @@ -553,9 +553,39 @@ static AstNodeIndex parsePrimaryTypeExpr(Parser* p) { } return 0; // tcc case TOKEN_KEYWORD_ERROR: - fprintf(stderr, "parsePrimaryTypeExpr does not support %s\n", - tokenizerGetTagString(tok)); - exit(1); + switch (p->token_tags[p->tok_i + 1]) { + case TOKEN_PERIOD: { + const AstTokenIndex error_token = nextToken(p); + const AstTokenIndex dot = nextToken(p); + const AstTokenIndex value = expectToken(p, TOKEN_IDENTIFIER); + return addNode(&p->nodes, + (AstNodeItem) { + .tag = AST_NODE_ERROR_VALUE, + .main_token = error_token, + .data = { .lhs = dot, .rhs = value }, + }); + } + case TOKEN_L_BRACE: { + const AstTokenIndex error_token = nextToken(p); + nextToken(p); // consume { + while (p->token_tags[p->tok_i] != TOKEN_R_BRACE) + p->tok_i++; + const AstTokenIndex rbrace = nextToken(p); + return addNode(&p->nodes, + (AstNodeItem) { + .tag = AST_NODE_ERROR_SET_DECL, + .main_token = error_token, + .data = { .lhs = 0, .rhs = rbrace }, + }); + } + default: + return addNode(&p->nodes, + (AstNodeItem) { + .tag = AST_NODE_IDENTIFIER, + .main_token = nextToken(p), + .data = {}, + }); + } case TOKEN_L_PAREN: { const AstTokenIndex lparen = nextToken(p); const AstNodeIndex inner = expectExpr(p); @@ -2241,9 +2271,16 @@ static AstNodeIndex expectStatement(Parser* p, bool allow_defer_var) { .data = { .lhs = block, .rhs = 0 }, }); } - // comptime var decl or expression + // comptime var decl or expression — the result needs to be + // wrapped in a comptime node if (allow_defer_var) { - return expectVarDeclExprStatement(p); + const AstNodeIndex inner = expectVarDeclExprStatement(p); + return addNode(&p->nodes, + (AstNodeItem) { + .tag = AST_NODE_COMPTIME, + .main_token = comptime_token, + .data = { .lhs = inner, .rhs = 0 }, + }); } fprintf( stderr, "expectStatement: comptime keyword not supported here\n"); diff --git a/parser_test.zig b/parser_test.zig index f22c8bc20d..6a81d7447f 100644 --- a/parser_test.zig +++ b/parser_test.zig @@ -2949,6 +2949,208 @@ test "zig fmt: decimal float literals with underscore separators" { ); } +test "zig fmt: comment after if before another if" { + try testCanonical( + \\test "aoeu" { + \\ // comment + \\ if (x) { + \\ bar(); + \\ } + \\} + \\ + \\test "aoeu" { + \\ if (x) { + \\ foo(); + \\ } + \\ // comment + \\ if (x) { + \\ bar(); + \\ } + \\} + \\ + ); +} + +test "zig fmt: line comment between if block and else keyword" { + try testCanonical( + \\test "aoeu" { + \\ // 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 + \\ // another comment + \\ else { + \\ return Complex(f32).init(x, y - y); + \\ } + \\} + \\ + ); +} + +test "zig fmt: same line comments in expression" { + try testCanonical( + \\test "aoeu" { + \\ const x = ( // a + \\ 0 // b + \\ ); // c + \\} + \\ + ); +} + +test "zig fmt: add comma on last switch prong" { + try testTransform( + \\test "aoeu" { + \\switch (self.init_arg_expr) { + \\ InitArg.Type => |t| { }, + \\ InitArg.None, + \\ InitArg.Enum => { } + \\} + \\ switch (self.init_arg_expr) { + \\ InitArg.Type => |t| { }, + \\ InitArg.None, + \\ InitArg.Enum => { }//line comment + \\ } + \\} + , + \\test "aoeu" { + \\ switch (self.init_arg_expr) { + \\ InitArg.Type => |t| {}, + \\ InitArg.None, InitArg.Enum => {}, + \\ } + \\ switch (self.init_arg_expr) { + \\ InitArg.Type => |t| {}, + \\ InitArg.None, InitArg.Enum => {}, //line comment + \\ } + \\} + \\ + ); +} + +test "zig fmt: same-line comment after a statement" { + try testCanonical( + \\test "" { + \\ a = b; + \\ debug.assert(H.digest_size <= H.block_size); // HMAC makes this assumption + \\ a = b; + \\} + \\ + ); +} + +test "zig fmt: same-line comment after var decl in struct" { + try testCanonical( + \\pub const vfs_cap_data = extern struct { + \\ const Data = struct {}; // when on disk. + \\}; + \\ + ); +} + +test "zig fmt: same-line comment after field decl" { + try testCanonical( + \\pub const dirent = extern struct { + \\ d_name: u8, + \\ d_name: u8, // comment 1 + \\ d_name: u8, + \\ d_name: u8, // comment 2 + \\ d_name: u8, + \\}; + \\ + ); +} + +test "zig fmt: same-line comment after switch prong" { + try testCanonical( + \\test "" { + \\ switch (err) { + \\ error.PathAlreadyExists => {}, // comment 2 + \\ else => return err, // comment 1 + \\ } + \\} + \\ + ); +} + +test "zig fmt: same-line comment after non-block if expression" { + try testCanonical( + \\comptime { + \\ if (sr > n_uword_bits - 1) // d > r + \\ return 0; + \\} + \\ + ); +} + +test "zig fmt: same-line comment on comptime expression" { + try testCanonical( + \\test "" { + \\ comptime assert(@typeInfo(T) == .int); // must pass an integer to absInt + \\} + \\ + ); +} + +test "zig fmt: switch with empty body" { + try testCanonical( + \\test "" { + \\ foo() catch |err| switch (err) {}; + \\} + \\ + ); +} + +test "zig fmt: line comments in struct initializer" { + try testCanonical( + \\fn foo() void { + \\ return Self{ + \\ .a = b, + \\ + \\ // Initialize these two fields to buffer_size so that + \\ // in `readFn` we treat the state as being able to read + \\ .start_index = buffer_size, + \\ .end_index = buffer_size, + \\ + \\ // middle + \\ + \\ .a = b, + \\ + \\ // end + \\ }; + \\} + \\ + ); +} + +test "zig fmt: first line comment in struct initializer" { + try testCanonical( + \\pub fn acquire(self: *Self) HeldLock { + \\ return HeldLock{ + \\ // guaranteed allocation elision + \\ .held = self.lock.acquire(), + \\ .value = &self.private_data, + \\ }; + \\} + \\ + ); +} + +test "zig fmt: doc comments before struct field" { + try testCanonical( + \\pub const Allocator = struct { + \\ /// Allocate byte_count bytes and return them in a slice, with the + \\ /// slice's pointer aligned at least to alignment bytes. + \\ allocFn: fn () void, + \\}; + \\ + ); +} + test "Ast header smoke test" { try std.testing.expectEqual(zigNode(c.AST_NODE_IF), Ast.Node.Tag.@"if"); }