diff --git a/lib/std/zig/Ast.zig b/lib/std/zig/Ast.zig index 016cefb255..a5ab8a2d55 100644 --- a/lib/std/zig/Ast.zig +++ b/lib/std/zig/Ast.zig @@ -17,6 +17,7 @@ const testing = std.testing; const mem = std.mem; const Token = std.zig.Token; const Ast = @This(); +const private_render = @import("./render.zig"); pub const TokenIndex = u32; pub const ByteOffset = u32; @@ -60,8 +61,14 @@ pub fn render(tree: Ast, gpa: mem.Allocator) RenderError![]u8 { return buffer.toOwnedSlice(); } +pub const Fixups = private_render.Fixups; + pub fn renderToArrayList(tree: Ast, buffer: *std.ArrayList(u8)) RenderError!void { - return @import("./render.zig").renderTree(buffer, tree); + return private_render.renderTree(buffer, tree, .{}); +} + +pub fn renderWithFixups(tree: Ast, buffer: *std.ArrayList(u8), fixups: Fixups) RenderError!void { + return private_render.renderTree(buffer, tree, fixups); } /// Returns an extra offset for column and byte offset of errors that diff --git a/lib/std/zig/render.zig b/lib/std/zig/render.zig index bc59ddc279..34c65af263 100644 --- a/lib/std/zig/render.zig +++ b/lib/std/zig/render.zig @@ -13,44 +13,73 @@ pub const Error = Ast.RenderError; const Ais = AutoIndentingStream(std.ArrayList(u8).Writer); -pub fn renderTree(buffer: *std.ArrayList(u8), tree: Ast) Error!void { +pub const Fixups = struct { + /// The key is the mut token (`var`/`const`) of the variable declaration + /// that should have a `_ = foo;` inserted afterwards. + unused_var_decls: std.AutoHashMapUnmanaged(Ast.TokenIndex, void) = .{}, + + pub fn count(f: Fixups) usize { + return f.unused_var_decls.count(); + } + + pub fn deinit(f: *Fixups, gpa: Allocator) void { + f.unused_var_decls.deinit(gpa); + f.* = undefined; + } +}; + +const Render = struct { + gpa: Allocator, + ais: *Ais, + tree: Ast, + fixups: Fixups, +}; + +pub fn renderTree(buffer: *std.ArrayList(u8), tree: Ast, fixups: Fixups) Error!void { assert(tree.errors.len == 0); // Cannot render an invalid tree. var auto_indenting_stream = Ais{ .indent_delta = indent_delta, .underlying_writer = buffer.writer(), }; - const ais = &auto_indenting_stream; + var r: Render = .{ + .gpa = buffer.allocator, + .ais = &auto_indenting_stream, + .tree = tree, + .fixups = fixups, + }; // Render all the line comments at the beginning of the file. const comment_end_loc = tree.tokens.items(.start)[0]; - _ = try renderComments(ais, tree, 0, comment_end_loc); + _ = try renderComments(r, 0, comment_end_loc); if (tree.tokens.items(.tag)[0] == .container_doc_comment) { - try renderContainerDocComments(ais, tree, 0); + try renderContainerDocComments(r, 0); } - try renderMembers(buffer.allocator, ais, tree, tree.rootDecls()); + try renderMembers(r, tree.rootDecls()); - if (ais.disabled_offset) |disabled_offset| { - try writeFixingWhitespace(ais.underlying_writer, tree.source[disabled_offset..]); + if (r.ais.disabled_offset) |disabled_offset| { + try writeFixingWhitespace(r.ais.underlying_writer, tree.source[disabled_offset..]); } } /// Render all members in the given slice, keeping empty lines where appropriate -fn renderMembers(gpa: Allocator, ais: *Ais, tree: Ast, members: []const Ast.Node.Index) Error!void { +fn renderMembers(r: Render, members: []const Ast.Node.Index) Error!void { if (members.len == 0) return; - try renderMember(gpa, ais, tree, members[0], .newline); + try renderMember(r, members[0], .newline); for (members[1..]) |member| { - try renderExtraNewline(ais, tree, member); - try renderMember(gpa, ais, tree, member, .newline); + try renderExtraNewline(r, member); + try renderMember(r, member, .newline); } } -fn renderMember(gpa: Allocator, ais: *Ais, tree: Ast, decl: Ast.Node.Index, space: Space) Error!void { +fn renderMember(r: Render, decl: Ast.Node.Index, space: Space) Error!void { + const tree = r.tree; + const ais = r.ais; const token_tags = tree.tokens.items(.tag); const main_tokens = tree.nodes.items(.main_token); const datas = tree.nodes.items(.data); - try renderDocComments(ais, tree, tree.firstToken(decl)); + try renderDocComments(r, tree.firstToken(decl)); switch (tree.nodes.items(.tag)[decl]) { .fn_decl => { // Some examples: @@ -78,7 +107,7 @@ fn renderMember(gpa: Allocator, ais: *Ais, tree: Ast, decl: Ast.Node.Index, spac } } while (i < fn_token) : (i += 1) { - try renderToken(ais, tree, i, .space); + try renderToken(r, i, .space); } switch (tree.nodes.items(.tag)[fn_proto]) { .fn_proto_one, .fn_proto => { @@ -96,8 +125,8 @@ fn renderMember(gpa: Allocator, ais: *Ais, tree: Ast, decl: Ast.Node.Index, spac else => unreachable, } assert(datas[decl].rhs != 0); - try renderExpression(gpa, ais, tree, fn_proto, .space); - return renderExpression(gpa, ais, tree, datas[decl].rhs, space); + try renderExpression(r, fn_proto, .space); + return renderExpression(r, datas[decl].rhs, space); }, .fn_proto_simple, .fn_proto_multi, @@ -126,42 +155,42 @@ fn renderMember(gpa: Allocator, ais: *Ais, tree: Ast, decl: Ast.Node.Index, spac } } while (i < fn_token) : (i += 1) { - try renderToken(ais, tree, i, .space); + try renderToken(r, i, .space); } - try renderExpression(gpa, ais, tree, decl, .none); - return renderToken(ais, tree, tree.lastToken(decl) + 1, space); // semicolon + try renderExpression(r, decl, .none); + return renderToken(r, tree.lastToken(decl) + 1, space); // semicolon }, .@"usingnamespace" => { const main_token = main_tokens[decl]; const expr = datas[decl].lhs; if (main_token > 0 and token_tags[main_token - 1] == .keyword_pub) { - try renderToken(ais, tree, main_token - 1, .space); // pub + try renderToken(r, main_token - 1, .space); // pub } - try renderToken(ais, tree, main_token, .space); // usingnamespace - try renderExpression(gpa, ais, tree, expr, .none); - return renderToken(ais, tree, tree.lastToken(expr) + 1, space); // ; + try renderToken(r, main_token, .space); // usingnamespace + try renderExpression(r, expr, .none); + return renderToken(r, tree.lastToken(expr) + 1, space); // ; }, - .global_var_decl => return renderVarDecl(gpa, ais, tree, tree.globalVarDecl(decl)), - .local_var_decl => return renderVarDecl(gpa, ais, tree, tree.localVarDecl(decl)), - .simple_var_decl => return renderVarDecl(gpa, ais, tree, tree.simpleVarDecl(decl)), - .aligned_var_decl => return renderVarDecl(gpa, ais, tree, tree.alignedVarDecl(decl)), + .global_var_decl => return renderVarDecl(r, tree.globalVarDecl(decl)), + .local_var_decl => return renderVarDecl(r, tree.localVarDecl(decl)), + .simple_var_decl => return renderVarDecl(r, tree.simpleVarDecl(decl)), + .aligned_var_decl => return renderVarDecl(r, tree.alignedVarDecl(decl)), .test_decl => { const test_token = main_tokens[decl]; - try renderToken(ais, tree, test_token, .space); + try renderToken(r, test_token, .space); const test_name_tag = token_tags[test_token + 1]; if (test_name_tag == .string_literal or test_name_tag == .identifier) { - try renderToken(ais, tree, test_token + 1, .space); + try renderToken(r, test_token + 1, .space); } - try renderExpression(gpa, ais, tree, datas[decl].rhs, space); + try renderExpression(r, datas[decl].rhs, space); }, - .container_field_init => return renderContainerField(gpa, ais, tree, tree.containerFieldInit(decl), space), - .container_field_align => return renderContainerField(gpa, ais, tree, tree.containerFieldAlign(decl), space), - .container_field => return renderContainerField(gpa, ais, tree, tree.containerField(decl), space), - .@"comptime" => return renderExpression(gpa, ais, tree, decl, space), + .container_field_init => return renderContainerField(r, tree.containerFieldInit(decl), space), + .container_field_align => return renderContainerField(r, tree.containerFieldAlign(decl), space), + .container_field => return renderContainerField(r, tree.containerField(decl), space), + .@"comptime" => return renderExpression(r, decl, space), .root => unreachable, else => unreachable, @@ -169,16 +198,18 @@ fn renderMember(gpa: Allocator, ais: *Ais, tree: Ast, decl: Ast.Node.Index, spac } /// Render all expressions in the slice, keeping empty lines where appropriate -fn renderExpressions(gpa: Allocator, ais: *Ais, tree: Ast, expressions: []const Ast.Node.Index, space: Space) Error!void { +fn renderExpressions(r: Render, expressions: []const Ast.Node.Index, space: Space) Error!void { if (expressions.len == 0) return; - try renderExpression(gpa, ais, tree, expressions[0], space); + try renderExpression(r, expressions[0], space); for (expressions[1..]) |expression| { - try renderExtraNewline(ais, tree, expression); - try renderExpression(gpa, ais, tree, expression, space); + try renderExtraNewline(r, expression); + try renderExpression(r, expression, space); } } -fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index, space: Space) Error!void { +fn renderExpression(r: Render, node: Ast.Node.Index, space: Space) Error!void { + const tree = r.tree; + const ais = r.ais; const token_tags = tree.tokens.items(.tag); const main_tokens = tree.nodes.items(.main_token); const node_tags = tree.nodes.items(.tag); @@ -196,7 +227,7 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index, try ais.writer().writeAll(lexeme); } - return renderSpace(ais, tree, token_index, lexeme.len, space); + return renderSpace(r, token_index, lexeme.len, space); }, .integer_literal, @@ -205,29 +236,29 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index, .unreachable_literal, .anyframe_literal, .string_literal, - => return renderToken(ais, tree, main_tokens[node], space), + => return renderToken(r, main_tokens[node], space), .multiline_string_literal => { var locked_indents = ais.lockOneShotIndent(); try ais.maybeInsertNewline(); var i = datas[node].lhs; - while (i <= datas[node].rhs) : (i += 1) try renderToken(ais, tree, i, .newline); + while (i <= datas[node].rhs) : (i += 1) try renderToken(r, i, .newline); while (locked_indents > 0) : (locked_indents -= 1) ais.popIndent(); switch (space) { .none, .space, .newline, .skip => {}, - .semicolon => if (token_tags[i] == .semicolon) try renderToken(ais, tree, i, .newline), - .comma => if (token_tags[i] == .comma) try renderToken(ais, tree, i, .newline), - .comma_space => if (token_tags[i] == .comma) try renderToken(ais, tree, i, .space), + .semicolon => if (token_tags[i] == .semicolon) try renderToken(r, i, .newline), + .comma => if (token_tags[i] == .comma) try renderToken(r, i, .newline), + .comma_space => if (token_tags[i] == .comma) try renderToken(r, i, .space), } }, .error_value => { - try renderToken(ais, tree, main_tokens[node], .none); - try renderToken(ais, tree, main_tokens[node] + 1, .none); - return renderToken(ais, tree, main_tokens[node] + 2, space); + try renderToken(r, main_tokens[node], .none); + try renderToken(r, main_tokens[node] + 1, .none); + return renderToken(r, main_tokens[node] + 2, space); }, .block_two, @@ -235,18 +266,18 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index, => { const statements = [2]Ast.Node.Index{ datas[node].lhs, datas[node].rhs }; if (datas[node].lhs == 0) { - return renderBlock(gpa, ais, tree, node, statements[0..0], space); + return renderBlock(r, node, statements[0..0], space); } else if (datas[node].rhs == 0) { - return renderBlock(gpa, ais, tree, node, statements[0..1], space); + return renderBlock(r, node, statements[0..1], space); } else { - return renderBlock(gpa, ais, tree, node, statements[0..2], space); + return renderBlock(r, node, statements[0..2], space); } }, .block, .block_semicolon, => { const statements = tree.extra_data[datas[node].lhs..datas[node].rhs]; - return renderBlock(gpa, ais, tree, node, statements, space); + return renderBlock(r, node, statements, space); }, .@"errdefer" => { @@ -254,33 +285,33 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index, const payload_token = datas[node].lhs; const expr = datas[node].rhs; - try renderToken(ais, tree, defer_token, .space); + try renderToken(r, defer_token, .space); if (payload_token != 0) { - try renderToken(ais, tree, payload_token - 1, .none); // | - try renderToken(ais, tree, payload_token, .none); // identifier - try renderToken(ais, tree, payload_token + 1, .space); // | + try renderToken(r, payload_token - 1, .none); // | + try renderToken(r, payload_token, .none); // identifier + try renderToken(r, payload_token + 1, .space); // | } - return renderExpression(gpa, ais, tree, expr, space); + return renderExpression(r, expr, space); }, .@"defer" => { const defer_token = main_tokens[node]; const expr = datas[node].rhs; - try renderToken(ais, tree, defer_token, .space); - return renderExpression(gpa, ais, tree, expr, space); + try renderToken(r, defer_token, .space); + return renderExpression(r, expr, space); }, .@"comptime", .@"nosuspend" => { const comptime_token = main_tokens[node]; const block = datas[node].lhs; - try renderToken(ais, tree, comptime_token, .space); - return renderExpression(gpa, ais, tree, block, space); + try renderToken(r, comptime_token, .space); + return renderExpression(r, block, space); }, .@"suspend" => { const suspend_token = main_tokens[node]; const body = datas[node].lhs; - try renderToken(ais, tree, suspend_token, .space); - return renderExpression(gpa, ais, tree, body, space); + try renderToken(r, suspend_token, .space); + return renderExpression(r, body, space); }, .@"catch" => { @@ -290,27 +321,27 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index, const same_line = tree.tokensOnSameLine(main_token, fallback_first); const after_op_space = if (same_line) Space.space else Space.newline; - try renderExpression(gpa, ais, tree, datas[node].lhs, .space); // target + try renderExpression(r, datas[node].lhs, .space); // target if (token_tags[fallback_first - 1] == .pipe) { - try renderToken(ais, tree, main_token, .space); // catch keyword - try renderToken(ais, tree, main_token + 1, .none); // pipe - try renderToken(ais, tree, main_token + 2, .none); // payload identifier - try renderToken(ais, tree, main_token + 3, after_op_space); // pipe + try renderToken(r, main_token, .space); // catch keyword + try renderToken(r, main_token + 1, .none); // pipe + try renderToken(r, main_token + 2, .none); // payload identifier + try renderToken(r, main_token + 3, after_op_space); // pipe } else { assert(token_tags[fallback_first - 1] == .keyword_catch); - try renderToken(ais, tree, main_token, after_op_space); // catch keyword + try renderToken(r, main_token, after_op_space); // catch keyword } ais.pushIndentOneShot(); - try renderExpression(gpa, ais, tree, datas[node].rhs, space); // fallback + try renderExpression(r, datas[node].rhs, space); // fallback }, .field_access => { const main_token = main_tokens[node]; const field_access = datas[node]; - try renderExpression(gpa, ais, tree, field_access.lhs, .none); + try renderExpression(r, field_access.lhs, .none); // Allow a line break between the lhs and the dot if the lhs and rhs // are on different lines. @@ -321,7 +352,7 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index, ais.pushIndentOneShot(); } - try renderToken(ais, tree, main_token, .none); + try renderToken(r, main_token, .none); // This check ensures that zag() is indented in the following example: // const x = foo @@ -332,16 +363,16 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index, ais.pushIndentOneShot(); } - return renderToken(ais, tree, field_access.rhs, space); + return renderToken(r, field_access.rhs, space); }, .error_union, .switch_range, => { const infix = datas[node]; - try renderExpression(gpa, ais, tree, infix.lhs, .none); - try renderToken(ais, tree, main_tokens[node], .none); - return renderExpression(gpa, ais, tree, infix.rhs, space); + try renderExpression(r, infix.lhs, .none); + try renderToken(r, main_tokens[node], .none); + return renderExpression(r, infix.rhs, space); }, .add, @@ -393,17 +424,17 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index, .@"orelse", => { const infix = datas[node]; - try renderExpression(gpa, ais, tree, infix.lhs, .space); + try renderExpression(r, infix.lhs, .space); const op_token = main_tokens[node]; if (tree.tokensOnSameLine(op_token, op_token + 1)) { - try renderToken(ais, tree, op_token, .space); + try renderToken(r, op_token, .space); } else { ais.pushIndent(); - try renderToken(ais, tree, op_token, .newline); + try renderToken(r, op_token, .newline); ais.popIndent(); } ais.pushIndentOneShot(); - return renderExpression(gpa, ais, tree, infix.rhs, space); + return renderExpression(r, infix.rhs, space); }, .bit_not, @@ -413,66 +444,66 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index, .optional_type, .address_of, => { - try renderToken(ais, tree, main_tokens[node], .none); - return renderExpression(gpa, ais, tree, datas[node].lhs, space); + try renderToken(r, main_tokens[node], .none); + return renderExpression(r, datas[node].lhs, space); }, .@"try", .@"resume", .@"await", => { - try renderToken(ais, tree, main_tokens[node], .space); - return renderExpression(gpa, ais, tree, datas[node].lhs, space); + try renderToken(r, main_tokens[node], .space); + return renderExpression(r, datas[node].lhs, space); }, - .array_type => return renderArrayType(gpa, ais, tree, tree.arrayType(node), space), - .array_type_sentinel => return renderArrayType(gpa, ais, tree, tree.arrayTypeSentinel(node), space), + .array_type => return renderArrayType(r, tree.arrayType(node), space), + .array_type_sentinel => return renderArrayType(r, tree.arrayTypeSentinel(node), space), - .ptr_type_aligned => return renderPtrType(gpa, ais, tree, tree.ptrTypeAligned(node), space), - .ptr_type_sentinel => return renderPtrType(gpa, ais, tree, tree.ptrTypeSentinel(node), space), - .ptr_type => return renderPtrType(gpa, ais, tree, tree.ptrType(node), space), - .ptr_type_bit_range => return renderPtrType(gpa, ais, tree, tree.ptrTypeBitRange(node), space), + .ptr_type_aligned => return renderPtrType(r, tree.ptrTypeAligned(node), space), + .ptr_type_sentinel => return renderPtrType(r, tree.ptrTypeSentinel(node), space), + .ptr_type => return renderPtrType(r, tree.ptrType(node), space), + .ptr_type_bit_range => return renderPtrType(r, tree.ptrTypeBitRange(node), space), .array_init_one, .array_init_one_comma => { var elements: [1]Ast.Node.Index = undefined; - return renderArrayInit(gpa, ais, tree, tree.arrayInitOne(&elements, node), space); + return renderArrayInit(r, tree.arrayInitOne(&elements, node), space); }, .array_init_dot_two, .array_init_dot_two_comma => { var elements: [2]Ast.Node.Index = undefined; - return renderArrayInit(gpa, ais, tree, tree.arrayInitDotTwo(&elements, node), space); + return renderArrayInit(r, tree.arrayInitDotTwo(&elements, node), space); }, .array_init_dot, .array_init_dot_comma, - => return renderArrayInit(gpa, ais, tree, tree.arrayInitDot(node), space), + => return renderArrayInit(r, tree.arrayInitDot(node), space), .array_init, .array_init_comma, - => return renderArrayInit(gpa, ais, tree, tree.arrayInit(node), space), + => return renderArrayInit(r, tree.arrayInit(node), space), .struct_init_one, .struct_init_one_comma => { var fields: [1]Ast.Node.Index = undefined; - return renderStructInit(gpa, ais, tree, node, tree.structInitOne(&fields, node), space); + return renderStructInit(r, node, tree.structInitOne(&fields, node), space); }, .struct_init_dot_two, .struct_init_dot_two_comma => { var fields: [2]Ast.Node.Index = undefined; - return renderStructInit(gpa, ais, tree, node, tree.structInitDotTwo(&fields, node), space); + return renderStructInit(r, node, tree.structInitDotTwo(&fields, node), space); }, .struct_init_dot, .struct_init_dot_comma, - => return renderStructInit(gpa, ais, tree, node, tree.structInitDot(node), space), + => return renderStructInit(r, node, tree.structInitDot(node), space), .struct_init, .struct_init_comma, - => return renderStructInit(gpa, ais, tree, node, tree.structInit(node), space), + => return renderStructInit(r, node, tree.structInit(node), space), .call_one, .call_one_comma, .async_call_one, .async_call_one_comma => { var params: [1]Ast.Node.Index = undefined; - return renderCall(gpa, ais, tree, tree.callOne(¶ms, node), space); + return renderCall(r, tree.callOne(¶ms, node), space); }, .call, .call_comma, .async_call, .async_call_comma, - => return renderCall(gpa, ais, tree, tree.callFull(node), space), + => return renderCall(r, tree.callFull(node), space), .array_access => { const suffix = datas[node]; @@ -480,27 +511,27 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index, const rbracket = tree.lastToken(suffix.rhs) + 1; const one_line = tree.tokensOnSameLine(lbracket, rbracket); const inner_space = if (one_line) Space.none else Space.newline; - try renderExpression(gpa, ais, tree, suffix.lhs, .none); + try renderExpression(r, suffix.lhs, .none); ais.pushIndentNextLine(); - try renderToken(ais, tree, lbracket, inner_space); // [ - try renderExpression(gpa, ais, tree, suffix.rhs, inner_space); + try renderToken(r, lbracket, inner_space); // [ + try renderExpression(r, suffix.rhs, inner_space); ais.popIndent(); - return renderToken(ais, tree, rbracket, space); // ] + return renderToken(r, rbracket, space); // ] }, - .slice_open => return renderSlice(gpa, ais, tree, node, tree.sliceOpen(node), space), - .slice => return renderSlice(gpa, ais, tree, node, tree.slice(node), space), - .slice_sentinel => return renderSlice(gpa, ais, tree, node, tree.sliceSentinel(node), space), + .slice_open => return renderSlice(r, node, tree.sliceOpen(node), space), + .slice => return renderSlice(r, node, tree.slice(node), space), + .slice_sentinel => return renderSlice(r, node, tree.sliceSentinel(node), space), .deref => { - try renderExpression(gpa, ais, tree, datas[node].lhs, .none); - return renderToken(ais, tree, main_tokens[node], space); + try renderExpression(r, datas[node].lhs, .none); + return renderToken(r, main_tokens[node], space); }, .unwrap_optional => { - try renderExpression(gpa, ais, tree, datas[node].lhs, .none); - try renderToken(ais, tree, main_tokens[node], .none); - return renderToken(ais, tree, datas[node].rhs, space); + try renderExpression(r, datas[node].lhs, .none); + try renderToken(r, main_tokens[node], .none); + return renderToken(r, datas[node].rhs, space); }, .@"break" => { @@ -508,19 +539,19 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index, const label_token = datas[node].lhs; const target = datas[node].rhs; if (label_token == 0 and target == 0) { - try renderToken(ais, tree, main_token, space); // break keyword + try renderToken(r, main_token, space); // break keyword } else if (label_token == 0 and target != 0) { - try renderToken(ais, tree, main_token, .space); // break keyword - try renderExpression(gpa, ais, tree, target, space); + try renderToken(r, main_token, .space); // break keyword + try renderExpression(r, target, space); } else if (label_token != 0 and target == 0) { - try renderToken(ais, tree, main_token, .space); // break keyword - try renderToken(ais, tree, label_token - 1, .none); // colon - try renderToken(ais, tree, label_token, space); // identifier + try renderToken(r, main_token, .space); // break keyword + try renderToken(r, label_token - 1, .none); // colon + try renderToken(r, label_token, space); // identifier } else if (label_token != 0 and target != 0) { - try renderToken(ais, tree, main_token, .space); // break keyword - try renderToken(ais, tree, label_token - 1, .none); // colon - try renderToken(ais, tree, label_token, .space); // identifier - try renderExpression(gpa, ais, tree, target, space); + try renderToken(r, main_token, .space); // break keyword + try renderToken(r, label_token - 1, .none); // colon + try renderToken(r, label_token, .space); // identifier + try renderExpression(r, target, space); } }, @@ -528,136 +559,136 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index, const main_token = main_tokens[node]; const label = datas[node].lhs; if (label != 0) { - try renderToken(ais, tree, main_token, .space); // continue - try renderToken(ais, tree, label - 1, .none); // : - return renderToken(ais, tree, label, space); // label + try renderToken(r, main_token, .space); // continue + try renderToken(r, label - 1, .none); // : + return renderToken(r, label, space); // label } else { - return renderToken(ais, tree, main_token, space); // continue + return renderToken(r, main_token, space); // continue } }, .@"return" => { if (datas[node].lhs != 0) { - try renderToken(ais, tree, main_tokens[node], .space); - try renderExpression(gpa, ais, tree, datas[node].lhs, space); + try renderToken(r, main_tokens[node], .space); + try renderExpression(r, datas[node].lhs, space); } else { - try renderToken(ais, tree, main_tokens[node], space); + try renderToken(r, main_tokens[node], space); } }, .grouped_expression => { - try renderToken(ais, tree, main_tokens[node], .none); // lparen + try renderToken(r, main_tokens[node], .none); // lparen ais.pushIndentOneShot(); - try renderExpression(gpa, ais, tree, datas[node].lhs, .none); - return renderToken(ais, tree, datas[node].rhs, space); // rparen + try renderExpression(r, datas[node].lhs, .none); + return renderToken(r, datas[node].rhs, space); // rparen }, .container_decl, .container_decl_trailing, - => return renderContainerDecl(gpa, ais, tree, node, tree.containerDecl(node), space), + => return renderContainerDecl(r, node, tree.containerDecl(node), space), .container_decl_two, .container_decl_two_trailing => { var buffer: [2]Ast.Node.Index = undefined; - return renderContainerDecl(gpa, ais, tree, node, tree.containerDeclTwo(&buffer, node), space); + return renderContainerDecl(r, node, tree.containerDeclTwo(&buffer, node), space); }, .container_decl_arg, .container_decl_arg_trailing, - => return renderContainerDecl(gpa, ais, tree, node, tree.containerDeclArg(node), space), + => return renderContainerDecl(r, node, tree.containerDeclArg(node), space), .tagged_union, .tagged_union_trailing, - => return renderContainerDecl(gpa, ais, tree, node, tree.taggedUnion(node), space), + => return renderContainerDecl(r, node, tree.taggedUnion(node), space), .tagged_union_two, .tagged_union_two_trailing => { var buffer: [2]Ast.Node.Index = undefined; - return renderContainerDecl(gpa, ais, tree, node, tree.taggedUnionTwo(&buffer, node), space); + return renderContainerDecl(r, node, tree.taggedUnionTwo(&buffer, node), space); }, .tagged_union_enum_tag, .tagged_union_enum_tag_trailing, - => return renderContainerDecl(gpa, ais, tree, node, tree.taggedUnionEnumTag(node), space), + => return renderContainerDecl(r, node, tree.taggedUnionEnumTag(node), space), .error_set_decl => { const error_token = main_tokens[node]; const lbrace = error_token + 1; const rbrace = datas[node].rhs; - try renderToken(ais, tree, error_token, .none); + try renderToken(r, error_token, .none); if (lbrace + 1 == rbrace) { // There is nothing between the braces so render condensed: `error{}` - try renderToken(ais, tree, lbrace, .none); - return renderToken(ais, tree, rbrace, space); + try renderToken(r, lbrace, .none); + return renderToken(r, rbrace, space); } else if (lbrace + 2 == rbrace and token_tags[lbrace + 1] == .identifier) { // There is exactly one member and no trailing comma or // comments, so render without surrounding spaces: `error{Foo}` - try renderToken(ais, tree, lbrace, .none); - try renderToken(ais, tree, lbrace + 1, .none); // identifier - return renderToken(ais, tree, rbrace, space); + try renderToken(r, lbrace, .none); + try renderToken(r, lbrace + 1, .none); // identifier + return renderToken(r, rbrace, space); } else if (token_tags[rbrace - 1] == .comma) { // There is a trailing comma so render each member on a new line. ais.pushIndentNextLine(); - try renderToken(ais, tree, lbrace, .newline); + try renderToken(r, lbrace, .newline); var i = lbrace + 1; while (i < rbrace) : (i += 1) { - if (i > lbrace + 1) try renderExtraNewlineToken(ais, tree, i); + if (i > lbrace + 1) try renderExtraNewlineToken(r, i); switch (token_tags[i]) { - .doc_comment => try renderToken(ais, tree, i, .newline), - .identifier => try renderToken(ais, tree, i, .comma), + .doc_comment => try renderToken(r, i, .newline), + .identifier => try renderToken(r, i, .comma), .comma => {}, else => unreachable, } } ais.popIndent(); - return renderToken(ais, tree, rbrace, space); + return renderToken(r, rbrace, space); } else { // There is no trailing comma so render everything on one line. - try renderToken(ais, tree, lbrace, .space); + try renderToken(r, lbrace, .space); var i = lbrace + 1; while (i < rbrace) : (i += 1) { switch (token_tags[i]) { .doc_comment => unreachable, // TODO - .identifier => try renderToken(ais, tree, i, .comma_space), + .identifier => try renderToken(r, i, .comma_space), .comma => {}, else => unreachable, } } - return renderToken(ais, tree, rbrace, space); + return renderToken(r, rbrace, space); } }, .builtin_call_two, .builtin_call_two_comma => { if (datas[node].lhs == 0) { - return renderBuiltinCall(gpa, ais, tree, main_tokens[node], &.{}, space); + return renderBuiltinCall(r, main_tokens[node], &.{}, space); } else if (datas[node].rhs == 0) { - return renderBuiltinCall(gpa, ais, tree, main_tokens[node], &.{datas[node].lhs}, space); + return renderBuiltinCall(r, main_tokens[node], &.{datas[node].lhs}, space); } else { - return renderBuiltinCall(gpa, ais, tree, main_tokens[node], &.{ datas[node].lhs, datas[node].rhs }, space); + return renderBuiltinCall(r, main_tokens[node], &.{ datas[node].lhs, datas[node].rhs }, space); } }, .builtin_call, .builtin_call_comma => { const params = tree.extra_data[datas[node].lhs..datas[node].rhs]; - return renderBuiltinCall(gpa, ais, tree, main_tokens[node], params, space); + return renderBuiltinCall(r, main_tokens[node], params, space); }, .fn_proto_simple => { var params: [1]Ast.Node.Index = undefined; - return renderFnProto(gpa, ais, tree, tree.fnProtoSimple(¶ms, node), space); + return renderFnProto(r, tree.fnProtoSimple(¶ms, node), space); }, - .fn_proto_multi => return renderFnProto(gpa, ais, tree, tree.fnProtoMulti(node), space), + .fn_proto_multi => return renderFnProto(r, tree.fnProtoMulti(node), space), .fn_proto_one => { var params: [1]Ast.Node.Index = undefined; - return renderFnProto(gpa, ais, tree, tree.fnProtoOne(¶ms, node), space); + return renderFnProto(r, tree.fnProtoOne(¶ms, node), space); }, - .fn_proto => return renderFnProto(gpa, ais, tree, tree.fnProto(node), space), + .fn_proto => return renderFnProto(r, tree.fnProto(node), space), .anyframe_type => { const main_token = main_tokens[node]; if (datas[node].rhs != 0) { - try renderToken(ais, tree, main_token, .none); // anyframe - try renderToken(ais, tree, main_token + 1, .none); // -> - return renderExpression(gpa, ais, tree, datas[node].rhs, space); + try renderToken(r, main_token, .none); // anyframe + try renderToken(r, main_token + 1, .none); // -> + return renderExpression(r, datas[node].rhs, space); } else { - return renderToken(ais, tree, main_token, space); // anyframe + return renderToken(r, main_token, space); // anyframe } }, @@ -670,40 +701,40 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index, const cases = tree.extra_data[extra.start..extra.end]; const rparen = tree.lastToken(condition) + 1; - try renderToken(ais, tree, switch_token, .space); // switch keyword - try renderToken(ais, tree, switch_token + 1, .none); // lparen - try renderExpression(gpa, ais, tree, condition, .none); // condtion expression - try renderToken(ais, tree, rparen, .space); // rparen + try renderToken(r, switch_token, .space); // switch keyword + try renderToken(r, switch_token + 1, .none); // lparen + try renderExpression(r, condition, .none); // condtion expression + try renderToken(r, rparen, .space); // rparen ais.pushIndentNextLine(); if (cases.len == 0) { - try renderToken(ais, tree, rparen + 1, .none); // lbrace + try renderToken(r, rparen + 1, .none); // lbrace } else { - try renderToken(ais, tree, rparen + 1, .newline); // lbrace - try renderExpressions(gpa, ais, tree, cases, .comma); + try renderToken(r, rparen + 1, .newline); // lbrace + try renderExpressions(r, cases, .comma); } ais.popIndent(); - return renderToken(ais, tree, tree.lastToken(node), space); // rbrace + return renderToken(r, tree.lastToken(node), space); // rbrace }, - .switch_case_one => return renderSwitchCase(gpa, ais, tree, tree.switchCaseOne(node), space), - .switch_case => return renderSwitchCase(gpa, ais, tree, tree.switchCase(node), space), + .switch_case_one => return renderSwitchCase(r, tree.switchCaseOne(node), space), + .switch_case => return renderSwitchCase(r, tree.switchCase(node), space), - .while_simple => return renderWhile(gpa, ais, tree, tree.whileSimple(node), space), - .while_cont => return renderWhile(gpa, ais, tree, tree.whileCont(node), space), - .@"while" => return renderWhile(gpa, ais, tree, tree.whileFull(node), space), - .for_simple => return renderWhile(gpa, ais, tree, tree.forSimple(node), space), - .@"for" => return renderWhile(gpa, ais, tree, tree.forFull(node), space), + .while_simple => return renderWhile(r, tree.whileSimple(node), space), + .while_cont => return renderWhile(r, tree.whileCont(node), space), + .@"while" => return renderWhile(r, tree.whileFull(node), space), + .for_simple => return renderWhile(r, tree.forSimple(node), space), + .@"for" => return renderWhile(r, tree.forFull(node), space), - .if_simple => return renderIf(gpa, ais, tree, tree.ifSimple(node), space), - .@"if" => return renderIf(gpa, ais, tree, tree.ifFull(node), space), + .if_simple => return renderIf(r, tree.ifSimple(node), space), + .@"if" => return renderIf(r, tree.ifFull(node), space), - .asm_simple => return renderAsm(gpa, ais, tree, tree.asmSimple(node), space), - .@"asm" => return renderAsm(gpa, ais, tree, tree.asmFull(node), space), + .asm_simple => return renderAsm(r, tree.asmSimple(node), space), + .@"asm" => return renderAsm(r, tree.asmFull(node), space), .enum_literal => { - try renderToken(ais, tree, main_tokens[node] - 1, .none); // . - return renderToken(ais, tree, main_tokens[node], space); // name + try renderToken(r, main_tokens[node] - 1, .none); // . + return renderToken(r, main_tokens[node], space); // name }, .fn_decl => unreachable, @@ -723,34 +754,29 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index, } fn renderArrayType( - gpa: Allocator, - ais: *Ais, - tree: Ast, + r: Render, array_type: Ast.full.ArrayType, space: Space, ) Error!void { + const tree = r.tree; + const ais = r.ais; const rbracket = tree.firstToken(array_type.ast.elem_type) - 1; const one_line = tree.tokensOnSameLine(array_type.ast.lbracket, rbracket); const inner_space = if (one_line) Space.none else Space.newline; ais.pushIndentNextLine(); - try renderToken(ais, tree, array_type.ast.lbracket, inner_space); // lbracket - try renderExpression(gpa, ais, tree, array_type.ast.elem_count, inner_space); + try renderToken(r, array_type.ast.lbracket, inner_space); // lbracket + try renderExpression(r, array_type.ast.elem_count, inner_space); if (array_type.ast.sentinel != 0) { - try renderToken(ais, tree, tree.firstToken(array_type.ast.sentinel) - 1, inner_space); // colon - try renderExpression(gpa, ais, tree, array_type.ast.sentinel, inner_space); + try renderToken(r, tree.firstToken(array_type.ast.sentinel) - 1, inner_space); // colon + try renderExpression(r, array_type.ast.sentinel, inner_space); } ais.popIndent(); - try renderToken(ais, tree, rbracket, .none); // rbracket - return renderExpression(gpa, ais, tree, array_type.ast.elem_type, space); + try renderToken(r, rbracket, .none); // rbracket + return renderExpression(r, array_type.ast.elem_type, space); } -fn renderPtrType( - gpa: Allocator, - ais: *Ais, - tree: Ast, - ptr_type: Ast.full.PtrType, - space: Space, -) Error!void { +fn renderPtrType(r: Render, ptr_type: Ast.full.PtrType, space: Space) Error!void { + const tree = r.tree; switch (ptr_type.size) { .One => { // Since ** tokens exist and the same token is shared by two @@ -761,90 +787,89 @@ fn renderPtrType( if (tree.tokens.items(.tag)[ptr_type.ast.main_token] == .asterisk_asterisk and ptr_type.ast.main_token == tree.nodes.items(.main_token)[ptr_type.ast.child_type]) { - return renderExpression(gpa, ais, tree, ptr_type.ast.child_type, space); + return renderExpression(r, ptr_type.ast.child_type, space); } - try renderToken(ais, tree, ptr_type.ast.main_token, .none); // asterisk + try renderToken(r, ptr_type.ast.main_token, .none); // asterisk }, .Many => { if (ptr_type.ast.sentinel == 0) { - try renderToken(ais, tree, ptr_type.ast.main_token - 1, .none); // lbracket - try renderToken(ais, tree, ptr_type.ast.main_token, .none); // asterisk - try renderToken(ais, tree, ptr_type.ast.main_token + 1, .none); // rbracket + try renderToken(r, ptr_type.ast.main_token - 1, .none); // lbracket + try renderToken(r, ptr_type.ast.main_token, .none); // asterisk + try renderToken(r, ptr_type.ast.main_token + 1, .none); // rbracket } else { - try renderToken(ais, tree, ptr_type.ast.main_token - 1, .none); // lbracket - try renderToken(ais, tree, ptr_type.ast.main_token, .none); // asterisk - try renderToken(ais, tree, ptr_type.ast.main_token + 1, .none); // colon - try renderExpression(gpa, ais, tree, ptr_type.ast.sentinel, .none); - try renderToken(ais, tree, tree.lastToken(ptr_type.ast.sentinel) + 1, .none); // rbracket + try renderToken(r, ptr_type.ast.main_token - 1, .none); // lbracket + try renderToken(r, ptr_type.ast.main_token, .none); // asterisk + try renderToken(r, ptr_type.ast.main_token + 1, .none); // colon + try renderExpression(r, ptr_type.ast.sentinel, .none); + try renderToken(r, tree.lastToken(ptr_type.ast.sentinel) + 1, .none); // rbracket } }, .C => { - try renderToken(ais, tree, ptr_type.ast.main_token - 1, .none); // lbracket - try renderToken(ais, tree, ptr_type.ast.main_token, .none); // asterisk - try renderToken(ais, tree, ptr_type.ast.main_token + 1, .none); // c - try renderToken(ais, tree, ptr_type.ast.main_token + 2, .none); // rbracket + try renderToken(r, ptr_type.ast.main_token - 1, .none); // lbracket + try renderToken(r, ptr_type.ast.main_token, .none); // asterisk + try renderToken(r, ptr_type.ast.main_token + 1, .none); // c + try renderToken(r, ptr_type.ast.main_token + 2, .none); // rbracket }, .Slice => { if (ptr_type.ast.sentinel == 0) { - try renderToken(ais, tree, ptr_type.ast.main_token, .none); // lbracket - try renderToken(ais, tree, ptr_type.ast.main_token + 1, .none); // rbracket + try renderToken(r, ptr_type.ast.main_token, .none); // lbracket + try renderToken(r, ptr_type.ast.main_token + 1, .none); // rbracket } else { - try renderToken(ais, tree, ptr_type.ast.main_token, .none); // lbracket - try renderToken(ais, tree, ptr_type.ast.main_token + 1, .none); // colon - try renderExpression(gpa, ais, tree, ptr_type.ast.sentinel, .none); - try renderToken(ais, tree, tree.lastToken(ptr_type.ast.sentinel) + 1, .none); // rbracket + try renderToken(r, ptr_type.ast.main_token, .none); // lbracket + try renderToken(r, ptr_type.ast.main_token + 1, .none); // colon + try renderExpression(r, ptr_type.ast.sentinel, .none); + try renderToken(r, tree.lastToken(ptr_type.ast.sentinel) + 1, .none); // rbracket } }, } if (ptr_type.allowzero_token) |allowzero_token| { - try renderToken(ais, tree, allowzero_token, .space); + try renderToken(r, allowzero_token, .space); } if (ptr_type.ast.align_node != 0) { const align_first = tree.firstToken(ptr_type.ast.align_node); - try renderToken(ais, tree, align_first - 2, .none); // align - try renderToken(ais, tree, align_first - 1, .none); // lparen - try renderExpression(gpa, ais, tree, ptr_type.ast.align_node, .none); + try renderToken(r, align_first - 2, .none); // align + try renderToken(r, align_first - 1, .none); // lparen + try renderExpression(r, ptr_type.ast.align_node, .none); if (ptr_type.ast.bit_range_start != 0) { assert(ptr_type.ast.bit_range_end != 0); - try renderToken(ais, tree, tree.firstToken(ptr_type.ast.bit_range_start) - 1, .none); // colon - try renderExpression(gpa, ais, tree, ptr_type.ast.bit_range_start, .none); - try renderToken(ais, tree, tree.firstToken(ptr_type.ast.bit_range_end) - 1, .none); // colon - try renderExpression(gpa, ais, tree, ptr_type.ast.bit_range_end, .none); - try renderToken(ais, tree, tree.lastToken(ptr_type.ast.bit_range_end) + 1, .space); // rparen + try renderToken(r, tree.firstToken(ptr_type.ast.bit_range_start) - 1, .none); // colon + try renderExpression(r, ptr_type.ast.bit_range_start, .none); + try renderToken(r, tree.firstToken(ptr_type.ast.bit_range_end) - 1, .none); // colon + try renderExpression(r, ptr_type.ast.bit_range_end, .none); + try renderToken(r, tree.lastToken(ptr_type.ast.bit_range_end) + 1, .space); // rparen } else { - try renderToken(ais, tree, tree.lastToken(ptr_type.ast.align_node) + 1, .space); // rparen + try renderToken(r, tree.lastToken(ptr_type.ast.align_node) + 1, .space); // rparen } } if (ptr_type.ast.addrspace_node != 0) { const addrspace_first = tree.firstToken(ptr_type.ast.addrspace_node); - try renderToken(ais, tree, addrspace_first - 2, .none); // addrspace - try renderToken(ais, tree, addrspace_first - 1, .none); // lparen - try renderExpression(gpa, ais, tree, ptr_type.ast.addrspace_node, .none); - try renderToken(ais, tree, tree.lastToken(ptr_type.ast.addrspace_node) + 1, .space); // rparen + try renderToken(r, addrspace_first - 2, .none); // addrspace + try renderToken(r, addrspace_first - 1, .none); // lparen + try renderExpression(r, ptr_type.ast.addrspace_node, .none); + try renderToken(r, tree.lastToken(ptr_type.ast.addrspace_node) + 1, .space); // rparen } if (ptr_type.const_token) |const_token| { - try renderToken(ais, tree, const_token, .space); + try renderToken(r, const_token, .space); } if (ptr_type.volatile_token) |volatile_token| { - try renderToken(ais, tree, volatile_token, .space); + try renderToken(r, volatile_token, .space); } - try renderExpression(gpa, ais, tree, ptr_type.ast.child_type, space); + try renderExpression(r, ptr_type.ast.child_type, space); } fn renderSlice( - gpa: Allocator, - ais: *Ais, - tree: Ast, + r: Render, slice_node: Ast.Node.Index, slice: Ast.full.Slice, space: Space, ) Error!void { + const tree = r.tree; const node_tags = tree.nodes.items(.tag); const after_start_space_bool = nodeCausesSliceOpSpace(node_tags[slice.ast.start]) or if (slice.ast.end != 0) nodeCausesSliceOpSpace(node_tags[slice.ast.end]) else false; @@ -853,33 +878,32 @@ fn renderSlice( after_start_space else if (slice.ast.sentinel != 0) Space.space else Space.none; - try renderExpression(gpa, ais, tree, slice.ast.sliced, .none); - try renderToken(ais, tree, slice.ast.lbracket, .none); // lbracket + try renderExpression(r, slice.ast.sliced, .none); + try renderToken(r, slice.ast.lbracket, .none); // lbracket const start_last = tree.lastToken(slice.ast.start); - try renderExpression(gpa, ais, tree, slice.ast.start, after_start_space); - try renderToken(ais, tree, start_last + 1, after_dots_space); // ellipsis2 ("..") + try renderExpression(r, slice.ast.start, after_start_space); + try renderToken(r, start_last + 1, after_dots_space); // ellipsis2 ("..") if (slice.ast.end != 0) { const after_end_space = if (slice.ast.sentinel != 0) Space.space else Space.none; - try renderExpression(gpa, ais, tree, slice.ast.end, after_end_space); + try renderExpression(r, slice.ast.end, after_end_space); } if (slice.ast.sentinel != 0) { - try renderToken(ais, tree, tree.firstToken(slice.ast.sentinel) - 1, .none); // colon - try renderExpression(gpa, ais, tree, slice.ast.sentinel, .none); + try renderToken(r, tree.firstToken(slice.ast.sentinel) - 1, .none); // colon + try renderExpression(r, slice.ast.sentinel, .none); } - try renderToken(ais, tree, tree.lastToken(slice_node), space); // rbracket + try renderToken(r, tree.lastToken(slice_node), space); // rbracket } fn renderAsmOutput( - gpa: Allocator, - ais: *Ais, - tree: Ast, + r: Render, asm_output: Ast.Node.Index, space: Space, ) Error!void { + const tree = r.tree; const token_tags = tree.tokens.items(.tag); const node_tags = tree.nodes.items(.tag); const main_tokens = tree.nodes.items(.main_token); @@ -887,66 +911,79 @@ fn renderAsmOutput( assert(node_tags[asm_output] == .asm_output); const symbolic_name = main_tokens[asm_output]; - try renderToken(ais, tree, symbolic_name - 1, .none); // lbracket - try renderToken(ais, tree, symbolic_name, .none); // ident - try renderToken(ais, tree, symbolic_name + 1, .space); // rbracket - try renderToken(ais, tree, symbolic_name + 2, .space); // "constraint" - try renderToken(ais, tree, symbolic_name + 3, .none); // lparen + try renderToken(r, symbolic_name - 1, .none); // lbracket + try renderToken(r, symbolic_name, .none); // ident + try renderToken(r, symbolic_name + 1, .space); // rbracket + try renderToken(r, symbolic_name + 2, .space); // "constraint" + try renderToken(r, symbolic_name + 3, .none); // lparen if (token_tags[symbolic_name + 4] == .arrow) { - try renderToken(ais, tree, symbolic_name + 4, .space); // -> - try renderExpression(gpa, ais, tree, datas[asm_output].lhs, Space.none); - return renderToken(ais, tree, datas[asm_output].rhs, space); // rparen + try renderToken(r, symbolic_name + 4, .space); // -> + try renderExpression(r, datas[asm_output].lhs, Space.none); + return renderToken(r, datas[asm_output].rhs, space); // rparen } else { - try renderToken(ais, tree, symbolic_name + 4, .none); // ident - return renderToken(ais, tree, symbolic_name + 5, space); // rparen + try renderToken(r, symbolic_name + 4, .none); // ident + return renderToken(r, symbolic_name + 5, space); // rparen } } fn renderAsmInput( - gpa: Allocator, - ais: *Ais, - tree: Ast, + r: Render, asm_input: Ast.Node.Index, space: Space, ) Error!void { + const tree = r.tree; const node_tags = tree.nodes.items(.tag); const main_tokens = tree.nodes.items(.main_token); const datas = tree.nodes.items(.data); assert(node_tags[asm_input] == .asm_input); const symbolic_name = main_tokens[asm_input]; - try renderToken(ais, tree, symbolic_name - 1, .none); // lbracket - try renderToken(ais, tree, symbolic_name, .none); // ident - try renderToken(ais, tree, symbolic_name + 1, .space); // rbracket - try renderToken(ais, tree, symbolic_name + 2, .space); // "constraint" - try renderToken(ais, tree, symbolic_name + 3, .none); // lparen - try renderExpression(gpa, ais, tree, datas[asm_input].lhs, Space.none); - return renderToken(ais, tree, datas[asm_input].rhs, space); // rparen + try renderToken(r, symbolic_name - 1, .none); // lbracket + try renderToken(r, symbolic_name, .none); // ident + try renderToken(r, symbolic_name + 1, .space); // rbracket + try renderToken(r, symbolic_name + 2, .space); // "constraint" + try renderToken(r, symbolic_name + 3, .none); // lparen + try renderExpression(r, datas[asm_input].lhs, Space.none); + return renderToken(r, datas[asm_input].rhs, space); // rparen } -fn renderVarDecl(gpa: Allocator, ais: *Ais, tree: Ast, var_decl: Ast.full.VarDecl) Error!void { +fn renderVarDecl(r: Render, var_decl: Ast.full.VarDecl) Error!void { + try renderVarDeclWithoutFixups(r, var_decl); + if (r.fixups.unused_var_decls.contains(var_decl.ast.mut_token)) { + // Discard the variable like this: `_ = foo;` + const w = r.ais.writer(); + try w.writeAll("_ = "); + try w.writeAll(tokenSliceForRender(r.tree, var_decl.ast.mut_token + 1)); + try w.writeAll(";\n"); + } +} + +fn renderVarDeclWithoutFixups(r: Render, var_decl: Ast.full.VarDecl) Error!void { + const tree = r.tree; + const ais = r.ais; + if (var_decl.visib_token) |visib_token| { - try renderToken(ais, tree, visib_token, Space.space); // pub + try renderToken(r, visib_token, Space.space); // pub } if (var_decl.extern_export_token) |extern_export_token| { - try renderToken(ais, tree, extern_export_token, Space.space); // extern + try renderToken(r, extern_export_token, Space.space); // extern if (var_decl.lib_name) |lib_name| { - try renderToken(ais, tree, lib_name, Space.space); // "lib" + try renderToken(r, lib_name, Space.space); // "lib" } } if (var_decl.threadlocal_token) |thread_local_token| { - try renderToken(ais, tree, thread_local_token, Space.space); // threadlocal + try renderToken(r, thread_local_token, Space.space); // threadlocal } if (var_decl.comptime_token) |comptime_token| { - try renderToken(ais, tree, comptime_token, Space.space); // comptime + try renderToken(r, comptime_token, Space.space); // comptime } - try renderToken(ais, tree, var_decl.ast.mut_token, .space); // var + try renderToken(r, var_decl.ast.mut_token, .space); // var const name_space = if (var_decl.ast.type_node == 0 and (var_decl.ast.align_node != 0 or @@ -956,18 +993,18 @@ fn renderVarDecl(gpa: Allocator, ais: *Ais, tree: Ast, var_decl: Ast.full.VarDec Space.space else Space.none; - try renderToken(ais, tree, var_decl.ast.mut_token + 1, name_space); // name + try renderToken(r, var_decl.ast.mut_token + 1, name_space); // name if (var_decl.ast.type_node != 0) { - try renderToken(ais, tree, var_decl.ast.mut_token + 2, Space.space); // : + try renderToken(r, var_decl.ast.mut_token + 2, Space.space); // : if (var_decl.ast.align_node != 0 or var_decl.ast.addrspace_node != 0 or var_decl.ast.section_node != 0 or var_decl.ast.init_node != 0) { - try renderExpression(gpa, ais, tree, var_decl.ast.type_node, .space); + try renderExpression(r, var_decl.ast.type_node, .space); } else { - try renderExpression(gpa, ais, tree, var_decl.ast.type_node, .none); + try renderExpression(r, var_decl.ast.type_node, .none); const semicolon = tree.lastToken(var_decl.ast.type_node) + 1; - return renderToken(ais, tree, semicolon, Space.newline); // ; + return renderToken(r, semicolon, Space.newline); // ; } } @@ -975,16 +1012,16 @@ fn renderVarDecl(gpa: Allocator, ais: *Ais, tree: Ast, var_decl: Ast.full.VarDec const lparen = tree.firstToken(var_decl.ast.align_node) - 1; const align_kw = lparen - 1; const rparen = tree.lastToken(var_decl.ast.align_node) + 1; - try renderToken(ais, tree, align_kw, Space.none); // align - try renderToken(ais, tree, lparen, Space.none); // ( - try renderExpression(gpa, ais, tree, var_decl.ast.align_node, Space.none); + try renderToken(r, align_kw, Space.none); // align + try renderToken(r, lparen, Space.none); // ( + try renderExpression(r, var_decl.ast.align_node, Space.none); if (var_decl.ast.addrspace_node != 0 or var_decl.ast.section_node != 0 or var_decl.ast.init_node != 0) { - try renderToken(ais, tree, rparen, .space); // ) + try renderToken(r, rparen, .space); // ) } else { - try renderToken(ais, tree, rparen, .none); // ) - return renderToken(ais, tree, rparen + 1, Space.newline); // ; + try renderToken(r, rparen, .none); // ) + return renderToken(r, rparen + 1, Space.newline); // ; } } @@ -992,14 +1029,14 @@ fn renderVarDecl(gpa: Allocator, ais: *Ais, tree: Ast, var_decl: Ast.full.VarDec const lparen = tree.firstToken(var_decl.ast.addrspace_node) - 1; const addrspace_kw = lparen - 1; const rparen = tree.lastToken(var_decl.ast.addrspace_node) + 1; - try renderToken(ais, tree, addrspace_kw, Space.none); // addrspace - try renderToken(ais, tree, lparen, Space.none); // ( - try renderExpression(gpa, ais, tree, var_decl.ast.addrspace_node, Space.none); + try renderToken(r, addrspace_kw, Space.none); // addrspace + try renderToken(r, lparen, Space.none); // ( + try renderExpression(r, var_decl.ast.addrspace_node, Space.none); if (var_decl.ast.section_node != 0 or var_decl.ast.init_node != 0) { - try renderToken(ais, tree, rparen, .space); // ) + try renderToken(r, rparen, .space); // ) } else { - try renderToken(ais, tree, rparen, .none); // ) - return renderToken(ais, tree, rparen + 1, Space.newline); // ; + try renderToken(r, rparen, .none); // ) + return renderToken(r, rparen + 1, Space.newline); // ; } } @@ -1007,14 +1044,14 @@ fn renderVarDecl(gpa: Allocator, ais: *Ais, tree: Ast, var_decl: Ast.full.VarDec const lparen = tree.firstToken(var_decl.ast.section_node) - 1; const section_kw = lparen - 1; const rparen = tree.lastToken(var_decl.ast.section_node) + 1; - try renderToken(ais, tree, section_kw, Space.none); // linksection - try renderToken(ais, tree, lparen, Space.none); // ( - try renderExpression(gpa, ais, tree, var_decl.ast.section_node, Space.none); + try renderToken(r, section_kw, Space.none); // linksection + try renderToken(r, lparen, Space.none); // ( + try renderExpression(r, var_decl.ast.section_node, Space.none); if (var_decl.ast.init_node != 0) { - try renderToken(ais, tree, rparen, .space); // ) + try renderToken(r, rparen, .space); // ) } else { - try renderToken(ais, tree, rparen, .none); // ) - return renderToken(ais, tree, rparen + 1, Space.newline); // ; + try renderToken(r, rparen, .none); // ) + return renderToken(r, rparen + 1, Space.newline); // ; } } @@ -1023,17 +1060,17 @@ fn renderVarDecl(gpa: Allocator, ais: *Ais, tree: Ast, var_decl: Ast.full.VarDec const eq_space: Space = if (tree.tokensOnSameLine(eq_token, eq_token + 1)) .space else .newline; { ais.pushIndent(); - try renderToken(ais, tree, eq_token, eq_space); // = + try renderToken(r, eq_token, eq_space); // = ais.popIndent(); } ais.pushIndentOneShot(); - return renderExpression(gpa, ais, tree, var_decl.ast.init_node, .semicolon); // ; + return renderExpression(r, var_decl.ast.init_node, .semicolon); // ; } - return renderToken(ais, tree, var_decl.ast.mut_token + 2, .newline); // ; + return renderToken(r, var_decl.ast.mut_token + 2, .newline); // ; } -fn renderIf(gpa: Allocator, ais: *Ais, tree: Ast, if_node: Ast.full.If, space: Space) Error!void { - return renderWhile(gpa, ais, tree, .{ +fn renderIf(r: Render, if_node: Ast.full.If, space: Space) Error!void { + return renderWhile(r, .{ .ast = .{ .while_token = if_node.ast.if_token, .cond_expr = if_node.ast.cond_expr, @@ -1051,41 +1088,43 @@ fn renderIf(gpa: Allocator, ais: *Ais, tree: Ast, if_node: Ast.full.If, space: S /// Note that this function is additionally used to render if and for expressions, with /// respective values set to null. -fn renderWhile(gpa: Allocator, ais: *Ais, tree: Ast, while_node: Ast.full.While, space: Space) Error!void { +fn renderWhile(r: Render, while_node: Ast.full.While, space: Space) Error!void { + const tree = r.tree; + const ais = r.ais; const node_tags = tree.nodes.items(.tag); const token_tags = tree.tokens.items(.tag); if (while_node.label_token) |label| { - try renderToken(ais, tree, label, .none); // label - try renderToken(ais, tree, label + 1, .space); // : + try renderToken(r, label, .none); // label + try renderToken(r, label + 1, .space); // : } if (while_node.inline_token) |inline_token| { - try renderToken(ais, tree, inline_token, .space); // inline + try renderToken(r, inline_token, .space); // inline } - try renderToken(ais, tree, while_node.ast.while_token, .space); // if/for/while - try renderToken(ais, tree, while_node.ast.while_token + 1, .none); // lparen - try renderExpression(gpa, ais, tree, while_node.ast.cond_expr, .none); // condition + try renderToken(r, while_node.ast.while_token, .space); // if/for/while + try renderToken(r, while_node.ast.while_token + 1, .none); // lparen + try renderExpression(r, while_node.ast.cond_expr, .none); // condition var last_prefix_token = tree.lastToken(while_node.ast.cond_expr) + 1; // rparen if (while_node.payload_token) |payload_token| { - try renderToken(ais, tree, last_prefix_token, .space); - try renderToken(ais, tree, payload_token - 1, .none); // | + try renderToken(r, last_prefix_token, .space); + try renderToken(r, payload_token - 1, .none); // | const ident = blk: { if (token_tags[payload_token] == .asterisk) { - try renderToken(ais, tree, payload_token, .none); // * + try renderToken(r, payload_token, .none); // * break :blk payload_token + 1; } else { break :blk payload_token; } }; - try renderToken(ais, tree, ident, .none); // identifier + try renderToken(r, ident, .none); // identifier const pipe = blk: { if (token_tags[ident + 1] == .comma) { - try renderToken(ais, tree, ident + 1, .space); // , - try renderToken(ais, tree, ident + 2, .none); // index + try renderToken(r, ident + 1, .space); // , + try renderToken(r, ident + 2, .none); // index break :blk ident + 3; } else { break :blk ident + 1; @@ -1095,11 +1134,11 @@ fn renderWhile(gpa: Allocator, ais: *Ais, tree: Ast, while_node: Ast.full.While, } if (while_node.ast.cont_expr != 0) { - try renderToken(ais, tree, last_prefix_token, .space); + try renderToken(r, last_prefix_token, .space); const lparen = tree.firstToken(while_node.ast.cont_expr) - 1; - try renderToken(ais, tree, lparen - 1, .space); // : - try renderToken(ais, tree, lparen, .none); // lparen - try renderExpression(gpa, ais, tree, while_node.ast.cont_expr, .none); + try renderToken(r, lparen - 1, .space); // : + try renderToken(r, lparen, .none); // lparen + try renderExpression(r, while_node.ast.cont_expr, .none); last_prefix_token = tree.lastToken(while_node.ast.cont_expr) + 1; // rparen } @@ -1108,27 +1147,27 @@ fn renderWhile(gpa: Allocator, ais: *Ais, tree: Ast, while_node: Ast.full.While, !tree.tokensOnSameLine(last_prefix_token, tree.firstToken(while_node.ast.then_expr)); if (indent_then_expr or (then_expr_is_block and ais.isLineOverIndented())) { ais.pushIndentNextLine(); - try renderToken(ais, tree, last_prefix_token, .newline); + try renderToken(r, last_prefix_token, .newline); ais.popIndent(); } else { - try renderToken(ais, tree, last_prefix_token, .space); + try renderToken(r, last_prefix_token, .space); } if (while_node.ast.else_expr != 0) { if (indent_then_expr) { ais.pushIndent(); - try renderExpression(gpa, ais, tree, while_node.ast.then_expr, .newline); + try renderExpression(r, while_node.ast.then_expr, .newline); ais.popIndent(); } else { - try renderExpression(gpa, ais, tree, while_node.ast.then_expr, .space); + try renderExpression(r, while_node.ast.then_expr, .space); } var last_else_token = while_node.else_token; if (while_node.error_token) |error_token| { - try renderToken(ais, tree, while_node.else_token, .space); // else - try renderToken(ais, tree, error_token - 1, .none); // | - try renderToken(ais, tree, error_token, .none); // identifier + try renderToken(r, while_node.else_token, .space); // else + try renderToken(r, error_token - 1, .none); // | + try renderToken(r, error_token, .none); // identifier last_else_token = error_token + 1; // | } @@ -1137,111 +1176,107 @@ fn renderWhile(gpa: Allocator, ais: *Ais, tree: Ast, while_node: Ast.full.While, !nodeIsIfForWhileSwitch(node_tags[while_node.ast.else_expr]); if (indent_else_expr) { ais.pushIndentNextLine(); - try renderToken(ais, tree, last_else_token, .newline); + try renderToken(r, last_else_token, .newline); ais.popIndent(); - try renderExpressionIndented(gpa, ais, tree, while_node.ast.else_expr, space); + try renderExpressionIndented(r, while_node.ast.else_expr, space); } else { - try renderToken(ais, tree, last_else_token, .space); - try renderExpression(gpa, ais, tree, while_node.ast.else_expr, space); + try renderToken(r, last_else_token, .space); + try renderExpression(r, while_node.ast.else_expr, space); } } else { if (indent_then_expr) { - try renderExpressionIndented(gpa, ais, tree, while_node.ast.then_expr, space); + try renderExpressionIndented(r, while_node.ast.then_expr, space); } else { - try renderExpression(gpa, ais, tree, while_node.ast.then_expr, space); + try renderExpression(r, while_node.ast.then_expr, space); } } } -fn renderContainerField( - gpa: Allocator, - ais: *Ais, - tree: Ast, - field: Ast.full.ContainerField, - space: Space, -) Error!void { +fn renderContainerField(r: Render, field: Ast.full.ContainerField, space: Space) Error!void { + const tree = r.tree; + const ais = r.ais; if (field.comptime_token) |t| { - try renderToken(ais, tree, t, .space); // comptime + try renderToken(r, t, .space); // comptime } if (field.ast.type_expr == 0 and field.ast.value_expr == 0) { - return renderTokenComma(ais, tree, field.ast.name_token, space); // name + return renderTokenComma(r, field.ast.name_token, space); // name } if (field.ast.type_expr != 0 and field.ast.value_expr == 0) { - try renderToken(ais, tree, field.ast.name_token, .none); // name - try renderToken(ais, tree, field.ast.name_token + 1, .space); // : + try renderToken(r, field.ast.name_token, .none); // name + try renderToken(r, field.ast.name_token + 1, .space); // : if (field.ast.align_expr != 0) { - try renderExpression(gpa, ais, tree, field.ast.type_expr, .space); // type + try renderExpression(r, field.ast.type_expr, .space); // type const align_token = tree.firstToken(field.ast.align_expr) - 2; - try renderToken(ais, tree, align_token, .none); // align - try renderToken(ais, tree, align_token + 1, .none); // ( - try renderExpression(gpa, ais, tree, field.ast.align_expr, .none); // alignment + try renderToken(r, align_token, .none); // align + try renderToken(r, align_token + 1, .none); // ( + try renderExpression(r, field.ast.align_expr, .none); // alignment const rparen = tree.lastToken(field.ast.align_expr) + 1; - return renderTokenComma(ais, tree, rparen, space); // ) + return renderTokenComma(r, rparen, space); // ) } else { - return renderExpressionComma(gpa, ais, tree, field.ast.type_expr, space); // type + return renderExpressionComma(r, field.ast.type_expr, space); // type } } if (field.ast.type_expr == 0 and field.ast.value_expr != 0) { - try renderToken(ais, tree, field.ast.name_token, .space); // name - try renderToken(ais, tree, field.ast.name_token + 1, .space); // = - return renderExpressionComma(gpa, ais, tree, field.ast.value_expr, space); // value + try renderToken(r, field.ast.name_token, .space); // name + try renderToken(r, field.ast.name_token + 1, .space); // = + return renderExpressionComma(r, field.ast.value_expr, space); // value } - try renderToken(ais, tree, field.ast.name_token, .none); // name - try renderToken(ais, tree, field.ast.name_token + 1, .space); // : - try renderExpression(gpa, ais, tree, field.ast.type_expr, .space); // type + try renderToken(r, field.ast.name_token, .none); // name + try renderToken(r, field.ast.name_token + 1, .space); // : + try renderExpression(r, field.ast.type_expr, .space); // type if (field.ast.align_expr != 0) { const lparen_token = tree.firstToken(field.ast.align_expr) - 1; const align_kw = lparen_token - 1; const rparen_token = tree.lastToken(field.ast.align_expr) + 1; - try renderToken(ais, tree, align_kw, .none); // align - try renderToken(ais, tree, lparen_token, .none); // ( - try renderExpression(gpa, ais, tree, field.ast.align_expr, .none); // alignment - try renderToken(ais, tree, rparen_token, .space); // ) + try renderToken(r, align_kw, .none); // align + try renderToken(r, lparen_token, .none); // ( + try renderExpression(r, field.ast.align_expr, .none); // alignment + try renderToken(r, rparen_token, .space); // ) } const eq_token = tree.firstToken(field.ast.value_expr) - 1; const eq_space: Space = if (tree.tokensOnSameLine(eq_token, eq_token + 1)) .space else .newline; { ais.pushIndent(); - try renderToken(ais, tree, eq_token, eq_space); // = + try renderToken(r, eq_token, eq_space); // = ais.popIndent(); } if (eq_space == .space) - return renderExpressionComma(gpa, ais, tree, field.ast.value_expr, space); // value + return renderExpressionComma(r, field.ast.value_expr, space); // value const token_tags = tree.tokens.items(.tag); const maybe_comma = tree.lastToken(field.ast.value_expr) + 1; if (token_tags[maybe_comma] == .comma) { ais.pushIndent(); - try renderExpression(gpa, ais, tree, field.ast.value_expr, .none); // value + try renderExpression(r, field.ast.value_expr, .none); // value ais.popIndent(); - try renderToken(ais, tree, maybe_comma, .newline); + try renderToken(r, maybe_comma, .newline); } else { ais.pushIndent(); - try renderExpression(gpa, ais, tree, field.ast.value_expr, space); // value + try renderExpression(r, field.ast.value_expr, space); // value ais.popIndent(); } } fn renderBuiltinCall( - gpa: Allocator, - ais: *Ais, - tree: Ast, + r: Render, builtin_token: Ast.TokenIndex, params: []const Ast.Node.Index, space: Space, ) Error!void { + const tree = r.tree; + const ais = r.ais; const token_tags = tree.tokens.items(.tag); - try renderToken(ais, tree, builtin_token, .none); // @name + try renderToken(r, builtin_token, .none); // @name if (params.len == 0) { - try renderToken(ais, tree, builtin_token + 1, .none); // ( - return renderToken(ais, tree, builtin_token + 2, space); // ) + try renderToken(r, builtin_token + 1, .none); // ( + return renderToken(r, builtin_token + 2, space); // ) } const last_param = params[params.len - 1]; @@ -1249,7 +1284,7 @@ fn renderBuiltinCall( if (token_tags[after_last_param_token] != .comma) { // Render all on one line, no trailing comma. - try renderToken(ais, tree, builtin_token + 1, .none); // ( + try renderToken(r, builtin_token + 1, .none); // ( for (params) |param_node, i| { const first_param_token = tree.firstToken(param_node); @@ -1258,39 +1293,41 @@ fn renderBuiltinCall( { ais.pushIndentOneShot(); } - try renderExpression(gpa, ais, tree, param_node, .none); + try renderExpression(r, param_node, .none); if (i + 1 < params.len) { const comma_token = tree.lastToken(param_node) + 1; - try renderToken(ais, tree, comma_token, .space); // , + try renderToken(r, comma_token, .space); // , } } - return renderToken(ais, tree, after_last_param_token, space); // ) + return renderToken(r, after_last_param_token, space); // ) } else { // Render one param per line. ais.pushIndent(); - try renderToken(ais, tree, builtin_token + 1, Space.newline); // ( + try renderToken(r, builtin_token + 1, Space.newline); // ( for (params) |param_node| { - try renderExpression(gpa, ais, tree, param_node, .comma); + try renderExpression(r, param_node, .comma); } ais.popIndent(); - return renderToken(ais, tree, after_last_param_token + 1, space); // ) + return renderToken(r, after_last_param_token + 1, space); // ) } } -fn renderFnProto(gpa: Allocator, ais: *Ais, tree: Ast, fn_proto: Ast.full.FnProto, space: Space) Error!void { +fn renderFnProto(r: Render, fn_proto: Ast.full.FnProto, space: Space) Error!void { + const tree = r.tree; + const ais = r.ais; const token_tags = tree.tokens.items(.tag); const token_starts = tree.tokens.items(.start); const after_fn_token = fn_proto.ast.fn_token + 1; const lparen = if (token_tags[after_fn_token] == .identifier) blk: { - try renderToken(ais, tree, fn_proto.ast.fn_token, .space); // fn - try renderToken(ais, tree, after_fn_token, .none); // name + try renderToken(r, fn_proto.ast.fn_token, .space); // fn + try renderToken(r, after_fn_token, .none); // name break :blk after_fn_token + 1; } else blk: { - try renderToken(ais, tree, fn_proto.ast.fn_token, .space); // fn + try renderToken(r, fn_proto.ast.fn_token, .space); // fn break :blk fn_proto.ast.fn_token + 1; }; assert(token_tags[lparen] == .l_paren); @@ -1342,7 +1379,7 @@ fn renderFnProto(gpa: Allocator, ais: *Ais, tree: Ast, fn_proto: Ast.full.FnProt const trailing_comma = token_tags[rparen - 1] == .comma; if (!trailing_comma and !hasComment(tree, lparen, rparen)) { // Render all on one line, no trailing comma. - try renderToken(ais, tree, lparen, .none); // ( + try renderToken(r, lparen, .none); // ( var param_i: usize = 0; var last_param_token = lparen; @@ -1350,25 +1387,25 @@ fn renderFnProto(gpa: Allocator, ais: *Ais, tree: Ast, fn_proto: Ast.full.FnProt last_param_token += 1; switch (token_tags[last_param_token]) { .doc_comment => { - try renderToken(ais, tree, last_param_token, .newline); + try renderToken(r, last_param_token, .newline); continue; }, .ellipsis3 => { - try renderToken(ais, tree, last_param_token, .none); // ... + try renderToken(r, last_param_token, .none); // ... break; }, .keyword_noalias, .keyword_comptime => { - try renderToken(ais, tree, last_param_token, .space); + try renderToken(r, last_param_token, .space); last_param_token += 1; }, .identifier => {}, .keyword_anytype => { - try renderToken(ais, tree, last_param_token, .none); // anytype + try renderToken(r, last_param_token, .none); // anytype continue; }, .r_paren => break, .comma => { - try renderToken(ais, tree, last_param_token, .space); // , + try renderToken(r, last_param_token, .space); // , continue; }, else => {}, // Parameter type without a name. @@ -1376,24 +1413,24 @@ fn renderFnProto(gpa: Allocator, ais: *Ais, tree: Ast, fn_proto: Ast.full.FnProt if (token_tags[last_param_token] == .identifier and token_tags[last_param_token + 1] == .colon) { - try renderToken(ais, tree, last_param_token, .none); // name + try renderToken(r, last_param_token, .none); // name last_param_token += 1; - try renderToken(ais, tree, last_param_token, .space); // : + try renderToken(r, last_param_token, .space); // : last_param_token += 1; } if (token_tags[last_param_token] == .keyword_anytype) { - try renderToken(ais, tree, last_param_token, .none); // anytype + try renderToken(r, last_param_token, .none); // anytype continue; } const param = fn_proto.ast.params[param_i]; param_i += 1; - try renderExpression(gpa, ais, tree, param, .none); + try renderExpression(r, param, .none); last_param_token = tree.lastToken(param); } } else { // One param per line. ais.pushIndent(); - try renderToken(ais, tree, lparen, .newline); // ( + try renderToken(r, lparen, .newline); // ( var param_i: usize = 0; var last_param_token = lparen; @@ -1401,20 +1438,20 @@ fn renderFnProto(gpa: Allocator, ais: *Ais, tree: Ast, fn_proto: Ast.full.FnProt last_param_token += 1; switch (token_tags[last_param_token]) { .doc_comment => { - try renderToken(ais, tree, last_param_token, .newline); + try renderToken(r, last_param_token, .newline); continue; }, .ellipsis3 => { - try renderToken(ais, tree, last_param_token, .comma); // ... + try renderToken(r, last_param_token, .comma); // ... break; }, .keyword_noalias, .keyword_comptime => { - try renderToken(ais, tree, last_param_token, .space); + try renderToken(r, last_param_token, .space); last_param_token += 1; }, .identifier => {}, .keyword_anytype => { - try renderToken(ais, tree, last_param_token, .comma); // anytype + try renderToken(r, last_param_token, .comma); // anytype if (token_tags[last_param_token + 1] == .comma) last_param_token += 1; continue; @@ -1425,56 +1462,56 @@ fn renderFnProto(gpa: Allocator, ais: *Ais, tree: Ast, fn_proto: Ast.full.FnProt if (token_tags[last_param_token] == .identifier and token_tags[last_param_token + 1] == .colon) { - try renderToken(ais, tree, last_param_token, .none); // name + try renderToken(r, last_param_token, .none); // name last_param_token += 1; - try renderToken(ais, tree, last_param_token, .space); // : + try renderToken(r, last_param_token, .space); // : last_param_token += 1; } if (token_tags[last_param_token] == .keyword_anytype) { - try renderToken(ais, tree, last_param_token, .comma); // anytype + try renderToken(r, last_param_token, .comma); // anytype if (token_tags[last_param_token + 1] == .comma) last_param_token += 1; continue; } const param = fn_proto.ast.params[param_i]; param_i += 1; - try renderExpression(gpa, ais, tree, param, .comma); + try renderExpression(r, param, .comma); last_param_token = tree.lastToken(param); if (token_tags[last_param_token + 1] == .comma) last_param_token += 1; } ais.popIndent(); } - try renderToken(ais, tree, rparen, .space); // ) + try renderToken(r, rparen, .space); // ) if (fn_proto.ast.align_expr != 0) { const align_lparen = tree.firstToken(fn_proto.ast.align_expr) - 1; const align_rparen = tree.lastToken(fn_proto.ast.align_expr) + 1; - try renderToken(ais, tree, align_lparen - 1, .none); // align - try renderToken(ais, tree, align_lparen, .none); // ( - try renderExpression(gpa, ais, tree, fn_proto.ast.align_expr, .none); - try renderToken(ais, tree, align_rparen, .space); // ) + try renderToken(r, align_lparen - 1, .none); // align + try renderToken(r, align_lparen, .none); // ( + try renderExpression(r, fn_proto.ast.align_expr, .none); + try renderToken(r, align_rparen, .space); // ) } if (fn_proto.ast.addrspace_expr != 0) { const align_lparen = tree.firstToken(fn_proto.ast.addrspace_expr) - 1; const align_rparen = tree.lastToken(fn_proto.ast.addrspace_expr) + 1; - try renderToken(ais, tree, align_lparen - 1, .none); // addrspace - try renderToken(ais, tree, align_lparen, .none); // ( - try renderExpression(gpa, ais, tree, fn_proto.ast.addrspace_expr, .none); - try renderToken(ais, tree, align_rparen, .space); // ) + try renderToken(r, align_lparen - 1, .none); // addrspace + try renderToken(r, align_lparen, .none); // ( + try renderExpression(r, fn_proto.ast.addrspace_expr, .none); + try renderToken(r, align_rparen, .space); // ) } if (fn_proto.ast.section_expr != 0) { const section_lparen = tree.firstToken(fn_proto.ast.section_expr) - 1; const section_rparen = tree.lastToken(fn_proto.ast.section_expr) + 1; - try renderToken(ais, tree, section_lparen - 1, .none); // section - try renderToken(ais, tree, section_lparen, .none); // ( - try renderExpression(gpa, ais, tree, fn_proto.ast.section_expr, .none); - try renderToken(ais, tree, section_rparen, .space); // ) + try renderToken(r, section_lparen - 1, .none); // section + try renderToken(r, section_lparen, .none); // ( + try renderExpression(r, fn_proto.ast.section_expr, .none); + try renderToken(r, section_rparen, .space); // ) } const is_callconv_inline = mem.eql(u8, "Inline", tree.tokenSlice(tree.nodes.items(.main_token)[fn_proto.ast.callconv_expr])); @@ -1483,25 +1520,24 @@ fn renderFnProto(gpa: Allocator, ais: *Ais, tree: Ast, fn_proto: Ast.full.FnProt const callconv_lparen = tree.firstToken(fn_proto.ast.callconv_expr) - 1; const callconv_rparen = tree.lastToken(fn_proto.ast.callconv_expr) + 1; - try renderToken(ais, tree, callconv_lparen - 1, .none); // callconv - try renderToken(ais, tree, callconv_lparen, .none); // ( - try renderExpression(gpa, ais, tree, fn_proto.ast.callconv_expr, .none); - try renderToken(ais, tree, callconv_rparen, .space); // ) + try renderToken(r, callconv_lparen - 1, .none); // callconv + try renderToken(r, callconv_lparen, .none); // ( + try renderExpression(r, fn_proto.ast.callconv_expr, .none); + try renderToken(r, callconv_rparen, .space); // ) } if (token_tags[maybe_bang] == .bang) { - try renderToken(ais, tree, maybe_bang, .none); // ! + try renderToken(r, maybe_bang, .none); // ! } - return renderExpression(gpa, ais, tree, fn_proto.ast.return_type, space); + return renderExpression(r, fn_proto.ast.return_type, space); } fn renderSwitchCase( - gpa: Allocator, - ais: *Ais, - tree: Ast, + r: Render, switch_case: Ast.full.SwitchCase, space: Space, ) Error!void { + const tree = r.tree; const node_tags = tree.nodes.items(.tag); const token_tags = tree.tokens.items(.tag); const trailing_comma = token_tags[switch_case.ast.arrow_token - 1] == .comma; @@ -1512,17 +1548,17 @@ fn renderSwitchCase( // Render everything before the arrow if (switch_case.ast.values.len == 0) { - try renderToken(ais, tree, switch_case.ast.arrow_token - 1, .space); // else keyword + try renderToken(r, switch_case.ast.arrow_token - 1, .space); // else keyword } else if (switch_case.ast.values.len == 1 and !has_comment_before_arrow) { // render on one line and drop the trailing comma if any - try renderExpression(gpa, ais, tree, switch_case.ast.values[0], .space); + try renderExpression(r, switch_case.ast.values[0], .space); } else if (trailing_comma or has_comment_before_arrow) { // Render each value on a new line - try renderExpressions(gpa, ais, tree, switch_case.ast.values, .comma); + try renderExpressions(r, switch_case.ast.values, .comma); } else { // Render on one line for (switch_case.ast.values) |value_expr| { - try renderExpression(gpa, ais, tree, value_expr, .comma_space); + try renderExpression(r, value_expr, .comma_space); } } @@ -1533,31 +1569,31 @@ fn renderSwitchCase( else Space.space; const after_arrow_space: Space = if (switch_case.payload_token == null) pre_target_space else .space; - try renderToken(ais, tree, switch_case.ast.arrow_token, after_arrow_space); + try renderToken(r, switch_case.ast.arrow_token, after_arrow_space); if (switch_case.payload_token) |payload_token| { - try renderToken(ais, tree, payload_token - 1, .none); // pipe + try renderToken(r, payload_token - 1, .none); // pipe if (token_tags[payload_token] == .asterisk) { - try renderToken(ais, tree, payload_token, .none); // asterisk - try renderToken(ais, tree, payload_token + 1, .none); // identifier - try renderToken(ais, tree, payload_token + 2, pre_target_space); // pipe + try renderToken(r, payload_token, .none); // asterisk + try renderToken(r, payload_token + 1, .none); // identifier + try renderToken(r, payload_token + 2, pre_target_space); // pipe } else { - try renderToken(ais, tree, payload_token, .none); // identifier - try renderToken(ais, tree, payload_token + 1, pre_target_space); // pipe + try renderToken(r, payload_token, .none); // identifier + try renderToken(r, payload_token + 1, pre_target_space); // pipe } } - try renderExpression(gpa, ais, tree, switch_case.ast.target_expr, space); + try renderExpression(r, switch_case.ast.target_expr, space); } fn renderBlock( - gpa: Allocator, - ais: *Ais, - tree: Ast, + r: Render, block_node: Ast.Node.Index, statements: []const Ast.Node.Index, space: Space, ) Error!void { + const tree = r.tree; + const ais = r.ais; const token_tags = tree.tokens.items(.tag); const node_tags = tree.nodes.items(.tag); const lbrace = tree.nodes.items(.main_token)[block_node]; @@ -1565,50 +1601,50 @@ fn renderBlock( if (token_tags[lbrace - 1] == .colon and token_tags[lbrace - 2] == .identifier) { - try renderToken(ais, tree, lbrace - 2, .none); - try renderToken(ais, tree, lbrace - 1, .space); + try renderToken(r, lbrace - 2, .none); + try renderToken(r, lbrace - 1, .space); } ais.pushIndentNextLine(); if (statements.len == 0) { - try renderToken(ais, tree, lbrace, .none); + try renderToken(r, lbrace, .none); } else { - try renderToken(ais, tree, lbrace, .newline); + try renderToken(r, lbrace, .newline); for (statements) |stmt, i| { - if (i != 0) try renderExtraNewline(ais, tree, stmt); + if (i != 0) try renderExtraNewline(r, stmt); switch (node_tags[stmt]) { - .global_var_decl => try renderVarDecl(gpa, ais, tree, tree.globalVarDecl(stmt)), - .local_var_decl => try renderVarDecl(gpa, ais, tree, tree.localVarDecl(stmt)), - .simple_var_decl => try renderVarDecl(gpa, ais, tree, tree.simpleVarDecl(stmt)), - .aligned_var_decl => try renderVarDecl(gpa, ais, tree, tree.alignedVarDecl(stmt)), - else => try renderExpression(gpa, ais, tree, stmt, .semicolon), + .global_var_decl => try renderVarDecl(r, tree.globalVarDecl(stmt)), + .local_var_decl => try renderVarDecl(r, tree.localVarDecl(stmt)), + .simple_var_decl => try renderVarDecl(r, tree.simpleVarDecl(stmt)), + .aligned_var_decl => try renderVarDecl(r, tree.alignedVarDecl(stmt)), + else => try renderExpression(r, stmt, .semicolon), } } } ais.popIndent(); - try renderToken(ais, tree, tree.lastToken(block_node), space); // rbrace + try renderToken(r, tree.lastToken(block_node), space); // rbrace } fn renderStructInit( - gpa: Allocator, - ais: *Ais, - tree: Ast, + r: Render, struct_node: Ast.Node.Index, struct_init: Ast.full.StructInit, space: Space, ) Error!void { + const tree = r.tree; + const ais = r.ais; const token_tags = tree.tokens.items(.tag); if (struct_init.ast.type_expr == 0) { - try renderToken(ais, tree, struct_init.ast.lbrace - 1, .none); // . + try renderToken(r, struct_init.ast.lbrace - 1, .none); // . } else { - try renderExpression(gpa, ais, tree, struct_init.ast.type_expr, .none); // T + try renderExpression(r, struct_init.ast.type_expr, .none); // T } if (struct_init.ast.fields.len == 0) { ais.pushIndentNextLine(); - try renderToken(ais, tree, struct_init.ast.lbrace, .none); // lbrace + try renderToken(r, struct_init.ast.lbrace, .none); // lbrace ais.popIndent(); - return renderToken(ais, tree, struct_init.ast.lbrace + 1, space); // rbrace + return renderToken(r, struct_init.ast.lbrace + 1, space); // rbrace } const rbrace = tree.lastToken(struct_node); @@ -1616,59 +1652,60 @@ fn renderStructInit( if (trailing_comma or hasComment(tree, struct_init.ast.lbrace, rbrace)) { // Render one field init per line. ais.pushIndentNextLine(); - try renderToken(ais, tree, struct_init.ast.lbrace, .newline); + try renderToken(r, struct_init.ast.lbrace, .newline); - try renderToken(ais, tree, struct_init.ast.lbrace + 1, .none); // . - try renderToken(ais, tree, struct_init.ast.lbrace + 2, .space); // name - try renderToken(ais, tree, struct_init.ast.lbrace + 3, .space); // = - try renderExpression(gpa, ais, tree, struct_init.ast.fields[0], .comma); + try renderToken(r, struct_init.ast.lbrace + 1, .none); // . + try renderToken(r, struct_init.ast.lbrace + 2, .space); // name + try renderToken(r, struct_init.ast.lbrace + 3, .space); // = + try renderExpression(r, struct_init.ast.fields[0], .comma); for (struct_init.ast.fields[1..]) |field_init| { const init_token = tree.firstToken(field_init); - try renderExtraNewlineToken(ais, tree, init_token - 3); - try renderToken(ais, tree, init_token - 3, .none); // . - try renderToken(ais, tree, init_token - 2, .space); // name - try renderToken(ais, tree, init_token - 1, .space); // = - try renderExpression(gpa, ais, tree, field_init, .comma); + try renderExtraNewlineToken(r, init_token - 3); + try renderToken(r, init_token - 3, .none); // . + try renderToken(r, init_token - 2, .space); // name + try renderToken(r, init_token - 1, .space); // = + try renderExpression(r, field_init, .comma); } ais.popIndent(); } else { // Render all on one line, no trailing comma. - try renderToken(ais, tree, struct_init.ast.lbrace, .space); + try renderToken(r, struct_init.ast.lbrace, .space); for (struct_init.ast.fields) |field_init| { const init_token = tree.firstToken(field_init); - try renderToken(ais, tree, init_token - 3, .none); // . - try renderToken(ais, tree, init_token - 2, .space); // name - try renderToken(ais, tree, init_token - 1, .space); // = - try renderExpression(gpa, ais, tree, field_init, .comma_space); + try renderToken(r, init_token - 3, .none); // . + try renderToken(r, init_token - 2, .space); // name + try renderToken(r, init_token - 1, .space); // = + try renderExpression(r, field_init, .comma_space); } } - return renderToken(ais, tree, rbrace, space); + return renderToken(r, rbrace, space); } fn renderArrayInit( - gpa: Allocator, - ais: *Ais, - tree: Ast, + r: Render, array_init: Ast.full.ArrayInit, space: Space, ) Error!void { + const tree = r.tree; + const ais = r.ais; + const gpa = r.gpa; const token_tags = tree.tokens.items(.tag); if (array_init.ast.type_expr == 0) { - try renderToken(ais, tree, array_init.ast.lbrace - 1, .none); // . + try renderToken(r, array_init.ast.lbrace - 1, .none); // . } else { - try renderExpression(gpa, ais, tree, array_init.ast.type_expr, .none); // T + try renderExpression(r, array_init.ast.type_expr, .none); // T } if (array_init.ast.elements.len == 0) { ais.pushIndentNextLine(); - try renderToken(ais, tree, array_init.ast.lbrace, .none); // lbrace + try renderToken(r, array_init.ast.lbrace, .none); // lbrace ais.popIndent(); - return renderToken(ais, tree, array_init.ast.lbrace + 1, space); // rbrace + return renderToken(r, array_init.ast.lbrace + 1, space); // rbrace } const last_elem = array_init.ast.elements[array_init.ast.elements.len - 1]; @@ -1683,9 +1720,9 @@ fn renderArrayInit( if (token_tags[first_token] != .multiline_string_literal_line and !anythingBetween(tree, last_elem_token, rbrace)) { - try renderToken(ais, tree, array_init.ast.lbrace, .none); - try renderExpression(gpa, ais, tree, only_elem, .none); - return renderToken(ais, tree, rbrace, space); + try renderToken(r, array_init.ast.lbrace, .none); + try renderExpression(r, only_elem, .none); + return renderToken(r, rbrace, space); } } @@ -1696,19 +1733,19 @@ fn renderArrayInit( // Render all on one line, no trailing comma. if (array_init.ast.elements.len == 1) { // If there is only one element, we don't use spaces - try renderToken(ais, tree, array_init.ast.lbrace, .none); - try renderExpression(gpa, ais, tree, array_init.ast.elements[0], .none); + try renderToken(r, array_init.ast.lbrace, .none); + try renderExpression(r, array_init.ast.elements[0], .none); } else { - try renderToken(ais, tree, array_init.ast.lbrace, .space); + try renderToken(r, array_init.ast.lbrace, .space); for (array_init.ast.elements) |elem| { - try renderExpression(gpa, ais, tree, elem, .comma_space); + try renderExpression(r, elem, .comma_space); } } - return renderToken(ais, tree, last_elem_token + 1, space); // rbrace + return renderToken(r, last_elem_token + 1, space); // rbrace } ais.pushIndentNextLine(); - try renderToken(ais, tree, array_init.ast.lbrace, .newline); + try renderToken(r, array_init.ast.lbrace, .newline); var expr_index: usize = 0; while (true) { @@ -1764,6 +1801,12 @@ fn renderArrayInit( .indent_delta = indent_delta, .underlying_writer = sub_expr_buffer.writer(), }; + var sub_render: Render = .{ + .gpa = r.gpa, + .ais = &auto_indenting_stream, + .tree = r.tree, + .fixups = r.fixups, + }; // Calculate size of columns in current section var column_counter: usize = 0; @@ -1774,7 +1817,7 @@ fn renderArrayInit( sub_expr_buffer_starts[i] = start; if (i + 1 < section_exprs.len) { - try renderExpression(gpa, &auto_indenting_stream, tree, expr, .none); + try renderExpression(sub_render, expr, .none); const width = sub_expr_buffer.items.len - start; const this_contains_newline = mem.indexOfScalar(u8, sub_expr_buffer.items[start..], '\n') != null; contains_newline = contains_newline or this_contains_newline; @@ -1794,7 +1837,7 @@ fn renderArrayInit( column_counter = 0; } } else { - try renderExpression(gpa, &auto_indenting_stream, tree, expr, .comma); + try renderExpression(sub_render, expr, .comma); const width = sub_expr_buffer.items.len - start - 2; const this_contains_newline = mem.indexOfScalar(u8, sub_expr_buffer.items[start .. sub_expr_buffer.items.len - 1], '\n') != null; contains_newline = contains_newline or this_contains_newline; @@ -1839,7 +1882,7 @@ fn renderArrayInit( if (column_counter != row_size - 1) { if (!expr_newlines[i] and !expr_newlines[i + 1]) { // Neither the current or next expression is multiline - try renderToken(ais, tree, comma, .space); // , + try renderToken(r, comma, .space); // , assert(column_widths[column_counter % row_size] >= expr_widths[i]); const padding = column_widths[column_counter % row_size] - expr_widths[i]; try ais.writer().writeByteNTimes(' ', padding); @@ -1850,13 +1893,13 @@ fn renderArrayInit( } if (single_line and row_size != 1) { - try renderToken(ais, tree, comma, .space); // , + try renderToken(r, comma, .space); // , continue; } column_counter = 0; - try renderToken(ais, tree, comma, .newline); // , - try renderExtraNewline(ais, tree, next_expr); + try renderToken(r, comma, .newline); // , + try renderExtraNewline(r, next_expr); } } @@ -1865,49 +1908,49 @@ fn renderArrayInit( } ais.popIndent(); - return renderToken(ais, tree, rbrace, space); // rbrace + return renderToken(r, rbrace, space); // rbrace } fn renderContainerDecl( - gpa: Allocator, - ais: *Ais, - tree: Ast, + r: Render, container_decl_node: Ast.Node.Index, container_decl: Ast.full.ContainerDecl, space: Space, ) Error!void { + const tree = r.tree; + const ais = r.ais; const token_tags = tree.tokens.items(.tag); const node_tags = tree.nodes.items(.tag); if (container_decl.layout_token) |layout_token| { - try renderToken(ais, tree, layout_token, .space); + try renderToken(r, layout_token, .space); } var lbrace: Ast.TokenIndex = undefined; if (container_decl.ast.enum_token) |enum_token| { - try renderToken(ais, tree, container_decl.ast.main_token, .none); // union - try renderToken(ais, tree, enum_token - 1, .none); // lparen - try renderToken(ais, tree, enum_token, .none); // enum + try renderToken(r, container_decl.ast.main_token, .none); // union + try renderToken(r, enum_token - 1, .none); // lparen + try renderToken(r, enum_token, .none); // enum if (container_decl.ast.arg != 0) { - try renderToken(ais, tree, enum_token + 1, .none); // lparen - try renderExpression(gpa, ais, tree, container_decl.ast.arg, .none); + try renderToken(r, enum_token + 1, .none); // lparen + try renderExpression(r, container_decl.ast.arg, .none); const rparen = tree.lastToken(container_decl.ast.arg) + 1; - try renderToken(ais, tree, rparen, .none); // rparen - try renderToken(ais, tree, rparen + 1, .space); // rparen + try renderToken(r, rparen, .none); // rparen + try renderToken(r, rparen + 1, .space); // rparen lbrace = rparen + 2; } else { - try renderToken(ais, tree, enum_token + 1, .space); // rparen + try renderToken(r, enum_token + 1, .space); // rparen lbrace = enum_token + 2; } } else if (container_decl.ast.arg != 0) { - try renderToken(ais, tree, container_decl.ast.main_token, .none); // union - try renderToken(ais, tree, container_decl.ast.main_token + 1, .none); // lparen - try renderExpression(gpa, ais, tree, container_decl.ast.arg, .none); + try renderToken(r, container_decl.ast.main_token, .none); // union + try renderToken(r, container_decl.ast.main_token + 1, .none); // lparen + try renderExpression(r, container_decl.ast.arg, .none); const rparen = tree.lastToken(container_decl.ast.arg) + 1; - try renderToken(ais, tree, rparen, .space); // rparen + try renderToken(r, rparen, .space); // rparen lbrace = rparen + 1; } else { - try renderToken(ais, tree, container_decl.ast.main_token, .space); // union + try renderToken(r, container_decl.ast.main_token, .space); // union lbrace = container_decl.ast.main_token + 1; } @@ -1915,13 +1958,13 @@ fn renderContainerDecl( if (container_decl.ast.members.len == 0) { ais.pushIndentNextLine(); if (token_tags[lbrace + 1] == .container_doc_comment) { - try renderToken(ais, tree, lbrace, .newline); // lbrace - try renderContainerDocComments(ais, tree, lbrace + 1); + try renderToken(r, lbrace, .newline); // lbrace + try renderContainerDocComments(r, lbrace + 1); } else { - try renderToken(ais, tree, lbrace, .none); // lbrace + try renderToken(r, lbrace, .none); // lbrace } ais.popIndent(); - return renderToken(ais, tree, rbrace, space); // rbrace + return renderToken(r, rbrace, space); // rbrace } const src_has_trailing_comma = token_tags[rbrace - 1] == .comma; @@ -1947,52 +1990,52 @@ fn renderContainerDecl( } // Print all the declarations on the same line. - try renderToken(ais, tree, lbrace, .space); // lbrace + try renderToken(r, lbrace, .space); // lbrace for (container_decl.ast.members) |member| { - try renderMember(gpa, ais, tree, member, .space); + try renderMember(r, member, .space); } - return renderToken(ais, tree, rbrace, space); // rbrace + return renderToken(r, rbrace, space); // rbrace } // One member per line. ais.pushIndentNextLine(); - try renderToken(ais, tree, lbrace, .newline); // lbrace + try renderToken(r, lbrace, .newline); // lbrace if (token_tags[lbrace + 1] == .container_doc_comment) { - try renderContainerDocComments(ais, tree, lbrace + 1); + try renderContainerDocComments(r, lbrace + 1); } for (container_decl.ast.members) |member, i| { - if (i != 0) try renderExtraNewline(ais, tree, member); + if (i != 0) try renderExtraNewline(r, member); switch (tree.nodes.items(.tag)[member]) { // For container fields, ensure a trailing comma is added if necessary. .container_field_init, .container_field_align, .container_field, - => try renderMember(gpa, ais, tree, member, .comma), + => try renderMember(r, member, .comma), - else => try renderMember(gpa, ais, tree, member, .newline), + else => try renderMember(r, member, .newline), } } ais.popIndent(); - return renderToken(ais, tree, rbrace, space); // rbrace + return renderToken(r, rbrace, space); // rbrace } fn renderAsm( - gpa: Allocator, - ais: *Ais, - tree: Ast, + r: Render, asm_node: Ast.full.Asm, space: Space, ) Error!void { + const tree = r.tree; + const ais = r.ais; const token_tags = tree.tokens.items(.tag); - try renderToken(ais, tree, asm_node.ast.asm_token, .space); // asm + try renderToken(r, asm_node.ast.asm_token, .space); // asm if (asm_node.volatile_token) |volatile_token| { - try renderToken(ais, tree, volatile_token, .space); // volatile - try renderToken(ais, tree, volatile_token + 1, .none); // lparen + try renderToken(r, volatile_token, .space); // volatile + try renderToken(r, volatile_token + 1, .none); // lparen } else { - try renderToken(ais, tree, asm_node.ast.asm_token + 1, .none); // lparen + try renderToken(r, asm_node.ast.asm_token + 1, .none); // lparen } if (asm_node.ast.items.len == 0) { @@ -2000,27 +2043,27 @@ fn renderAsm( if (asm_node.first_clobber) |first_clobber| { // asm ("foo" ::: "a", "b") // asm ("foo" ::: "a", "b",) - try renderExpression(gpa, ais, tree, asm_node.ast.template, .space); + try renderExpression(r, asm_node.ast.template, .space); // Render the three colons. - try renderToken(ais, tree, first_clobber - 3, .none); - try renderToken(ais, tree, first_clobber - 2, .none); - try renderToken(ais, tree, first_clobber - 1, .space); + try renderToken(r, first_clobber - 3, .none); + try renderToken(r, first_clobber - 2, .none); + try renderToken(r, first_clobber - 1, .space); var tok_i = first_clobber; while (true) : (tok_i += 1) { - try renderToken(ais, tree, tok_i, .none); + try renderToken(r, tok_i, .none); tok_i += 1; switch (token_tags[tok_i]) { .r_paren => { ais.popIndent(); - return renderToken(ais, tree, tok_i, space); + return renderToken(r, tok_i, space); }, .comma => { if (token_tags[tok_i + 1] == .r_paren) { ais.popIndent(); - return renderToken(ais, tree, tok_i + 1, space); + return renderToken(r, tok_i + 1, space); } else { - try renderToken(ais, tree, tok_i, .space); + try renderToken(r, tok_i, .space); } }, else => unreachable, @@ -2028,40 +2071,40 @@ fn renderAsm( } } else { // asm ("foo") - try renderExpression(gpa, ais, tree, asm_node.ast.template, .none); + try renderExpression(r, asm_node.ast.template, .none); ais.popIndent(); - return renderToken(ais, tree, asm_node.ast.rparen, space); // rparen + return renderToken(r, asm_node.ast.rparen, space); // rparen } } ais.pushIndent(); - try renderExpression(gpa, ais, tree, asm_node.ast.template, .newline); + try renderExpression(r, asm_node.ast.template, .newline); ais.setIndentDelta(asm_indent_delta); const colon1 = tree.lastToken(asm_node.ast.template) + 1; const colon2 = if (asm_node.outputs.len == 0) colon2: { - try renderToken(ais, tree, colon1, .newline); // : + try renderToken(r, colon1, .newline); // : break :colon2 colon1 + 1; } else colon2: { - try renderToken(ais, tree, colon1, .space); // : + try renderToken(r, colon1, .space); // : ais.pushIndent(); for (asm_node.outputs) |asm_output, i| { if (i + 1 < asm_node.outputs.len) { const next_asm_output = asm_node.outputs[i + 1]; - try renderAsmOutput(gpa, ais, tree, asm_output, .none); + try renderAsmOutput(r, asm_output, .none); const comma = tree.firstToken(next_asm_output) - 1; - try renderToken(ais, tree, comma, .newline); // , - try renderExtraNewlineToken(ais, tree, tree.firstToken(next_asm_output)); + try renderToken(r, comma, .newline); // , + try renderExtraNewlineToken(r, tree.firstToken(next_asm_output)); } else if (asm_node.inputs.len == 0 and asm_node.first_clobber == null) { - try renderAsmOutput(gpa, ais, tree, asm_output, .comma); + try renderAsmOutput(r, asm_output, .comma); ais.popIndent(); ais.setIndentDelta(indent_delta); ais.popIndent(); - return renderToken(ais, tree, asm_node.ast.rparen, space); // rparen + return renderToken(r, asm_node.ast.rparen, space); // rparen } else { - try renderAsmOutput(gpa, ais, tree, asm_output, .comma); + try renderAsmOutput(r, asm_output, .comma); const comma_or_colon = tree.lastToken(asm_output) + 1; ais.popIndent(); break :colon2 switch (token_tags[comma_or_colon]) { @@ -2073,27 +2116,27 @@ fn renderAsm( }; const colon3 = if (asm_node.inputs.len == 0) colon3: { - try renderToken(ais, tree, colon2, .newline); // : + try renderToken(r, colon2, .newline); // : break :colon3 colon2 + 1; } else colon3: { - try renderToken(ais, tree, colon2, .space); // : + try renderToken(r, colon2, .space); // : ais.pushIndent(); for (asm_node.inputs) |asm_input, i| { if (i + 1 < asm_node.inputs.len) { const next_asm_input = asm_node.inputs[i + 1]; - try renderAsmInput(gpa, ais, tree, asm_input, .none); + try renderAsmInput(r, asm_input, .none); const first_token = tree.firstToken(next_asm_input); - try renderToken(ais, tree, first_token - 1, .newline); // , - try renderExtraNewlineToken(ais, tree, first_token); + try renderToken(r, first_token - 1, .newline); // , + try renderExtraNewlineToken(r, first_token); } else if (asm_node.first_clobber == null) { - try renderAsmInput(gpa, ais, tree, asm_input, .comma); + try renderAsmInput(r, asm_input, .comma); ais.popIndent(); ais.setIndentDelta(indent_delta); ais.popIndent(); - return renderToken(ais, tree, asm_node.ast.rparen, space); // rparen + return renderToken(r, asm_node.ast.rparen, space); // rparen } else { - try renderAsmInput(gpa, ais, tree, asm_input, .comma); + try renderAsmInput(r, asm_input, .comma); const comma_or_colon = tree.lastToken(asm_input) + 1; ais.popIndent(); break :colon3 switch (token_tags[comma_or_colon]) { @@ -2105,7 +2148,7 @@ fn renderAsm( unreachable; }; - try renderToken(ais, tree, colon3, .space); // : + try renderToken(r, colon3, .space); // : const first_clobber = asm_node.first_clobber.?; var tok_i = first_clobber; while (true) { @@ -2113,20 +2156,20 @@ fn renderAsm( .r_paren => { ais.setIndentDelta(indent_delta); ais.popIndent(); - try renderToken(ais, tree, tok_i, .newline); - return renderToken(ais, tree, tok_i + 1, space); + try renderToken(r, tok_i, .newline); + return renderToken(r, tok_i + 1, space); }, .comma => { switch (token_tags[tok_i + 2]) { .r_paren => { ais.setIndentDelta(indent_delta); ais.popIndent(); - try renderToken(ais, tree, tok_i, .newline); - return renderToken(ais, tree, tok_i + 2, space); + try renderToken(r, tok_i, .newline); + return renderToken(r, tok_i + 2, space); }, else => { - try renderToken(ais, tree, tok_i, .none); - try renderToken(ais, tree, tok_i + 1, .space); + try renderToken(r, tok_i, .none); + try renderToken(r, tok_i + 1, .space); tok_i += 2; }, } @@ -2137,36 +2180,36 @@ fn renderAsm( } fn renderCall( - gpa: Allocator, - ais: *Ais, - tree: Ast, + r: Render, call: Ast.full.Call, space: Space, ) Error!void { + const tree = r.tree; + const ais = r.ais; const token_tags = tree.tokens.items(.tag); if (call.async_token) |async_token| { - try renderToken(ais, tree, async_token, .space); + try renderToken(r, async_token, .space); } - try renderExpression(gpa, ais, tree, call.ast.fn_expr, .none); + try renderExpression(r, call.ast.fn_expr, .none); const lparen = call.ast.lparen; const params = call.ast.params; if (params.len == 0) { ais.pushIndentNextLine(); - try renderToken(ais, tree, lparen, .none); + try renderToken(r, lparen, .none); ais.popIndent(); - return renderToken(ais, tree, lparen + 1, space); // ) + return renderToken(r, lparen + 1, space); // ) } const last_param = params[params.len - 1]; const after_last_param_tok = tree.lastToken(last_param) + 1; if (token_tags[after_last_param_tok] == .comma) { ais.pushIndentNextLine(); - try renderToken(ais, tree, lparen, .newline); // ( + try renderToken(r, lparen, .newline); // ( for (params) |param_node, i| { if (i + 1 < params.len) { - try renderExpression(gpa, ais, tree, param_node, .none); + try renderExpression(r, param_node, .none); // Unindent the comma for multiline string literals. const is_multiline_string = @@ -2174,20 +2217,20 @@ fn renderCall( if (is_multiline_string) ais.popIndent(); const comma = tree.lastToken(param_node) + 1; - try renderToken(ais, tree, comma, .newline); // , + try renderToken(r, comma, .newline); // , if (is_multiline_string) ais.pushIndent(); - try renderExtraNewline(ais, tree, params[i + 1]); + try renderExtraNewline(r, params[i + 1]); } else { - try renderExpression(gpa, ais, tree, param_node, .comma); + try renderExpression(r, param_node, .comma); } } ais.popIndent(); - return renderToken(ais, tree, after_last_param_tok + 1, space); // ) + return renderToken(r, after_last_param_tok + 1, space); // ) } - try renderToken(ais, tree, lparen, .none); // ( + try renderToken(r, lparen, .none); // ( for (params) |param_node, i| { const first_param_token = tree.firstToken(param_node); @@ -2196,23 +2239,25 @@ fn renderCall( { ais.pushIndentOneShot(); } - try renderExpression(gpa, ais, tree, param_node, .none); + try renderExpression(r, param_node, .none); if (i + 1 < params.len) { const comma = tree.lastToken(param_node) + 1; const next_multiline_string = token_tags[tree.firstToken(params[i + 1])] == .multiline_string_literal_line; const comma_space: Space = if (next_multiline_string) .none else .space; - try renderToken(ais, tree, comma, comma_space); + try renderToken(r, comma, comma_space); } } - return renderToken(ais, tree, after_last_param_tok, space); // ) + return renderToken(r, after_last_param_tok, space); // ) } /// Renders the given expression indented, popping the indent before rendering /// any following line comments -fn renderExpressionIndented(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index, space: Space) Error!void { +fn renderExpressionIndented(r: Render, node: Ast.Node.Index, space: Space) Error!void { + const tree = r.tree; + const ais = r.ais; const token_starts = tree.tokens.items(.start); const token_tags = tree.tokens.items(.tag); @@ -2226,24 +2271,24 @@ fn renderExpressionIndented(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node .semicolon => token_tags[last_token + 1] == .semicolon, }; - try renderExpression(gpa, ais, tree, node, if (punctuation) .none else .skip); + try renderExpression(r, node, if (punctuation) .none else .skip); switch (space) { .none, .space, .newline, .skip => {}, .comma => { if (token_tags[last_token + 1] == .comma) { - try renderToken(ais, tree, last_token + 1, .skip); + try renderToken(r, last_token + 1, .skip); last_token += 1; } else { try ais.writer().writeByte(','); } }, .comma_space => if (token_tags[last_token + 1] == .comma) { - try renderToken(ais, tree, last_token + 1, .skip); + try renderToken(r, last_token + 1, .skip); last_token += 1; }, .semicolon => if (token_tags[last_token + 1] == .semicolon) { - try renderToken(ais, tree, last_token + 1, .skip); + try renderToken(r, last_token + 1, .skip); last_token += 1; }, } @@ -2253,7 +2298,7 @@ fn renderExpressionIndented(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node if (space == .skip) return; const comment_start = token_starts[last_token] + tokenSliceForRender(tree, last_token).len; - const comment = try renderComments(ais, tree, comment_start, token_starts[last_token + 1]); + const comment = try renderComments(r, comment_start, token_starts[last_token + 1]); if (!comment) switch (space) { .none => {}, @@ -2270,27 +2315,29 @@ fn renderExpressionIndented(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node /// Render an expression, and the comma that follows it, if it is present in the source. /// If a comma is present, and `space` is `Space.comma`, render only a single comma. -fn renderExpressionComma(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index, space: Space) Error!void { +fn renderExpressionComma(r: Render, node: Ast.Node.Index, space: Space) Error!void { + const tree = r.tree; const token_tags = tree.tokens.items(.tag); const maybe_comma = tree.lastToken(node) + 1; if (token_tags[maybe_comma] == .comma and space != .comma) { - try renderExpression(gpa, ais, tree, node, .none); - return renderToken(ais, tree, maybe_comma, space); + try renderExpression(r, node, .none); + return renderToken(r, maybe_comma, space); } else { - return renderExpression(gpa, ais, tree, node, space); + return renderExpression(r, node, space); } } /// Render a token, and the comma that follows it, if it is present in the source. /// If a comma is present, and `space` is `Space.comma`, render only a single comma. -fn renderTokenComma(ais: *Ais, tree: Ast, token: Ast.TokenIndex, space: Space) Error!void { +fn renderTokenComma(r: Render, token: Ast.TokenIndex, space: Space) Error!void { + const tree = r.tree; const token_tags = tree.tokens.items(.tag); const maybe_comma = token + 1; if (token_tags[maybe_comma] == .comma and space != .comma) { - try renderToken(ais, tree, token, .none); - return renderToken(ais, tree, maybe_comma, space); + try renderToken(r, token, .none); + return renderToken(r, maybe_comma, space); } else { - return renderToken(ais, tree, token, space); + return renderToken(r, token, space); } } @@ -2315,13 +2362,17 @@ const Space = enum { skip, }; -fn renderToken(ais: *Ais, tree: Ast, token_index: Ast.TokenIndex, space: Space) Error!void { +fn renderToken(r: Render, token_index: Ast.TokenIndex, space: Space) Error!void { + const tree = r.tree; + const ais = r.ais; const lexeme = tokenSliceForRender(tree, token_index); try ais.writer().writeAll(lexeme); - try renderSpace(ais, tree, token_index, lexeme.len, space); + try renderSpace(r, token_index, lexeme.len, space); } -fn renderSpace(ais: *Ais, tree: Ast, token_index: Ast.TokenIndex, lexeme_len: usize, space: Space) Error!void { +fn renderSpace(r: Render, token_index: Ast.TokenIndex, lexeme_len: usize, space: Space) Error!void { + const tree = r.tree; + const ais = r.ais; const token_tags = tree.tokens.items(.tag); const token_starts = tree.tokens.items(.start); @@ -2333,26 +2384,26 @@ fn renderSpace(ais: *Ais, tree: Ast, token_index: Ast.TokenIndex, lexeme_len: us try ais.writer().writeByte(','); } - const comment = try renderComments(ais, tree, token_start + lexeme_len, token_starts[token_index + 1]); + const comment = try renderComments(r, token_start + lexeme_len, token_starts[token_index + 1]); switch (space) { .none => {}, .space => if (!comment) try ais.writer().writeByte(' '), .newline => if (!comment) try ais.insertNewline(), .comma => if (token_tags[token_index + 1] == .comma) { - try renderToken(ais, tree, token_index + 1, .newline); + try renderToken(r, token_index + 1, .newline); } else if (!comment) { try ais.insertNewline(); }, .comma_space => if (token_tags[token_index + 1] == .comma) { - try renderToken(ais, tree, token_index + 1, .space); + try renderToken(r, token_index + 1, .space); } else if (!comment) { try ais.writer().writeByte(' '); }, .semicolon => if (token_tags[token_index + 1] == .semicolon) { - try renderToken(ais, tree, token_index + 1, .newline); + try renderToken(r, token_index + 1, .newline); } else if (!comment) { try ais.insertNewline(); }, @@ -2395,7 +2446,10 @@ fn hasMultilineString(tree: Ast, start_token: Ast.TokenIndex, end_token: Ast.Tok /// Assumes that start is the first byte past the previous token and /// that end is the last byte before the next token. -fn renderComments(ais: *Ais, tree: Ast, start: usize, end: usize) Error!bool { +fn renderComments(r: Render, start: usize, end: usize) Error!bool { + const tree = r.tree; + const ais = r.ais; + var index: usize = start; while (mem.indexOf(u8, tree.source[index..end], "//")) |offset| { const comment_start = index + offset; @@ -2452,12 +2506,14 @@ fn renderComments(ais: *Ais, tree: Ast, start: usize, end: usize) Error!bool { return index != start; } -fn renderExtraNewline(ais: *Ais, tree: Ast, node: Ast.Node.Index) Error!void { - return renderExtraNewlineToken(ais, tree, tree.firstToken(node)); +fn renderExtraNewline(r: Render, node: Ast.Node.Index) Error!void { + return renderExtraNewlineToken(r, r.tree.firstToken(node)); } /// Check if there is an empty line immediately before the given token. If so, render it. -fn renderExtraNewlineToken(ais: *Ais, tree: Ast, token_index: Ast.TokenIndex) Error!void { +fn renderExtraNewlineToken(r: Render, token_index: Ast.TokenIndex) Error!void { + const tree = r.tree; + const ais = r.ais; const token_starts = tree.tokens.items(.start); const token_start = token_starts[token_index]; if (token_start == 0) return; @@ -2482,7 +2538,8 @@ fn renderExtraNewlineToken(ais: *Ais, tree: Ast, token_index: Ast.TokenIndex) Er /// end_token is the token one past the last doc comment token. This function /// searches backwards from there. -fn renderDocComments(ais: *Ais, tree: Ast, end_token: Ast.TokenIndex) Error!void { +fn renderDocComments(r: Render, end_token: Ast.TokenIndex) Error!void { + const tree = r.tree; // Search backwards for the first doc comment. const token_tags = tree.tokens.items(.tag); if (end_token == 0) return; @@ -2503,27 +2560,28 @@ fn renderDocComments(ais: *Ais, tree: Ast, end_token: Ast.TokenIndex) Error!void assert(prev_token_tag != .l_paren); if (prev_token_tag != .l_brace) { - try renderExtraNewlineToken(ais, tree, first_tok); + try renderExtraNewlineToken(r, first_tok); } } while (token_tags[tok] == .doc_comment) : (tok += 1) { - try renderToken(ais, tree, tok, .newline); + try renderToken(r, tok, .newline); } } /// start_token is first container doc comment token. -fn renderContainerDocComments(ais: *Ais, tree: Ast, start_token: Ast.TokenIndex) Error!void { +fn renderContainerDocComments(r: Render, start_token: Ast.TokenIndex) Error!void { + const tree = r.tree; const token_tags = tree.tokens.items(.tag); var tok = start_token; while (token_tags[tok] == .container_doc_comment) : (tok += 1) { - try renderToken(ais, tree, tok, .newline); + try renderToken(r, tok, .newline); } // Render extra newline if there is one between final container doc comment and // the next token. If the next token is a doc comment, that code path // will have its own logic to insert a newline. if (token_tags[tok] != .doc_comment) { - try renderExtraNewlineToken(ais, tree, tok); + try renderExtraNewlineToken(r, tok); } } diff --git a/src/Compilation.zig b/src/Compilation.zig index 597f5cffff..09c25abc6d 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -93,6 +93,7 @@ unwind_tables: bool, test_evented_io: bool, debug_compiler_runtime_libs: bool, debug_compile_errors: bool, +autofix: bool, job_queued_compiler_rt_lib: bool = false, job_queued_compiler_rt_obj: bool = false, alloc_failure_occurred: bool = false, @@ -856,6 +857,7 @@ pub const InitOptions = struct { single_threaded: ?bool = null, rdynamic: bool = false, strip: bool = false, + autofix: bool = false, function_sections: bool = false, no_builtin: bool = false, is_native_os: bool, @@ -1838,6 +1840,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { .color = options.color, .time_report = options.time_report, .stack_report = options.stack_report, + .autofix = options.autofix, .unwind_tables = unwind_tables, .test_filter = options.test_filter, .test_name_prefix = options.test_name_prefix, diff --git a/src/Module.zig b/src/Module.zig index ea89225537..f56f02275a 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -3493,7 +3493,11 @@ pub fn astGenFile(mod: *Module, file: *File) !void { const gpa = mod.gpa; // In any case we need to examine the stat of the file to determine the course of action. - var source_file = try file.pkg.root_src_directory.handle.openFile(file.sub_file_path, .{}); + const want_autofix = comp.autofix and mod.main_pkg == file.pkg; + const open_flags: std.fs.File.OpenMode = if (want_autofix) .read_write else .read_only; + var source_file = try file.pkg.root_src_directory.handle.openFile(file.sub_file_path, .{ + .mode = open_flags, + }); defer source_file.close(); const stat = try source_file.stat(); @@ -3643,12 +3647,17 @@ pub fn astGenFile(mod: *Module, file: *File) !void { // TODO don't report compile errors until Sema @importFile if (file.zir.hasCompileErrors()) { + file.status = .astgen_failure; + + // If autofix is requested, treat this is a cache miss so that + // the autofix code has a chance to run. + if (want_autofix) break :cached; + { comp.mutex.lock(); defer comp.mutex.unlock(); try mod.failed_files.putNoClobber(gpa, file, null); } - file.status = .astgen_failure; return error.AnalysisFail; } return; @@ -3726,72 +3735,134 @@ pub fn astGenFile(mod: *Module, file: *File) !void { file.source = source; file.source_loaded = true; - file.tree = try std.zig.parse(gpa, source); - defer if (!file.tree_loaded) file.tree.deinit(gpa); + // This is purely to detect when the autofix mechanism has a bug and + // the autofix did not make the compile error go away. + var reparsing = false; + parse: while (true) { + file.tree = try std.zig.parse(gpa, file.source); + defer if (!file.tree_loaded) file.tree.deinit(gpa); - if (file.tree.errors.len != 0) { - const parse_err = file.tree.errors[0]; + if (file.tree.errors.len != 0) { + const parse_err = file.tree.errors[0]; - var msg = std.ArrayList(u8).init(gpa); - defer msg.deinit(); + var msg = std.ArrayList(u8).init(gpa); + defer msg.deinit(); - const token_starts = file.tree.tokens.items(.start); - const token_tags = file.tree.tokens.items(.tag); + const token_starts = file.tree.tokens.items(.start); + const token_tags = file.tree.tokens.items(.tag); - const extra_offset = file.tree.errorOffset(parse_err); - try file.tree.renderError(parse_err, msg.writer()); - const err_msg = try gpa.create(ErrorMsg); - err_msg.* = .{ - .src_loc = .{ - .file_scope = file, - .parent_decl_node = 0, - .lazy = if (extra_offset == 0) .{ - .token_abs = parse_err.token, - } else .{ - .byte_abs = token_starts[parse_err.token] + extra_offset, - }, - }, - .msg = msg.toOwnedSlice(), - }; - if (token_tags[parse_err.token + @boolToInt(parse_err.token_is_prev)] == .invalid) { - const bad_off = @intCast(u32, file.tree.tokenSlice(parse_err.token + @boolToInt(parse_err.token_is_prev)).len); - const byte_abs = token_starts[parse_err.token + @boolToInt(parse_err.token_is_prev)] + bad_off; - try mod.errNoteNonLazy(.{ - .file_scope = file, - .parent_decl_node = 0, - .lazy = .{ .byte_abs = byte_abs }, - }, err_msg, "invalid byte: '{'}'", .{std.zig.fmtEscapes(source[byte_abs..][0..1])}); - } - - for (file.tree.errors[1..]) |note| { - if (!note.is_note) break; - - try file.tree.renderError(note, msg.writer()); - err_msg.notes = try mod.gpa.realloc(err_msg.notes, err_msg.notes.len + 1); - err_msg.notes[err_msg.notes.len - 1] = .{ + const extra_offset = file.tree.errorOffset(parse_err); + try file.tree.renderError(parse_err, msg.writer()); + const err_msg = try gpa.create(ErrorMsg); + err_msg.* = .{ .src_loc = .{ .file_scope = file, .parent_decl_node = 0, - .lazy = .{ .token_abs = note.token }, + .lazy = if (extra_offset == 0) .{ + .token_abs = parse_err.token, + } else .{ + .byte_abs = token_starts[parse_err.token] + extra_offset, + }, }, .msg = msg.toOwnedSlice(), }; + if (token_tags[parse_err.token + @boolToInt(parse_err.token_is_prev)] == .invalid) { + const bad_off = @intCast(u32, file.tree.tokenSlice(parse_err.token + @boolToInt(parse_err.token_is_prev)).len); + const byte_abs = token_starts[parse_err.token + @boolToInt(parse_err.token_is_prev)] + bad_off; + try mod.errNoteNonLazy(.{ + .file_scope = file, + .parent_decl_node = 0, + .lazy = .{ .byte_abs = byte_abs }, + }, err_msg, "invalid byte: '{'}'", .{std.zig.fmtEscapes(file.source[byte_abs..][0..1])}); + } + + for (file.tree.errors[1..]) |note| { + if (!note.is_note) break; + + try file.tree.renderError(note, msg.writer()); + err_msg.notes = try mod.gpa.realloc(err_msg.notes, err_msg.notes.len + 1); + err_msg.notes[err_msg.notes.len - 1] = .{ + .src_loc = .{ + .file_scope = file, + .parent_decl_node = 0, + .lazy = .{ .token_abs = note.token }, + }, + .msg = msg.toOwnedSlice(), + }; + } + + { + comp.mutex.lock(); + defer comp.mutex.unlock(); + try mod.failed_files.putNoClobber(gpa, file, err_msg); + } + file.status = .parse_failure; + return error.AnalysisFail; + } + file.tree_loaded = true; + + file.zir = try AstGen.generate(gpa, file.tree); + file.zir_loaded = true; + file.status = .success_zir; + log.debug("AstGen fresh success: {s}", .{file.sub_file_path}); + + if (want_autofix and file.zir.hasCompileErrors()) { + assert(!reparsing); // there are still compile errors after autofixing + const payload_index = file.zir.extra[@enumToInt(Zir.ExtraIndex.compile_errors)]; + assert(payload_index != 0); + const header = file.zir.extraData(Zir.Inst.CompileErrors, payload_index); + const items_len = header.data.items_len; + var fixups: Ast.Fixups = .{}; + defer fixups.deinit(gpa); + var extra_index = header.end; + var item_i: usize = 0; + while (item_i < items_len) : (item_i += 1) { + const item = file.zir.extraData(Zir.Inst.CompileErrors.Item, extra_index); + extra_index = item.end; + const msg = file.zir.nullTerminatedString(item.data.msg); + if (mem.eql(u8, msg, "unused local constant")) { + // goal: insert "_ = identifier;" after the variable declaration + const ident_token = item.data.token; + try fixups.unused_var_decls.put(gpa, ident_token - 1, {}); + } else { + std.debug.print("found other ZIR error: '{s}'\n", .{msg}); + } + } + + if (fixups.count() > 0) { + var autofixed_source = std.ArrayList(u8).init(gpa); + defer autofixed_source.deinit(); + try file.tree.renderWithFixups(&autofixed_source, fixups); + + // Replace the file source with the fixed up source. + const new_source = try autofixed_source.toOwnedSliceSentinel(0); + gpa.free(file.source); + file.source = new_source; + + // Overwrite the source file on disk with the fixed up source. + try source_file.pwriteAll(file.source, 0); + + // If the fixed up source is smaller in bytes then we need to + // truncate the file. + if (file.stat.size > file.source.len) { + try source_file.setEndPos(file.source.len); + } + + // Redo the stat so that next time we get a cache hit. + const new_stat = try source_file.stat(); + file.stat = .{ + .size = new_stat.size, + .inode = new_stat.inode, + .mtime = new_stat.mtime, + }; + + reparsing = true; + continue :parse; + } } - { - comp.mutex.lock(); - defer comp.mutex.unlock(); - try mod.failed_files.putNoClobber(gpa, file, err_msg); - } - file.status = .parse_failure; - return error.AnalysisFail; + break :parse; } - file.tree_loaded = true; - - file.zir = try AstGen.generate(gpa, file.tree); - file.zir_loaded = true; - file.status = .success_zir; - log.debug("AstGen fresh success: {s}", .{file.sub_file_path}); const safety_buffer = if (data_has_safety_tag) try gpa.alloc([8]u8, file.zir.instructions.len) diff --git a/src/main.zig b/src/main.zig index aaea682c7b..25c1f6bdb3 100644 --- a/src/main.zig +++ b/src/main.zig @@ -403,6 +403,7 @@ const usage_build_generic = \\ -ffunction-sections Places each function in a separate section \\ -fno-function-sections All functions go into same section \\ --strip Omit debug symbols + \\ --autofix Avoid some errors by allowing Zig to edit your code \\ -ofmt=[mode] Override target object format \\ elf Executable and Linking Format \\ c C source code @@ -629,6 +630,7 @@ fn buildOutputType( var have_version = false; var compatibility_version: ?std.builtin.Version = null; var strip = false; + var autofix = false; var function_sections = false; var no_builtin = false; var watch = false; @@ -1286,6 +1288,8 @@ fn buildOutputType( emit_bin = .no; } else if (mem.eql(u8, arg, "--strip")) { strip = true; + } else if (mem.eql(u8, arg, "--autofix")) { + autofix = true; } else if (mem.eql(u8, arg, "-fsingle-threaded")) { single_threaded = true; } else if (mem.eql(u8, arg, "-fno-single-threaded")) { @@ -2944,6 +2948,7 @@ fn buildOutputType( .stack_size_override = stack_size_override, .image_base_override = image_base_override, .strip = strip, + .autofix = autofix, .single_threaded = single_threaded, .function_sections = function_sections, .no_builtin = no_builtin,