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.
This commit is contained in:
Isaac Freund
2021-02-07 23:14:33 +01:00
committed by Andrew Kelley
parent 0e38362d24
commit 57cec38e61
4 changed files with 42 additions and 11 deletions

View File

@@ -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]`.

View File

@@ -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,

View File

@@ -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 {

View File

@@ -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);