From 57cec38e6144754fcd15266100974a7cf0059570 Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Sun, 7 Feb 2021 23:14:33 +0100 Subject: [PATCH] std/zig/ast: fix Tree.lastToken() for blocks The fact that blocks may end in a semicolon but this semicolon is not counted by recursive lastToken() evaluation on the sub expression causes off-by-one errors for lastToken() on blocks currently. To fix this, introduce BlockSemicolon and BlockTwoSemicolon following the pattern used for trailing commas in e.g. builtin function arguments. --- lib/std/zig/ast.zig | 18 ++++++++++++------ lib/std/zig/parse.zig | 9 ++++++--- lib/std/zig/parser_test.zig | 18 ++++++++++++++++++ lib/std/zig/render.zig | 8 ++++++-- 4 files changed, 42 insertions(+), 11 deletions(-) diff --git a/lib/std/zig/ast.zig b/lib/std/zig/ast.zig index 815d783234..d5857c9d53 100644 --- a/lib/std/zig/ast.zig +++ b/lib/std/zig/ast.zig @@ -357,7 +357,9 @@ pub const Tree = struct { }, .Block, + .BlockSemicolon, .BlockTwo, + .BlockTwoSemicolon, => { // Look for a label. const lbrace = main_tokens[n]; @@ -552,18 +554,17 @@ pub const Tree = struct { .TaggedUnion, .BuiltinCall, => { + assert(datas[n].rhs - datas[n].lhs > 0); end_offset += 1; // for the rbrace - if (datas[n].rhs - datas[n].lhs == 0) { - return main_tokens[n] + end_offset; - } n = tree.extra_data[datas[n].rhs - 1]; // last statement }, + .BlockSemicolon, .ContainerDeclComma, .TaggedUnionComma, .BuiltinCallComma, => { assert(datas[n].rhs - datas[n].lhs > 0); - end_offset += 2; // for the comma + rbrace/rparen + end_offset += 2; // for the comma/semicolon + rbrace/rparen n = tree.extra_data[datas[n].rhs - 1]; // last member }, .CallOne, @@ -594,11 +595,12 @@ pub const Tree = struct { }, .ArrayInitDotTwoComma, .BuiltinCallTwoComma, + .BlockTwoSemicolon, .StructInitDotTwoComma, .ContainerDeclTwoComma, .TaggedUnionTwoComma, => { - end_offset += 2; // for the comma + rbrace/rparen + end_offset += 2; // for the comma/semicolon + rbrace/rparen if (datas[n].rhs != 0) { n = datas[n].rhs; } else if (datas[n].lhs != 0) { @@ -2137,12 +2139,16 @@ pub const Node = struct { Comptime, /// `nosuspend lhs`. rhs unused. Nosuspend, - /// `{lhs; rhs;}`. rhs or lhs can be omitted. + /// `{lhs rhs}`. rhs or lhs can be omitted. /// main_token points at the lbrace. BlockTwo, + /// Same as BlockTwo but there is known to be a semicolon before the rbrace. + BlockTwoSemicolon, /// `{}`. `sub_list[lhs..rhs]`. /// main_token points at the lbrace. Block, + /// Same as BlockTwo but there is known to be a semicolon before the rbrace. + BlockSemicolon, /// `asm(lhs)`. rhs unused. AsmSimple, /// `asm(lhs, a)`. `sub_range_list[rhs]`. diff --git a/lib/std/zig/parse.zig b/lib/std/zig/parse.zig index 31182c4952..96d9a0dea6 100644 --- a/lib/std/zig/parse.zig +++ b/lib/std/zig/parse.zig @@ -1984,8 +1984,9 @@ const Parser = struct { const stmt_one = try p.expectStatementRecoverable(); if (p.eatToken(.RBrace)) |_| { + const semicolon = p.token_tags[p.tok_i - 2] == .Semicolon; return p.addNode(.{ - .tag = .BlockTwo, + .tag = if (semicolon) .BlockTwoSemicolon else .BlockTwo, .main_token = lbrace, .data = .{ .lhs = stmt_one, @@ -1995,8 +1996,9 @@ const Parser = struct { } const stmt_two = try p.expectStatementRecoverable(); if (p.eatToken(.RBrace)) |_| { + const semicolon = p.token_tags[p.tok_i - 2] == .Semicolon; return p.addNode(.{ - .tag = .BlockTwo, + .tag = if (semicolon) .BlockTwoSemicolon else .BlockTwo, .main_token = lbrace, .data = .{ .lhs = stmt_one, @@ -2017,9 +2019,10 @@ const Parser = struct { if (p.token_tags[p.tok_i] == .RBrace) break; } _ = try p.expectToken(.RBrace); + const semicolon = p.token_tags[p.tok_i - 2] == .Semicolon; const statements_span = try p.listToSpan(statements.items); return p.addNode(.{ - .tag = .Block, + .tag = if (semicolon) .BlockSemicolon else .Block, .main_token = lbrace, .data = .{ .lhs = statements_span.start, diff --git a/lib/std/zig/parser_test.zig b/lib/std/zig/parser_test.zig index dec7b09127..5d95a2b12f 100644 --- a/lib/std/zig/parser_test.zig +++ b/lib/std/zig/parser_test.zig @@ -655,6 +655,24 @@ test "zig fmt: slices with spaces in bounds" { ); } +test "zig fmt: block in slice expression" { + try testCanonical( + \\const a = b[{ + \\ _ = x; + \\}..]; + \\const c = d[0..{ + \\ _ = x; + \\ _ = y; + \\}]; + \\const e = f[0..1 :{ + \\ _ = x; + \\ _ = y; + \\ _ = z; + \\}]; + \\ + ); +} + //test "zig fmt: async function" { // try testCanonical( // \\pub const Server = struct { diff --git a/lib/std/zig/render.zig b/lib/std/zig/render.zig index 1c18d15219..ce3ce7055a 100644 --- a/lib/std/zig/render.zig +++ b/lib/std/zig/render.zig @@ -202,7 +202,9 @@ fn renderExpression(ais: *Ais, tree: ast.Tree, node: ast.Node.Index, space: Spac // } // return renderToken(ais, tree, any_type.token, space); //}, - .BlockTwo => { + .BlockTwo, + .BlockTwoSemicolon, + => { const statements = [2]ast.Node.Index{ datas[node].lhs, datas[node].rhs }; if (datas[node].lhs == 0) { return renderBlock(ais, tree, main_tokens[node], statements[0..0], space); @@ -212,7 +214,9 @@ fn renderExpression(ais: *Ais, tree: ast.Tree, node: ast.Node.Index, space: Spac return renderBlock(ais, tree, main_tokens[node], statements[0..2], space); } }, - .Block => { + .Block, + .BlockSemicolon, + => { const lbrace = main_tokens[node]; const statements = tree.extra_data[datas[node].lhs..datas[node].rhs]; return renderBlock(ais, tree, main_tokens[node], statements, space);