authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-11-04 14:25:50-04:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2023-11-04 14:25:50-04:00
log98dc28bbe223cb7183aabe7ed7a847c67c1a4df9
tree41185ee2963e41dbe3ca4f121ece9d6aab54bb3b
parenta7d8cd591c47536b0a1e359bf3b1806fc057ffe9
parent31529f912bae6fae32d2a25e873dcc23ba4e96c4
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #17852 from ziglang/zig-reduce

introduce `zig reduce` subcommand

9 files changed, 2000 insertions(+), 665 deletions(-)

build.zig+4
...@@ -33,6 +33,7 @@ pub fn build(b: *std.Build) !void {...@@ -33,6 +33,7 @@ pub fn build(b: *std.Build) !void {
33 const skip_install_langref = b.option(bool, "no-langref", "skip copying of langref to the installation prefix") orelse skip_install_lib_files;33 const skip_install_langref = b.option(bool, "no-langref", "skip copying of langref to the installation prefix") orelse skip_install_lib_files;
34 const skip_install_autodocs = b.option(bool, "no-autodocs", "skip copying of standard library autodocs to the installation prefix") orelse skip_install_lib_files;34 const skip_install_autodocs = b.option(bool, "no-autodocs", "skip copying of standard library autodocs to the installation prefix") orelse skip_install_lib_files;
35 const no_bin = b.option(bool, "no-bin", "skip emitting compiler binary") orelse false;35 const no_bin = b.option(bool, "no-bin", "skip emitting compiler binary") orelse false;
36 const only_reduce = b.option(bool, "only-reduce", "only build zig reduce") orelse false;
3637
37 const docgen_exe = b.addExecutable(.{38 const docgen_exe = b.addExecutable(.{
38 .name = "docgen",39 .name = "docgen",
...@@ -236,6 +237,7 @@ pub fn build(b: *std.Build) !void {...@@ -236,6 +237,7 @@ pub fn build(b: *std.Build) !void {
236 exe_options.addOption(bool, "force_gpa", force_gpa);237 exe_options.addOption(bool, "force_gpa", force_gpa);
237 exe_options.addOption(bool, "only_c", only_c);238 exe_options.addOption(bool, "only_c", only_c);
238 exe_options.addOption(bool, "only_core_functionality", only_c);239 exe_options.addOption(bool, "only_core_functionality", only_c);
240 exe_options.addOption(bool, "only_reduce", only_reduce);
239241
240 if (link_libc) {242 if (link_libc) {
241 exe.linkLibC();243 exe.linkLibC();
...@@ -391,6 +393,7 @@ pub fn build(b: *std.Build) !void {...@@ -391,6 +393,7 @@ pub fn build(b: *std.Build) !void {
391 test_cases_options.addOption(bool, "force_gpa", force_gpa);393 test_cases_options.addOption(bool, "force_gpa", force_gpa);
392 test_cases_options.addOption(bool, "only_c", only_c);394 test_cases_options.addOption(bool, "only_c", only_c);
393 test_cases_options.addOption(bool, "only_core_functionality", true);395 test_cases_options.addOption(bool, "only_core_functionality", true);
396 test_cases_options.addOption(bool, "only_reduce", false);
394 test_cases_options.addOption(bool, "enable_qemu", b.enable_qemu);397 test_cases_options.addOption(bool, "enable_qemu", b.enable_qemu);
395 test_cases_options.addOption(bool, "enable_wine", b.enable_wine);398 test_cases_options.addOption(bool, "enable_wine", b.enable_wine);
396 test_cases_options.addOption(bool, "enable_wasmtime", b.enable_wasmtime);399 test_cases_options.addOption(bool, "enable_wasmtime", b.enable_wasmtime);
...@@ -549,6 +552,7 @@ fn addWasiUpdateStep(b: *std.Build, version: [:0]const u8) !void {...@@ -549,6 +552,7 @@ fn addWasiUpdateStep(b: *std.Build, version: [:0]const u8) !void {
549 exe_options.addOption(bool, "enable_tracy_allocation", false);552 exe_options.addOption(bool, "enable_tracy_allocation", false);
550 exe_options.addOption(bool, "value_tracing", false);553 exe_options.addOption(bool, "value_tracing", false);
551 exe_options.addOption(bool, "only_core_functionality", true);554 exe_options.addOption(bool, "only_core_functionality", true);
555 exe_options.addOption(bool, "only_reduce", false);
552556
553 const run_opt = b.addSystemCommand(&.{557 const run_opt = b.addSystemCommand(&.{
554 "wasm-opt",558 "wasm-opt",
lib/build_runner.zig+1-1
...@@ -203,7 +203,7 @@ pub fn main() !void {...@@ -203,7 +203,7 @@ pub fn main() !void {
203 usageAndErr(builder, false, stderr_stream);203 usageAndErr(builder, false, stderr_stream);
204 };204 };
205 seed = std.fmt.parseUnsigned(u32, next_arg, 0) catch |err| {205 seed = std.fmt.parseUnsigned(u32, next_arg, 0) catch |err| {
206 std.debug.print("unable to parse seed '{s}' as 32-bit integer: {s}", .{206 std.debug.print("unable to parse seed '{s}' as 32-bit integer: {s}\n", .{
207 next_arg, @errorName(err),207 next_arg, @errorName(err),
208 });208 });
209 process.exit(1);209 process.exit(1);
lib/std/bit_set.zig+12
...@@ -859,6 +859,18 @@ pub const DynamicBitSetUnmanaged = struct {...@@ -859,6 +859,18 @@ pub const DynamicBitSetUnmanaged = struct {
859 self.masks[maskIndex(index)] &= ~maskBit(index);859 self.masks[maskIndex(index)] &= ~maskBit(index);
860 }860 }
861861
862 /// Set all bits to 0.
863 pub fn unsetAll(self: *Self) void {
864 const masks_len = numMasks(self.bit_length);
865 @memset(self.masks[0..masks_len], 0);
866 }
867
868 /// Set all bits to 1.
869 pub fn setAll(self: *Self) void {
870 const masks_len = numMasks(self.bit_length);
871 @memset(self.masks[0..masks_len], std.math.maxInt(MaskInt));
872 }
873
862 /// Flips a specific bit in the bit set874 /// Flips a specific bit in the bit set
863 pub fn toggle(self: *Self, index: usize) void {875 pub fn toggle(self: *Self, index: usize) void {
864 assert(index < self.bit_length);876 assert(index < self.bit_length);
lib/std/zig/Ast.zig+6-3
...@@ -113,12 +113,14 @@ pub fn render(tree: Ast, gpa: Allocator) RenderError![]u8 {...@@ -113,12 +113,14 @@ pub fn render(tree: Ast, gpa: Allocator) RenderError![]u8 {
113 var buffer = std.ArrayList(u8).init(gpa);113 var buffer = std.ArrayList(u8).init(gpa);
114 defer buffer.deinit();114 defer buffer.deinit();
115115
116 try tree.renderToArrayList(&buffer);116 try tree.renderToArrayList(&buffer, .{});
117 return buffer.toOwnedSlice();117 return buffer.toOwnedSlice();
118}118}
119119
120pub fn renderToArrayList(tree: Ast, buffer: *std.ArrayList(u8)) RenderError!void {120pub const Fixups = private_render.Fixups;
121 return @import("./render.zig").renderTree(buffer, tree);121
122pub fn renderToArrayList(tree: Ast, buffer: *std.ArrayList(u8), fixups: Fixups) RenderError!void {
123 return @import("./render.zig").renderTree(buffer, tree, fixups);
122}124}
123125
124/// Returns an extra offset for column and byte offset of errors that126/// Returns an extra offset for column and byte offset of errors that
...@@ -3530,6 +3532,7 @@ const Token = std.zig.Token;...@@ -3530,6 +3532,7 @@ const Token = std.zig.Token;
3530const Ast = @This();3532const Ast = @This();
3531const Allocator = std.mem.Allocator;3533const Allocator = std.mem.Allocator;
3532const Parse = @import("Parse.zig");3534const Parse = @import("Parse.zig");
3535const private_render = @import("./render.zig");
35333536
3534test {3537test {
3535 testing.refAllDecls(@This());3538 testing.refAllDecls(@This());
lib/std/zig/render.zig+792-660
...@@ -14,49 +14,96 @@ pub const Error = Ast.RenderError;...@@ -14,49 +14,96 @@ pub const Error = Ast.RenderError;
1414
15const Ais = AutoIndentingStream(std.ArrayList(u8).Writer);15const Ais = AutoIndentingStream(std.ArrayList(u8).Writer);
1616
17pub fn renderTree(buffer: *std.ArrayList(u8), tree: Ast) Error!void {17pub const Fixups = struct {
18 /// The key is the mut token (`var`/`const`) of the variable declaration
19 /// that should have a `_ = foo;` inserted afterwards.
20 unused_var_decls: std.AutoHashMapUnmanaged(Ast.TokenIndex, void) = .{},
21 /// The functions in this unordered set of AST fn decl nodes will render
22 /// with a function body of `@trap()` instead, with all parameters
23 /// discarded.
24 gut_functions: std.AutoHashMapUnmanaged(Ast.Node.Index, void) = .{},
25 /// These global declarations will be omitted.
26 omit_nodes: std.AutoHashMapUnmanaged(Ast.Node.Index, void) = .{},
27 /// These expressions will be replaced with `undefined`.
28 replace_nodes: std.AutoHashMapUnmanaged(Ast.Node.Index, void) = .{},
29
30 pub fn count(f: Fixups) usize {
31 return f.unused_var_decls.count() +
32 f.gut_functions.count() +
33 f.omit_nodes.count() +
34 f.replace_nodes.count();
35 }
36
37 pub fn clearRetainingCapacity(f: *Fixups) void {
38 f.unused_var_decls.clearRetainingCapacity();
39 f.gut_functions.clearRetainingCapacity();
40 f.omit_nodes.clearRetainingCapacity();
41 f.replace_nodes.clearRetainingCapacity();
42 }
43
44 pub fn deinit(f: *Fixups, gpa: Allocator) void {
45 f.unused_var_decls.deinit(gpa);
46 f.gut_functions.deinit(gpa);
47 f.omit_nodes.deinit(gpa);
48 f.replace_nodes.deinit(gpa);
49 f.* = undefined;
50 }
51};
52
53const Render = struct {
54 gpa: Allocator,
55 ais: *Ais,
56 tree: Ast,
57 fixups: Fixups,
58};
59
60pub fn renderTree(buffer: *std.ArrayList(u8), tree: Ast, fixups: Fixups) Error!void {
18 assert(tree.errors.len == 0); // Cannot render an invalid tree.61 assert(tree.errors.len == 0); // Cannot render an invalid tree.
19 var auto_indenting_stream = Ais{62 var auto_indenting_stream = Ais{
20 .indent_delta = indent_delta,63 .indent_delta = indent_delta,
21 .underlying_writer = buffer.writer(),64 .underlying_writer = buffer.writer(),
22 };65 };
23 const ais = &auto_indenting_stream;66 var r: Render = .{
67 .gpa = buffer.allocator,
68 .ais = &auto_indenting_stream,
69 .tree = tree,
70 .fixups = fixups,
71 };
2472
25 // Render all the line comments at the beginning of the file.73 // Render all the line comments at the beginning of the file.
26 const comment_end_loc = tree.tokens.items(.start)[0];74 const comment_end_loc = tree.tokens.items(.start)[0];
27 _ = try renderComments(ais, tree, 0, comment_end_loc);75 _ = try renderComments(&r, 0, comment_end_loc);
2876
29 if (tree.tokens.items(.tag)[0] == .container_doc_comment) {77 if (tree.tokens.items(.tag)[0] == .container_doc_comment) {
30 try renderContainerDocComments(ais, tree, 0);78 try renderContainerDocComments(&r, 0);
31 }79 }
3280
33 if (tree.mode == .zon) {81 if (tree.mode == .zon) {
34 try renderExpression(82 try renderExpression(
35 buffer.allocator,83 &r,
36 ais,
37 tree,
38 tree.nodes.items(.data)[0].lhs,84 tree.nodes.items(.data)[0].lhs,
39 .newline,85 .newline,
40 );86 );
41 } else {87 } else {
42 try renderMembers(buffer.allocator, ais, tree, tree.rootDecls());88 try renderMembers(&r, tree.rootDecls());
43 }89 }
4490
45 if (ais.disabled_offset) |disabled_offset| {91 if (auto_indenting_stream.disabled_offset) |disabled_offset| {
46 try writeFixingWhitespace(ais.underlying_writer, tree.source[disabled_offset..]);92 try writeFixingWhitespace(auto_indenting_stream.underlying_writer, tree.source[disabled_offset..]);
47 }93 }
48}94}
4995
50/// Render all members in the given slice, keeping empty lines where appropriate96/// Render all members in the given slice, keeping empty lines where appropriate
51fn renderMembers(gpa: Allocator, ais: *Ais, tree: Ast, members: []const Ast.Node.Index) Error!void {97fn renderMembers(r: *Render, members: []const Ast.Node.Index) Error!void {
98 const tree = r.tree;
52 if (members.len == 0) return;99 if (members.len == 0) return;
53 const container: Container = for (members) |member| {100 const container: Container = for (members) |member| {
54 if (tree.fullContainerField(member)) |field| if (!field.ast.tuple_like) break .other;101 if (tree.fullContainerField(member)) |field| if (!field.ast.tuple_like) break .other;
55 } else .tuple;102 } else .tuple;
56 try renderMember(gpa, ais, tree, container, members[0], .newline);103 try renderMember(r, container, members[0], .newline);
57 for (members[1..]) |member| {104 for (members[1..]) |member| {
58 try renderExtraNewline(ais, tree, member);105 try renderExtraNewline(r, member);
59 try renderMember(gpa, ais, tree, container, member, .newline);106 try renderMember(r, container, member, .newline);
60 }107 }
61}108}
62109
...@@ -67,17 +114,18 @@ const Container = enum {...@@ -67,17 +114,18 @@ const Container = enum {
67};114};
68115
69fn renderMember(116fn renderMember(
70 gpa: Allocator,117 r: *Render,
71 ais: *Ais,
72 tree: Ast,
73 container: Container,118 container: Container,
74 decl: Ast.Node.Index,119 decl: Ast.Node.Index,
75 space: Space,120 space: Space,
76) Error!void {121) Error!void {
122 const tree = r.tree;
123 const ais = r.ais;
77 const token_tags = tree.tokens.items(.tag);124 const token_tags = tree.tokens.items(.tag);
78 const main_tokens = tree.nodes.items(.main_token);125 const main_tokens = tree.nodes.items(.main_token);
79 const datas = tree.nodes.items(.data);126 const datas = tree.nodes.items(.data);
80 try renderDocComments(ais, tree, tree.firstToken(decl));127 if (r.fixups.omit_nodes.contains(decl)) return;
128 try renderDocComments(r, tree.firstToken(decl));
81 switch (tree.nodes.items(.tag)[decl]) {129 switch (tree.nodes.items(.tag)[decl]) {
82 .fn_decl => {130 .fn_decl => {
83 // Some examples:131 // Some examples:
...@@ -105,7 +153,7 @@ fn renderMember(...@@ -105,7 +153,7 @@ fn renderMember(
105 }153 }
106 }154 }
107 while (i < fn_token) : (i += 1) {155 while (i < fn_token) : (i += 1) {
108 try renderToken(ais, tree, i, .space);156 try renderToken(r, i, .space);
109 }157 }
110 switch (tree.nodes.items(.tag)[fn_proto]) {158 switch (tree.nodes.items(.tag)[fn_proto]) {
111 .fn_proto_one, .fn_proto => {159 .fn_proto_one, .fn_proto => {
...@@ -123,8 +171,20 @@ fn renderMember(...@@ -123,8 +171,20 @@ fn renderMember(
123 else => unreachable,171 else => unreachable,
124 }172 }
125 assert(datas[decl].rhs != 0);173 assert(datas[decl].rhs != 0);
126 try renderExpression(gpa, ais, tree, fn_proto, .space);174 try renderExpression(r, fn_proto, .space);
127 return renderExpression(gpa, ais, tree, datas[decl].rhs, space);175 const body_node = datas[decl].rhs;
176 if (r.fixups.gut_functions.contains(decl)) {
177 ais.pushIndent();
178 const lbrace = tree.nodes.items(.main_token)[body_node];
179 try renderToken(r, lbrace, .newline);
180 try discardAllParams(r, fn_proto);
181 try ais.writer().writeAll("@trap();");
182 ais.popIndent();
183 try ais.insertNewline();
184 try renderToken(r, tree.lastToken(body_node), space); // rbrace
185 } else {
186 return renderExpression(r, body_node, space);
187 }
128 },188 },
129 .fn_proto_simple,189 .fn_proto_simple,
130 .fn_proto_multi,190 .fn_proto_multi,
...@@ -153,47 +213,47 @@ fn renderMember(...@@ -153,47 +213,47 @@ fn renderMember(
153 }213 }
154 }214 }
155 while (i < fn_token) : (i += 1) {215 while (i < fn_token) : (i += 1) {
156 try renderToken(ais, tree, i, .space);216 try renderToken(r, i, .space);
157 }217 }
158 try renderExpression(gpa, ais, tree, decl, .none);218 try renderExpression(r, decl, .none);
159 return renderToken(ais, tree, tree.lastToken(decl) + 1, space); // semicolon219 return renderToken(r, tree.lastToken(decl) + 1, space); // semicolon
160 },220 },
161221
162 .@"usingnamespace" => {222 .@"usingnamespace" => {
163 const main_token = main_tokens[decl];223 const main_token = main_tokens[decl];
164 const expr = datas[decl].lhs;224 const expr = datas[decl].lhs;
165 if (main_token > 0 and token_tags[main_token - 1] == .keyword_pub) {225 if (main_token > 0 and token_tags[main_token - 1] == .keyword_pub) {
166 try renderToken(ais, tree, main_token - 1, .space); // pub226 try renderToken(r, main_token - 1, .space); // pub
167 }227 }
168 try renderToken(ais, tree, main_token, .space); // usingnamespace228 try renderToken(r, main_token, .space); // usingnamespace
169 try renderExpression(gpa, ais, tree, expr, .none);229 try renderExpression(r, expr, .none);
170 return renderToken(ais, tree, tree.lastToken(expr) + 1, space); // ;230 return renderToken(r, tree.lastToken(expr) + 1, space); // ;
171 },231 },
172232
173 .global_var_decl,233 .global_var_decl,
174 .local_var_decl,234 .local_var_decl,
175 .simple_var_decl,235 .simple_var_decl,
176 .aligned_var_decl,236 .aligned_var_decl,
177 => return renderVarDecl(gpa, ais, tree, tree.fullVarDecl(decl).?, false, .semicolon),237 => return renderVarDecl(r, tree.fullVarDecl(decl).?, false, .semicolon),
178238
179 .test_decl => {239 .test_decl => {
180 const test_token = main_tokens[decl];240 const test_token = main_tokens[decl];
181 try renderToken(ais, tree, test_token, .space);241 try renderToken(r, test_token, .space);
182 const test_name_tag = token_tags[test_token + 1];242 const test_name_tag = token_tags[test_token + 1];
183 switch (test_name_tag) {243 switch (test_name_tag) {
184 .string_literal => try renderToken(ais, tree, test_token + 1, .space),244 .string_literal => try renderToken(r, test_token + 1, .space),
185 .identifier => try renderIdentifier(ais, tree, test_token + 1, .space, .preserve_when_shadowing),245 .identifier => try renderIdentifier(r, test_token + 1, .space, .preserve_when_shadowing),
186 else => {},246 else => {},
187 }247 }
188 try renderExpression(gpa, ais, tree, datas[decl].rhs, space);248 try renderExpression(r, datas[decl].rhs, space);
189 },249 },
190250
191 .container_field_init,251 .container_field_init,
192 .container_field_align,252 .container_field_align,
193 .container_field,253 .container_field,
194 => return renderContainerField(gpa, ais, tree, container, tree.fullContainerField(decl).?, space),254 => return renderContainerField(r, container, tree.fullContainerField(decl).?, space),
195255
196 .@"comptime" => return renderExpression(gpa, ais, tree, decl, space),256 .@"comptime" => return renderExpression(r, decl, space),
197257
198 .root => unreachable,258 .root => unreachable,
199 else => unreachable,259 else => unreachable,
...@@ -201,24 +261,31 @@ fn renderMember(...@@ -201,24 +261,31 @@ fn renderMember(
201}261}
202262
203/// Render all expressions in the slice, keeping empty lines where appropriate263/// Render all expressions in the slice, keeping empty lines where appropriate
204fn renderExpressions(gpa: Allocator, ais: *Ais, tree: Ast, expressions: []const Ast.Node.Index, space: Space) Error!void {264fn renderExpressions(r: *Render, expressions: []const Ast.Node.Index, space: Space) Error!void {
205 if (expressions.len == 0) return;265 if (expressions.len == 0) return;
206 try renderExpression(gpa, ais, tree, expressions[0], space);266 try renderExpression(r, expressions[0], space);
207 for (expressions[1..]) |expression| {267 for (expressions[1..]) |expression| {
208 try renderExtraNewline(ais, tree, expression);268 try renderExtraNewline(r, expression);
209 try renderExpression(gpa, ais, tree, expression, space);269 try renderExpression(r, expression, space);
210 }270 }
211}271}
212272
213fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index, space: Space) Error!void {273fn renderExpression(r: *Render, node: Ast.Node.Index, space: Space) Error!void {
274 const tree = r.tree;
275 const ais = r.ais;
214 const token_tags = tree.tokens.items(.tag);276 const token_tags = tree.tokens.items(.tag);
215 const main_tokens = tree.nodes.items(.main_token);277 const main_tokens = tree.nodes.items(.main_token);
216 const node_tags = tree.nodes.items(.tag);278 const node_tags = tree.nodes.items(.tag);
217 const datas = tree.nodes.items(.data);279 const datas = tree.nodes.items(.data);
280 if (r.fixups.replace_nodes.contains(node)) {
281 try ais.writer().writeAll("undefined");
282 try renderOnlySpace(r, space);
283 return;
284 }
218 switch (node_tags[node]) {285 switch (node_tags[node]) {
219 .identifier => {286 .identifier => {
220 const token_index = main_tokens[node];287 const token_index = main_tokens[node];
221 return renderIdentifier(ais, tree, token_index, space, .preserve_when_shadowing);288 return renderIdentifier(r, token_index, space, .preserve_when_shadowing);
222 },289 },
223290
224 .number_literal,291 .number_literal,
...@@ -226,29 +293,29 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,...@@ -226,29 +293,29 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,
226 .unreachable_literal,293 .unreachable_literal,
227 .anyframe_literal,294 .anyframe_literal,
228 .string_literal,295 .string_literal,
229 => return renderToken(ais, tree, main_tokens[node], space),296 => return renderToken(r, main_tokens[node], space),
230297
231 .multiline_string_literal => {298 .multiline_string_literal => {
232 var locked_indents = ais.lockOneShotIndent();299 var locked_indents = ais.lockOneShotIndent();
233 try ais.maybeInsertNewline();300 try ais.maybeInsertNewline();
234301
235 var i = datas[node].lhs;302 var i = datas[node].lhs;
236 while (i <= datas[node].rhs) : (i += 1) try renderToken(ais, tree, i, .newline);303 while (i <= datas[node].rhs) : (i += 1) try renderToken(r, i, .newline);
237304
238 while (locked_indents > 0) : (locked_indents -= 1) ais.popIndent();305 while (locked_indents > 0) : (locked_indents -= 1) ais.popIndent();
239306
240 switch (space) {307 switch (space) {
241 .none, .space, .newline, .skip => {},308 .none, .space, .newline, .skip => {},
242 .semicolon => if (token_tags[i] == .semicolon) try renderToken(ais, tree, i, .newline),309 .semicolon => if (token_tags[i] == .semicolon) try renderToken(r, i, .newline),
243 .comma => if (token_tags[i] == .comma) try renderToken(ais, tree, i, .newline),310 .comma => if (token_tags[i] == .comma) try renderToken(r, i, .newline),
244 .comma_space => if (token_tags[i] == .comma) try renderToken(ais, tree, i, .space),311 .comma_space => if (token_tags[i] == .comma) try renderToken(r, i, .space),
245 }312 }
246 },313 },
247314
248 .error_value => {315 .error_value => {
249 try renderToken(ais, tree, main_tokens[node], .none);316 try renderToken(r, main_tokens[node], .none);
250 try renderToken(ais, tree, main_tokens[node] + 1, .none);317 try renderToken(r, main_tokens[node] + 1, .none);
251 return renderIdentifier(ais, tree, main_tokens[node] + 2, space, .eagerly_unquote);318 return renderIdentifier(r, main_tokens[node] + 2, space, .eagerly_unquote);
252 },319 },
253320
254 .block_two,321 .block_two,
...@@ -256,18 +323,18 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,...@@ -256,18 +323,18 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,
256 => {323 => {
257 const statements = [2]Ast.Node.Index{ datas[node].lhs, datas[node].rhs };324 const statements = [2]Ast.Node.Index{ datas[node].lhs, datas[node].rhs };
258 if (datas[node].lhs == 0) {325 if (datas[node].lhs == 0) {
259 return renderBlock(gpa, ais, tree, node, statements[0..0], space);326 return renderBlock(r, node, statements[0..0], space);
260 } else if (datas[node].rhs == 0) {327 } else if (datas[node].rhs == 0) {
261 return renderBlock(gpa, ais, tree, node, statements[0..1], space);328 return renderBlock(r, node, statements[0..1], space);
262 } else {329 } else {
263 return renderBlock(gpa, ais, tree, node, statements[0..2], space);330 return renderBlock(r, node, statements[0..2], space);
264 }331 }
265 },332 },
266 .block,333 .block,
267 .block_semicolon,334 .block_semicolon,
268 => {335 => {
269 const statements = tree.extra_data[datas[node].lhs..datas[node].rhs];336 const statements = tree.extra_data[datas[node].lhs..datas[node].rhs];
270 return renderBlock(gpa, ais, tree, node, statements, space);337 return renderBlock(r, node, statements, space);
271 },338 },
272339
273 .@"errdefer" => {340 .@"errdefer" => {
...@@ -275,33 +342,33 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,...@@ -275,33 +342,33 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,
275 const payload_token = datas[node].lhs;342 const payload_token = datas[node].lhs;
276 const expr = datas[node].rhs;343 const expr = datas[node].rhs;
277344
278 try renderToken(ais, tree, defer_token, .space);345 try renderToken(r, defer_token, .space);
279 if (payload_token != 0) {346 if (payload_token != 0) {
280 try renderToken(ais, tree, payload_token - 1, .none); // |347 try renderToken(r, payload_token - 1, .none); // |
281 try renderIdentifier(ais, tree, payload_token, .none, .preserve_when_shadowing); // identifier348 try renderIdentifier(r, payload_token, .none, .preserve_when_shadowing); // identifier
282 try renderToken(ais, tree, payload_token + 1, .space); // |349 try renderToken(r, payload_token + 1, .space); // |
283 }350 }
284 return renderExpression(gpa, ais, tree, expr, space);351 return renderExpression(r, expr, space);
285 },352 },
286353
287 .@"defer" => {354 .@"defer" => {
288 const defer_token = main_tokens[node];355 const defer_token = main_tokens[node];
289 const expr = datas[node].rhs;356 const expr = datas[node].rhs;
290 try renderToken(ais, tree, defer_token, .space);357 try renderToken(r, defer_token, .space);
291 return renderExpression(gpa, ais, tree, expr, space);358 return renderExpression(r, expr, space);
292 },359 },
293 .@"comptime", .@"nosuspend" => {360 .@"comptime", .@"nosuspend" => {
294 const comptime_token = main_tokens[node];361 const comptime_token = main_tokens[node];
295 const block = datas[node].lhs;362 const block = datas[node].lhs;
296 try renderToken(ais, tree, comptime_token, .space);363 try renderToken(r, comptime_token, .space);
297 return renderExpression(gpa, ais, tree, block, space);364 return renderExpression(r, block, space);
298 },365 },
299366
300 .@"suspend" => {367 .@"suspend" => {
301 const suspend_token = main_tokens[node];368 const suspend_token = main_tokens[node];
302 const body = datas[node].lhs;369 const body = datas[node].lhs;
303 try renderToken(ais, tree, suspend_token, .space);370 try renderToken(r, suspend_token, .space);
304 return renderExpression(gpa, ais, tree, body, space);371 return renderExpression(r, body, space);
305 },372 },
306373
307 .@"catch" => {374 .@"catch" => {
...@@ -311,27 +378,27 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,...@@ -311,27 +378,27 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,
311 const same_line = tree.tokensOnSameLine(main_token, fallback_first);378 const same_line = tree.tokensOnSameLine(main_token, fallback_first);
312 const after_op_space = if (same_line) Space.space else Space.newline;379 const after_op_space = if (same_line) Space.space else Space.newline;
313380
314 try renderExpression(gpa, ais, tree, datas[node].lhs, .space); // target381 try renderExpression(r, datas[node].lhs, .space); // target
315382
316 if (token_tags[fallback_first - 1] == .pipe) {383 if (token_tags[fallback_first - 1] == .pipe) {
317 try renderToken(ais, tree, main_token, .space); // catch keyword384 try renderToken(r, main_token, .space); // catch keyword
318 try renderToken(ais, tree, main_token + 1, .none); // pipe385 try renderToken(r, main_token + 1, .none); // pipe
319 try renderIdentifier(ais, tree, main_token + 2, .none, .preserve_when_shadowing); // payload identifier386 try renderIdentifier(r, main_token + 2, .none, .preserve_when_shadowing); // payload identifier
320 try renderToken(ais, tree, main_token + 3, after_op_space); // pipe387 try renderToken(r, main_token + 3, after_op_space); // pipe
321 } else {388 } else {
322 assert(token_tags[fallback_first - 1] == .keyword_catch);389 assert(token_tags[fallback_first - 1] == .keyword_catch);
323 try renderToken(ais, tree, main_token, after_op_space); // catch keyword390 try renderToken(r, main_token, after_op_space); // catch keyword
324 }391 }
325392
326 ais.pushIndentOneShot();393 ais.pushIndentOneShot();
327 try renderExpression(gpa, ais, tree, datas[node].rhs, space); // fallback394 try renderExpression(r, datas[node].rhs, space); // fallback
328 },395 },
329396
330 .field_access => {397 .field_access => {
331 const main_token = main_tokens[node];398 const main_token = main_tokens[node];
332 const field_access = datas[node];399 const field_access = datas[node];
333400
334 try renderExpression(gpa, ais, tree, field_access.lhs, .none);401 try renderExpression(r, field_access.lhs, .none);
335402
336 // Allow a line break between the lhs and the dot if the lhs and rhs403 // Allow a line break between the lhs and the dot if the lhs and rhs
337 // are on different lines.404 // are on different lines.
...@@ -342,7 +409,7 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,...@@ -342,7 +409,7 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,
342 ais.pushIndentOneShot();409 ais.pushIndentOneShot();
343 }410 }
344411
345 try renderToken(ais, tree, main_token, .none); // .412 try renderToken(r, main_token, .none); // .
346413
347 // This check ensures that zag() is indented in the following example:414 // This check ensures that zag() is indented in the following example:
348 // const x = foo415 // const x = foo
...@@ -353,25 +420,25 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,...@@ -353,25 +420,25 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,
353 ais.pushIndentOneShot();420 ais.pushIndentOneShot();
354 }421 }
355422
356 return renderIdentifier(ais, tree, field_access.rhs, space, .eagerly_unquote); // field423 return renderIdentifier(r, field_access.rhs, space, .eagerly_unquote); // field
357 },424 },
358425
359 .error_union,426 .error_union,
360 .switch_range,427 .switch_range,
361 => {428 => {
362 const infix = datas[node];429 const infix = datas[node];
363 try renderExpression(gpa, ais, tree, infix.lhs, .none);430 try renderExpression(r, infix.lhs, .none);
364 try renderToken(ais, tree, main_tokens[node], .none);431 try renderToken(r, main_tokens[node], .none);
365 return renderExpression(gpa, ais, tree, infix.rhs, space);432 return renderExpression(r, infix.rhs, space);
366 },433 },
367 .for_range => {434 .for_range => {
368 const infix = datas[node];435 const infix = datas[node];
369 try renderExpression(gpa, ais, tree, infix.lhs, .none);436 try renderExpression(r, infix.lhs, .none);
370 if (infix.rhs != 0) {437 if (infix.rhs != 0) {
371 try renderToken(ais, tree, main_tokens[node], .none);438 try renderToken(r, main_tokens[node], .none);
372 return renderExpression(gpa, ais, tree, infix.rhs, space);439 return renderExpression(r, infix.rhs, space);
373 } else {440 } else {
374 return renderToken(ais, tree, main_tokens[node], space);441 return renderToken(r, main_tokens[node], space);
375 }442 }
376 },443 },
377444
...@@ -424,17 +491,17 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,...@@ -424,17 +491,17 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,
424 .@"orelse",491 .@"orelse",
425 => {492 => {
426 const infix = datas[node];493 const infix = datas[node];
427 try renderExpression(gpa, ais, tree, infix.lhs, .space);494 try renderExpression(r, infix.lhs, .space);
428 const op_token = main_tokens[node];495 const op_token = main_tokens[node];
429 if (tree.tokensOnSameLine(op_token, op_token + 1)) {496 if (tree.tokensOnSameLine(op_token, op_token + 1)) {
430 try renderToken(ais, tree, op_token, .space);497 try renderToken(r, op_token, .space);
431 } else {498 } else {
432 ais.pushIndent();499 ais.pushIndent();
433 try renderToken(ais, tree, op_token, .newline);500 try renderToken(r, op_token, .newline);
434 ais.popIndent();501 ais.popIndent();
435 }502 }
436 ais.pushIndentOneShot();503 ais.pushIndentOneShot();
437 return renderExpression(gpa, ais, tree, infix.rhs, space);504 return renderExpression(r, infix.rhs, space);
438 },505 },
439506
440 .assign_destructure => {507 .assign_destructure => {
...@@ -445,7 +512,7 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,...@@ -445,7 +512,7 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,
445512
446 const maybe_comptime_token = tree.firstToken(node) - 1;513 const maybe_comptime_token = tree.firstToken(node) - 1;
447 if (token_tags[maybe_comptime_token] == .keyword_comptime) {514 if (token_tags[maybe_comptime_token] == .keyword_comptime) {
448 try renderToken(ais, tree, maybe_comptime_token, .space);515 try renderToken(r, maybe_comptime_token, .space);
449 }516 }
450517
451 for (lhs_exprs, 0..) |lhs_node, i| {518 for (lhs_exprs, 0..) |lhs_node, i| {
...@@ -456,21 +523,21 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,...@@ -456,21 +523,21 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,
456 .simple_var_decl,523 .simple_var_decl,
457 .aligned_var_decl,524 .aligned_var_decl,
458 => {525 => {
459 try renderVarDecl(gpa, ais, tree, tree.fullVarDecl(lhs_node).?, true, lhs_space);526 try renderVarDecl(r, tree.fullVarDecl(lhs_node).?, true, lhs_space);
460 },527 },
461 else => try renderExpression(gpa, ais, tree, lhs_node, lhs_space),528 else => try renderExpression(r, lhs_node, lhs_space),
462 }529 }
463 }530 }
464 const equal_token = main_tokens[node];531 const equal_token = main_tokens[node];
465 if (tree.tokensOnSameLine(equal_token, equal_token + 1)) {532 if (tree.tokensOnSameLine(equal_token, equal_token + 1)) {
466 try renderToken(ais, tree, equal_token, .space);533 try renderToken(r, equal_token, .space);
467 } else {534 } else {
468 ais.pushIndent();535 ais.pushIndent();
469 try renderToken(ais, tree, equal_token, .newline);536 try renderToken(r, equal_token, .newline);
470 ais.popIndent();537 ais.popIndent();
471 }538 }
472 ais.pushIndentOneShot();539 ais.pushIndentOneShot();
473 return renderExpression(gpa, ais, tree, rhs, space);540 return renderExpression(r, rhs, space);
474 },541 },
475542
476 .bit_not,543 .bit_not,
...@@ -480,27 +547,27 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,...@@ -480,27 +547,27 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,
480 .optional_type,547 .optional_type,
481 .address_of,548 .address_of,
482 => {549 => {
483 try renderToken(ais, tree, main_tokens[node], .none);550 try renderToken(r, main_tokens[node], .none);
484 return renderExpression(gpa, ais, tree, datas[node].lhs, space);551 return renderExpression(r, datas[node].lhs, space);
485 },552 },
486553
487 .@"try",554 .@"try",
488 .@"resume",555 .@"resume",
489 .@"await",556 .@"await",
490 => {557 => {
491 try renderToken(ais, tree, main_tokens[node], .space);558 try renderToken(r, main_tokens[node], .space);
492 return renderExpression(gpa, ais, tree, datas[node].lhs, space);559 return renderExpression(r, datas[node].lhs, space);
493 },560 },
494561
495 .array_type,562 .array_type,
496 .array_type_sentinel,563 .array_type_sentinel,
497 => return renderArrayType(gpa, ais, tree, tree.fullArrayType(node).?, space),564 => return renderArrayType(r, tree.fullArrayType(node).?, space),
498565
499 .ptr_type_aligned,566 .ptr_type_aligned,
500 .ptr_type_sentinel,567 .ptr_type_sentinel,
501 .ptr_type,568 .ptr_type,
502 .ptr_type_bit_range,569 .ptr_type_bit_range,
503 => return renderPtrType(gpa, ais, tree, tree.fullPtrType(node).?, space),570 => return renderPtrType(r, tree.fullPtrType(node).?, space),
504571
505 .array_init_one,572 .array_init_one,
506 .array_init_one_comma,573 .array_init_one_comma,
...@@ -512,7 +579,7 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,...@@ -512,7 +579,7 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,
512 .array_init_comma,579 .array_init_comma,
513 => {580 => {
514 var elements: [2]Ast.Node.Index = undefined;581 var elements: [2]Ast.Node.Index = undefined;
515 return renderArrayInit(gpa, ais, tree, tree.fullArrayInit(&elements, node).?, space);582 return renderArrayInit(r, tree.fullArrayInit(&elements, node).?, space);
516 },583 },
517584
518 .struct_init_one,585 .struct_init_one,
...@@ -525,7 +592,7 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,...@@ -525,7 +592,7 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,
525 .struct_init_comma,592 .struct_init_comma,
526 => {593 => {
527 var buf: [2]Ast.Node.Index = undefined;594 var buf: [2]Ast.Node.Index = undefined;
528 return renderStructInit(gpa, ais, tree, node, tree.fullStructInit(&buf, node).?, space);595 return renderStructInit(r, node, tree.fullStructInit(&buf, node).?, space);
529 },596 },
530597
531 .call_one,598 .call_one,
...@@ -538,7 +605,7 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,...@@ -538,7 +605,7 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,
538 .async_call_comma,605 .async_call_comma,
539 => {606 => {
540 var buf: [1]Ast.Node.Index = undefined;607 var buf: [1]Ast.Node.Index = undefined;
541 return renderCall(gpa, ais, tree, tree.fullCall(&buf, node).?, space);608 return renderCall(r, tree.fullCall(&buf, node).?, space);
542 },609 },
543610
544 .array_access => {611 .array_access => {
...@@ -547,25 +614,25 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,...@@ -547,25 +614,25 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,
547 const rbracket = tree.lastToken(suffix.rhs) + 1;614 const rbracket = tree.lastToken(suffix.rhs) + 1;
548 const one_line = tree.tokensOnSameLine(lbracket, rbracket);615 const one_line = tree.tokensOnSameLine(lbracket, rbracket);
549 const inner_space = if (one_line) Space.none else Space.newline;616 const inner_space = if (one_line) Space.none else Space.newline;
550 try renderExpression(gpa, ais, tree, suffix.lhs, .none);617 try renderExpression(r, suffix.lhs, .none);
551 ais.pushIndentNextLine();618 ais.pushIndentNextLine();
552 try renderToken(ais, tree, lbracket, inner_space); // [619 try renderToken(r, lbracket, inner_space); // [
553 try renderExpression(gpa, ais, tree, suffix.rhs, inner_space);620 try renderExpression(r, suffix.rhs, inner_space);
554 ais.popIndent();621 ais.popIndent();
555 return renderToken(ais, tree, rbracket, space); // ]622 return renderToken(r, rbracket, space); // ]
556 },623 },
557624
558 .slice_open, .slice, .slice_sentinel => return renderSlice(gpa, ais, tree, node, tree.fullSlice(node).?, space),625 .slice_open, .slice, .slice_sentinel => return renderSlice(r, node, tree.fullSlice(node).?, space),
559626
560 .deref => {627 .deref => {
561 try renderExpression(gpa, ais, tree, datas[node].lhs, .none);628 try renderExpression(r, datas[node].lhs, .none);
562 return renderToken(ais, tree, main_tokens[node], space);629 return renderToken(r, main_tokens[node], space);
563 },630 },
564631
565 .unwrap_optional => {632 .unwrap_optional => {
566 try renderExpression(gpa, ais, tree, datas[node].lhs, .none);633 try renderExpression(r, datas[node].lhs, .none);
567 try renderToken(ais, tree, main_tokens[node], .none);634 try renderToken(r, main_tokens[node], .none);
568 return renderToken(ais, tree, datas[node].rhs, space);635 return renderToken(r, datas[node].rhs, space);
569 },636 },
570637
571 .@"break" => {638 .@"break" => {
...@@ -573,19 +640,19 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,...@@ -573,19 +640,19 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,
573 const label_token = datas[node].lhs;640 const label_token = datas[node].lhs;
574 const target = datas[node].rhs;641 const target = datas[node].rhs;
575 if (label_token == 0 and target == 0) {642 if (label_token == 0 and target == 0) {
576 try renderToken(ais, tree, main_token, space); // break keyword643 try renderToken(r, main_token, space); // break keyword
577 } else if (label_token == 0 and target != 0) {644 } else if (label_token == 0 and target != 0) {
578 try renderToken(ais, tree, main_token, .space); // break keyword645 try renderToken(r, main_token, .space); // break keyword
579 try renderExpression(gpa, ais, tree, target, space);646 try renderExpression(r, target, space);
580 } else if (label_token != 0 and target == 0) {647 } else if (label_token != 0 and target == 0) {
581 try renderToken(ais, tree, main_token, .space); // break keyword648 try renderToken(r, main_token, .space); // break keyword
582 try renderToken(ais, tree, label_token - 1, .none); // colon649 try renderToken(r, label_token - 1, .none); // colon
583 try renderIdentifier(ais, tree, label_token, space, .eagerly_unquote); // identifier650 try renderIdentifier(r, label_token, space, .eagerly_unquote); // identifier
584 } else if (label_token != 0 and target != 0) {651 } else if (label_token != 0 and target != 0) {
585 try renderToken(ais, tree, main_token, .space); // break keyword652 try renderToken(r, main_token, .space); // break keyword
586 try renderToken(ais, tree, label_token - 1, .none); // colon653 try renderToken(r, label_token - 1, .none); // colon
587 try renderIdentifier(ais, tree, label_token, .space, .eagerly_unquote); // identifier654 try renderIdentifier(r, label_token, .space, .eagerly_unquote); // identifier
588 try renderExpression(gpa, ais, tree, target, space);655 try renderExpression(r, target, space);
589 }656 }
590 },657 },
591658
...@@ -593,28 +660,28 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,...@@ -593,28 +660,28 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,
593 const main_token = main_tokens[node];660 const main_token = main_tokens[node];
594 const label = datas[node].lhs;661 const label = datas[node].lhs;
595 if (label != 0) {662 if (label != 0) {
596 try renderToken(ais, tree, main_token, .space); // continue663 try renderToken(r, main_token, .space); // continue
597 try renderToken(ais, tree, label - 1, .none); // :664 try renderToken(r, label - 1, .none); // :
598 return renderIdentifier(ais, tree, label, space, .eagerly_unquote); // label665 return renderIdentifier(r, label, space, .eagerly_unquote); // label
599 } else {666 } else {
600 return renderToken(ais, tree, main_token, space); // continue667 return renderToken(r, main_token, space); // continue
601 }668 }
602 },669 },
603670
604 .@"return" => {671 .@"return" => {
605 if (datas[node].lhs != 0) {672 if (datas[node].lhs != 0) {
606 try renderToken(ais, tree, main_tokens[node], .space);673 try renderToken(r, main_tokens[node], .space);
607 try renderExpression(gpa, ais, tree, datas[node].lhs, space);674 try renderExpression(r, datas[node].lhs, space);
608 } else {675 } else {
609 try renderToken(ais, tree, main_tokens[node], space);676 try renderToken(r, main_tokens[node], space);
610 }677 }
611 },678 },
612679
613 .grouped_expression => {680 .grouped_expression => {
614 try renderToken(ais, tree, main_tokens[node], .none); // lparen681 try renderToken(r, main_tokens[node], .none); // lparen
615 ais.pushIndentOneShot();682 ais.pushIndentOneShot();
616 try renderExpression(gpa, ais, tree, datas[node].lhs, .none);683 try renderExpression(r, datas[node].lhs, .none);
617 return renderToken(ais, tree, datas[node].rhs, space); // rparen684 return renderToken(r, datas[node].rhs, space); // rparen
618 },685 },
619686
620 .container_decl,687 .container_decl,
...@@ -631,7 +698,7 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,...@@ -631,7 +698,7 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,
631 .tagged_union_two_trailing,698 .tagged_union_two_trailing,
632 => {699 => {
633 var buf: [2]Ast.Node.Index = undefined;700 var buf: [2]Ast.Node.Index = undefined;
634 return renderContainerDecl(gpa, ais, tree, node, tree.fullContainerDecl(&buf, node).?, space);701 return renderContainerDecl(r, node, tree.fullContainerDecl(&buf, node).?, space);
635 },702 },
636703
637 .error_set_decl => {704 .error_set_decl => {
...@@ -639,62 +706,62 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,...@@ -639,62 +706,62 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,
639 const lbrace = error_token + 1;706 const lbrace = error_token + 1;
640 const rbrace = datas[node].rhs;707 const rbrace = datas[node].rhs;
641708
642 try renderToken(ais, tree, error_token, .none);709 try renderToken(r, error_token, .none);
643710
644 if (lbrace + 1 == rbrace) {711 if (lbrace + 1 == rbrace) {
645 // There is nothing between the braces so render condensed: `error{}`712 // There is nothing between the braces so render condensed: `error{}`
646 try renderToken(ais, tree, lbrace, .none);713 try renderToken(r, lbrace, .none);
647 return renderToken(ais, tree, rbrace, space);714 return renderToken(r, rbrace, space);
648 } else if (lbrace + 2 == rbrace and token_tags[lbrace + 1] == .identifier) {715 } else if (lbrace + 2 == rbrace and token_tags[lbrace + 1] == .identifier) {
649 // There is exactly one member and no trailing comma or716 // There is exactly one member and no trailing comma or
650 // comments, so render without surrounding spaces: `error{Foo}`717 // comments, so render without surrounding spaces: `error{Foo}`
651 try renderToken(ais, tree, lbrace, .none);718 try renderToken(r, lbrace, .none);
652 try renderIdentifier(ais, tree, lbrace + 1, .none, .eagerly_unquote); // identifier719 try renderIdentifier(r, lbrace + 1, .none, .eagerly_unquote); // identifier
653 return renderToken(ais, tree, rbrace, space);720 return renderToken(r, rbrace, space);
654 } else if (token_tags[rbrace - 1] == .comma) {721 } else if (token_tags[rbrace - 1] == .comma) {
655 // There is a trailing comma so render each member on a new line.722 // There is a trailing comma so render each member on a new line.
656 ais.pushIndentNextLine();723 ais.pushIndentNextLine();
657 try renderToken(ais, tree, lbrace, .newline);724 try renderToken(r, lbrace, .newline);
658 var i = lbrace + 1;725 var i = lbrace + 1;
659 while (i < rbrace) : (i += 1) {726 while (i < rbrace) : (i += 1) {
660 if (i > lbrace + 1) try renderExtraNewlineToken(ais, tree, i);727 if (i > lbrace + 1) try renderExtraNewlineToken(r, i);
661 switch (token_tags[i]) {728 switch (token_tags[i]) {
662 .doc_comment => try renderToken(ais, tree, i, .newline),729 .doc_comment => try renderToken(r, i, .newline),
663 .identifier => try renderIdentifier(ais, tree, i, .comma, .eagerly_unquote),730 .identifier => try renderIdentifier(r, i, .comma, .eagerly_unquote),
664 .comma => {},731 .comma => {},
665 else => unreachable,732 else => unreachable,
666 }733 }
667 }734 }
668 ais.popIndent();735 ais.popIndent();
669 return renderToken(ais, tree, rbrace, space);736 return renderToken(r, rbrace, space);
670 } else {737 } else {
671 // There is no trailing comma so render everything on one line.738 // There is no trailing comma so render everything on one line.
672 try renderToken(ais, tree, lbrace, .space);739 try renderToken(r, lbrace, .space);
673 var i = lbrace + 1;740 var i = lbrace + 1;
674 while (i < rbrace) : (i += 1) {741 while (i < rbrace) : (i += 1) {
675 switch (token_tags[i]) {742 switch (token_tags[i]) {
676 .doc_comment => unreachable, // TODO743 .doc_comment => unreachable, // TODO
677 .identifier => try renderIdentifier(ais, tree, i, .comma_space, .eagerly_unquote),744 .identifier => try renderIdentifier(r, i, .comma_space, .eagerly_unquote),
678 .comma => {},745 .comma => {},
679 else => unreachable,746 else => unreachable,
680 }747 }
681 }748 }
682 return renderToken(ais, tree, rbrace, space);749 return renderToken(r, rbrace, space);
683 }750 }
684 },751 },
685752
686 .builtin_call_two, .builtin_call_two_comma => {753 .builtin_call_two, .builtin_call_two_comma => {
687 if (datas[node].lhs == 0) {754 if (datas[node].lhs == 0) {
688 return renderBuiltinCall(gpa, ais, tree, main_tokens[node], &.{}, space);755 return renderBuiltinCall(r, main_tokens[node], &.{}, space);
689 } else if (datas[node].rhs == 0) {756 } else if (datas[node].rhs == 0) {
690 return renderBuiltinCall(gpa, ais, tree, main_tokens[node], &.{datas[node].lhs}, space);757 return renderBuiltinCall(r, main_tokens[node], &.{datas[node].lhs}, space);
691 } else {758 } else {
692 return renderBuiltinCall(gpa, ais, tree, main_tokens[node], &.{ datas[node].lhs, datas[node].rhs }, space);759 return renderBuiltinCall(r, main_tokens[node], &.{ datas[node].lhs, datas[node].rhs }, space);
693 }760 }
694 },761 },
695 .builtin_call, .builtin_call_comma => {762 .builtin_call, .builtin_call_comma => {
696 const params = tree.extra_data[datas[node].lhs..datas[node].rhs];763 const params = tree.extra_data[datas[node].lhs..datas[node].rhs];
697 return renderBuiltinCall(gpa, ais, tree, main_tokens[node], params, space);764 return renderBuiltinCall(r, main_tokens[node], params, space);
698 },765 },
699766
700 .fn_proto_simple,767 .fn_proto_simple,
...@@ -703,17 +770,17 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,...@@ -703,17 +770,17 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,
703 .fn_proto,770 .fn_proto,
704 => {771 => {
705 var buf: [1]Ast.Node.Index = undefined;772 var buf: [1]Ast.Node.Index = undefined;
706 return renderFnProto(gpa, ais, tree, tree.fullFnProto(&buf, node).?, space);773 return renderFnProto(r, tree.fullFnProto(&buf, node).?, space);
707 },774 },
708775
709 .anyframe_type => {776 .anyframe_type => {
710 const main_token = main_tokens[node];777 const main_token = main_tokens[node];
711 if (datas[node].rhs != 0) {778 if (datas[node].rhs != 0) {
712 try renderToken(ais, tree, main_token, .none); // anyframe779 try renderToken(r, main_token, .none); // anyframe
713 try renderToken(ais, tree, main_token + 1, .none); // ->780 try renderToken(r, main_token + 1, .none); // ->
714 return renderExpression(gpa, ais, tree, datas[node].rhs, space);781 return renderExpression(r, datas[node].rhs, space);
715 } else {782 } else {
716 return renderToken(ais, tree, main_token, space); // anyframe783 return renderToken(r, main_token, space); // anyframe
717 }784 }
718 },785 },
719786
...@@ -726,48 +793,48 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,...@@ -726,48 +793,48 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,
726 const cases = tree.extra_data[extra.start..extra.end];793 const cases = tree.extra_data[extra.start..extra.end];
727 const rparen = tree.lastToken(condition) + 1;794 const rparen = tree.lastToken(condition) + 1;
728795
729 try renderToken(ais, tree, switch_token, .space); // switch keyword796 try renderToken(r, switch_token, .space); // switch keyword
730 try renderToken(ais, tree, switch_token + 1, .none); // lparen797 try renderToken(r, switch_token + 1, .none); // lparen
731 try renderExpression(gpa, ais, tree, condition, .none); // condition expression798 try renderExpression(r, condition, .none); // condition expression
732 try renderToken(ais, tree, rparen, .space); // rparen799 try renderToken(r, rparen, .space); // rparen
733800
734 ais.pushIndentNextLine();801 ais.pushIndentNextLine();
735 if (cases.len == 0) {802 if (cases.len == 0) {
736 try renderToken(ais, tree, rparen + 1, .none); // lbrace803 try renderToken(r, rparen + 1, .none); // lbrace
737 } else {804 } else {
738 try renderToken(ais, tree, rparen + 1, .newline); // lbrace805 try renderToken(r, rparen + 1, .newline); // lbrace
739 try renderExpressions(gpa, ais, tree, cases, .comma);806 try renderExpressions(r, cases, .comma);
740 }807 }
741 ais.popIndent();808 ais.popIndent();
742 return renderToken(ais, tree, tree.lastToken(node), space); // rbrace809 return renderToken(r, tree.lastToken(node), space); // rbrace
743 },810 },
744811
745 .switch_case_one,812 .switch_case_one,
746 .switch_case_inline_one,813 .switch_case_inline_one,
747 .switch_case,814 .switch_case,
748 .switch_case_inline,815 .switch_case_inline,
749 => return renderSwitchCase(gpa, ais, tree, tree.fullSwitchCase(node).?, space),816 => return renderSwitchCase(r, tree.fullSwitchCase(node).?, space),
750817
751 .while_simple,818 .while_simple,
752 .while_cont,819 .while_cont,
753 .@"while",820 .@"while",
754 => return renderWhile(gpa, ais, tree, tree.fullWhile(node).?, space),821 => return renderWhile(r, tree.fullWhile(node).?, space),
755822
756 .for_simple,823 .for_simple,
757 .@"for",824 .@"for",
758 => return renderFor(gpa, ais, tree, tree.fullFor(node).?, space),825 => return renderFor(r, tree.fullFor(node).?, space),
759826
760 .if_simple,827 .if_simple,
761 .@"if",828 .@"if",
762 => return renderIf(gpa, ais, tree, tree.fullIf(node).?, space),829 => return renderIf(r, tree.fullIf(node).?, space),
763830
764 .asm_simple,831 .asm_simple,
765 .@"asm",832 .@"asm",
766 => return renderAsm(gpa, ais, tree, tree.fullAsm(node).?, space),833 => return renderAsm(r, tree.fullAsm(node).?, space),
767834
768 .enum_literal => {835 .enum_literal => {
769 try renderToken(ais, tree, main_tokens[node] - 1, .none); // .836 try renderToken(r, main_tokens[node] - 1, .none); // .
770 return renderIdentifier(ais, tree, main_tokens[node], space, .eagerly_unquote); // name837 return renderIdentifier(r, main_tokens[node], space, .eagerly_unquote); // name
771 },838 },
772839
773 .fn_decl => unreachable,840 .fn_decl => unreachable,
...@@ -787,34 +854,29 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,...@@ -787,34 +854,29 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,
787}854}
788855
789fn renderArrayType(856fn renderArrayType(
790 gpa: Allocator,857 r: *Render,
791 ais: *Ais,
792 tree: Ast,
793 array_type: Ast.full.ArrayType,858 array_type: Ast.full.ArrayType,
794 space: Space,859 space: Space,
795) Error!void {860) Error!void {
861 const tree = r.tree;
862 const ais = r.ais;
796 const rbracket = tree.firstToken(array_type.ast.elem_type) - 1;863 const rbracket = tree.firstToken(array_type.ast.elem_type) - 1;
797 const one_line = tree.tokensOnSameLine(array_type.ast.lbracket, rbracket);864 const one_line = tree.tokensOnSameLine(array_type.ast.lbracket, rbracket);
798 const inner_space = if (one_line) Space.none else Space.newline;865 const inner_space = if (one_line) Space.none else Space.newline;
799 ais.pushIndentNextLine();866 ais.pushIndentNextLine();
800 try renderToken(ais, tree, array_type.ast.lbracket, inner_space); // lbracket867 try renderToken(r, array_type.ast.lbracket, inner_space); // lbracket
801 try renderExpression(gpa, ais, tree, array_type.ast.elem_count, inner_space);868 try renderExpression(r, array_type.ast.elem_count, inner_space);
802 if (array_type.ast.sentinel != 0) {869 if (array_type.ast.sentinel != 0) {
803 try renderToken(ais, tree, tree.firstToken(array_type.ast.sentinel) - 1, inner_space); // colon870 try renderToken(r, tree.firstToken(array_type.ast.sentinel) - 1, inner_space); // colon
804 try renderExpression(gpa, ais, tree, array_type.ast.sentinel, inner_space);871 try renderExpression(r, array_type.ast.sentinel, inner_space);
805 }872 }
806 ais.popIndent();873 ais.popIndent();
807 try renderToken(ais, tree, rbracket, .none); // rbracket874 try renderToken(r, rbracket, .none); // rbracket
808 return renderExpression(gpa, ais, tree, array_type.ast.elem_type, space);875 return renderExpression(r, array_type.ast.elem_type, space);
809}876}
810877
811fn renderPtrType(878fn renderPtrType(r: *Render, ptr_type: Ast.full.PtrType, space: Space) Error!void {
812 gpa: Allocator,879 const tree = r.tree;
813 ais: *Ais,
814 tree: Ast,
815 ptr_type: Ast.full.PtrType,
816 space: Space,
817) Error!void {
818 switch (ptr_type.size) {880 switch (ptr_type.size) {
819 .One => {881 .One => {
820 // Since ** tokens exist and the same token is shared by two882 // Since ** tokens exist and the same token is shared by two
...@@ -825,90 +887,89 @@ fn renderPtrType(...@@ -825,90 +887,89 @@ fn renderPtrType(
825 if (tree.tokens.items(.tag)[ptr_type.ast.main_token] == .asterisk_asterisk and887 if (tree.tokens.items(.tag)[ptr_type.ast.main_token] == .asterisk_asterisk and
826 ptr_type.ast.main_token == tree.nodes.items(.main_token)[ptr_type.ast.child_type])888 ptr_type.ast.main_token == tree.nodes.items(.main_token)[ptr_type.ast.child_type])
827 {889 {
828 return renderExpression(gpa, ais, tree, ptr_type.ast.child_type, space);890 return renderExpression(r, ptr_type.ast.child_type, space);
829 }891 }
830 try renderToken(ais, tree, ptr_type.ast.main_token, .none); // asterisk892 try renderToken(r, ptr_type.ast.main_token, .none); // asterisk
831 },893 },
832 .Many => {894 .Many => {
833 if (ptr_type.ast.sentinel == 0) {895 if (ptr_type.ast.sentinel == 0) {
834 try renderToken(ais, tree, ptr_type.ast.main_token - 1, .none); // lbracket896 try renderToken(r, ptr_type.ast.main_token - 1, .none); // lbracket
835 try renderToken(ais, tree, ptr_type.ast.main_token, .none); // asterisk897 try renderToken(r, ptr_type.ast.main_token, .none); // asterisk
836 try renderToken(ais, tree, ptr_type.ast.main_token + 1, .none); // rbracket898 try renderToken(r, ptr_type.ast.main_token + 1, .none); // rbracket
837 } else {899 } else {
838 try renderToken(ais, tree, ptr_type.ast.main_token - 1, .none); // lbracket900 try renderToken(r, ptr_type.ast.main_token - 1, .none); // lbracket
839 try renderToken(ais, tree, ptr_type.ast.main_token, .none); // asterisk901 try renderToken(r, ptr_type.ast.main_token, .none); // asterisk
840 try renderToken(ais, tree, ptr_type.ast.main_token + 1, .none); // colon902 try renderToken(r, ptr_type.ast.main_token + 1, .none); // colon
841 try renderExpression(gpa, ais, tree, ptr_type.ast.sentinel, .none);903 try renderExpression(r, ptr_type.ast.sentinel, .none);
842 try renderToken(ais, tree, tree.lastToken(ptr_type.ast.sentinel) + 1, .none); // rbracket904 try renderToken(r, tree.lastToken(ptr_type.ast.sentinel) + 1, .none); // rbracket
843 }905 }
844 },906 },
845 .C => {907 .C => {
846 try renderToken(ais, tree, ptr_type.ast.main_token - 1, .none); // lbracket908 try renderToken(r, ptr_type.ast.main_token - 1, .none); // lbracket
847 try renderToken(ais, tree, ptr_type.ast.main_token, .none); // asterisk909 try renderToken(r, ptr_type.ast.main_token, .none); // asterisk
848 try renderToken(ais, tree, ptr_type.ast.main_token + 1, .none); // c910 try renderToken(r, ptr_type.ast.main_token + 1, .none); // c
849 try renderToken(ais, tree, ptr_type.ast.main_token + 2, .none); // rbracket911 try renderToken(r, ptr_type.ast.main_token + 2, .none); // rbracket
850 },912 },
851 .Slice => {913 .Slice => {
852 if (ptr_type.ast.sentinel == 0) {914 if (ptr_type.ast.sentinel == 0) {
853 try renderToken(ais, tree, ptr_type.ast.main_token, .none); // lbracket915 try renderToken(r, ptr_type.ast.main_token, .none); // lbracket
854 try renderToken(ais, tree, ptr_type.ast.main_token + 1, .none); // rbracket916 try renderToken(r, ptr_type.ast.main_token + 1, .none); // rbracket
855 } else {917 } else {
856 try renderToken(ais, tree, ptr_type.ast.main_token, .none); // lbracket918 try renderToken(r, ptr_type.ast.main_token, .none); // lbracket
857 try renderToken(ais, tree, ptr_type.ast.main_token + 1, .none); // colon919 try renderToken(r, ptr_type.ast.main_token + 1, .none); // colon
858 try renderExpression(gpa, ais, tree, ptr_type.ast.sentinel, .none);920 try renderExpression(r, ptr_type.ast.sentinel, .none);
859 try renderToken(ais, tree, tree.lastToken(ptr_type.ast.sentinel) + 1, .none); // rbracket921 try renderToken(r, tree.lastToken(ptr_type.ast.sentinel) + 1, .none); // rbracket
860 }922 }
861 },923 },
862 }924 }
863925
864 if (ptr_type.allowzero_token) |allowzero_token| {926 if (ptr_type.allowzero_token) |allowzero_token| {
865 try renderToken(ais, tree, allowzero_token, .space);927 try renderToken(r, allowzero_token, .space);
866 }928 }
867929
868 if (ptr_type.ast.align_node != 0) {930 if (ptr_type.ast.align_node != 0) {
869 const align_first = tree.firstToken(ptr_type.ast.align_node);931 const align_first = tree.firstToken(ptr_type.ast.align_node);
870 try renderToken(ais, tree, align_first - 2, .none); // align932 try renderToken(r, align_first - 2, .none); // align
871 try renderToken(ais, tree, align_first - 1, .none); // lparen933 try renderToken(r, align_first - 1, .none); // lparen
872 try renderExpression(gpa, ais, tree, ptr_type.ast.align_node, .none);934 try renderExpression(r, ptr_type.ast.align_node, .none);
873 if (ptr_type.ast.bit_range_start != 0) {935 if (ptr_type.ast.bit_range_start != 0) {
874 assert(ptr_type.ast.bit_range_end != 0);936 assert(ptr_type.ast.bit_range_end != 0);
875 try renderToken(ais, tree, tree.firstToken(ptr_type.ast.bit_range_start) - 1, .none); // colon937 try renderToken(r, tree.firstToken(ptr_type.ast.bit_range_start) - 1, .none); // colon
876 try renderExpression(gpa, ais, tree, ptr_type.ast.bit_range_start, .none);938 try renderExpression(r, ptr_type.ast.bit_range_start, .none);
877 try renderToken(ais, tree, tree.firstToken(ptr_type.ast.bit_range_end) - 1, .none); // colon939 try renderToken(r, tree.firstToken(ptr_type.ast.bit_range_end) - 1, .none); // colon
878 try renderExpression(gpa, ais, tree, ptr_type.ast.bit_range_end, .none);940 try renderExpression(r, ptr_type.ast.bit_range_end, .none);
879 try renderToken(ais, tree, tree.lastToken(ptr_type.ast.bit_range_end) + 1, .space); // rparen941 try renderToken(r, tree.lastToken(ptr_type.ast.bit_range_end) + 1, .space); // rparen
880 } else {942 } else {
881 try renderToken(ais, tree, tree.lastToken(ptr_type.ast.align_node) + 1, .space); // rparen943 try renderToken(r, tree.lastToken(ptr_type.ast.align_node) + 1, .space); // rparen
882 }944 }
883 }945 }
884946
885 if (ptr_type.ast.addrspace_node != 0) {947 if (ptr_type.ast.addrspace_node != 0) {
886 const addrspace_first = tree.firstToken(ptr_type.ast.addrspace_node);948 const addrspace_first = tree.firstToken(ptr_type.ast.addrspace_node);
887 try renderToken(ais, tree, addrspace_first - 2, .none); // addrspace949 try renderToken(r, addrspace_first - 2, .none); // addrspace
888 try renderToken(ais, tree, addrspace_first - 1, .none); // lparen950 try renderToken(r, addrspace_first - 1, .none); // lparen
889 try renderExpression(gpa, ais, tree, ptr_type.ast.addrspace_node, .none);951 try renderExpression(r, ptr_type.ast.addrspace_node, .none);
890 try renderToken(ais, tree, tree.lastToken(ptr_type.ast.addrspace_node) + 1, .space); // rparen952 try renderToken(r, tree.lastToken(ptr_type.ast.addrspace_node) + 1, .space); // rparen
891 }953 }
892954
893 if (ptr_type.const_token) |const_token| {955 if (ptr_type.const_token) |const_token| {
894 try renderToken(ais, tree, const_token, .space);956 try renderToken(r, const_token, .space);
895 }957 }
896958
897 if (ptr_type.volatile_token) |volatile_token| {959 if (ptr_type.volatile_token) |volatile_token| {
898 try renderToken(ais, tree, volatile_token, .space);960 try renderToken(r, volatile_token, .space);
899 }961 }
900962
901 try renderExpression(gpa, ais, tree, ptr_type.ast.child_type, space);963 try renderExpression(r, ptr_type.ast.child_type, space);
902}964}
903965
904fn renderSlice(966fn renderSlice(
905 gpa: Allocator,967 r: *Render,
906 ais: *Ais,
907 tree: Ast,
908 slice_node: Ast.Node.Index,968 slice_node: Ast.Node.Index,
909 slice: Ast.full.Slice,969 slice: Ast.full.Slice,
910 space: Space,970 space: Space,
911) Error!void {971) Error!void {
972 const tree = r.tree;
912 const node_tags = tree.nodes.items(.tag);973 const node_tags = tree.nodes.items(.tag);
913 const after_start_space_bool = nodeCausesSliceOpSpace(node_tags[slice.ast.start]) or974 const after_start_space_bool = nodeCausesSliceOpSpace(node_tags[slice.ast.start]) or
914 if (slice.ast.end != 0) nodeCausesSliceOpSpace(node_tags[slice.ast.end]) else false;975 if (slice.ast.end != 0) nodeCausesSliceOpSpace(node_tags[slice.ast.end]) else false;
...@@ -917,33 +978,32 @@ fn renderSlice(...@@ -917,33 +978,32 @@ fn renderSlice(
917 after_start_space978 after_start_space
918 else if (slice.ast.sentinel != 0) Space.space else Space.none;979 else if (slice.ast.sentinel != 0) Space.space else Space.none;
919980
920 try renderExpression(gpa, ais, tree, slice.ast.sliced, .none);981 try renderExpression(r, slice.ast.sliced, .none);
921 try renderToken(ais, tree, slice.ast.lbracket, .none); // lbracket982 try renderToken(r, slice.ast.lbracket, .none); // lbracket
922983
923 const start_last = tree.lastToken(slice.ast.start);984 const start_last = tree.lastToken(slice.ast.start);
924 try renderExpression(gpa, ais, tree, slice.ast.start, after_start_space);985 try renderExpression(r, slice.ast.start, after_start_space);
925 try renderToken(ais, tree, start_last + 1, after_dots_space); // ellipsis2 ("..")986 try renderToken(r, start_last + 1, after_dots_space); // ellipsis2 ("..")
926987
927 if (slice.ast.end != 0) {988 if (slice.ast.end != 0) {
928 const after_end_space = if (slice.ast.sentinel != 0) Space.space else Space.none;989 const after_end_space = if (slice.ast.sentinel != 0) Space.space else Space.none;
929 try renderExpression(gpa, ais, tree, slice.ast.end, after_end_space);990 try renderExpression(r, slice.ast.end, after_end_space);
930 }991 }
931992
932 if (slice.ast.sentinel != 0) {993 if (slice.ast.sentinel != 0) {
933 try renderToken(ais, tree, tree.firstToken(slice.ast.sentinel) - 1, .none); // colon994 try renderToken(r, tree.firstToken(slice.ast.sentinel) - 1, .none); // colon
934 try renderExpression(gpa, ais, tree, slice.ast.sentinel, .none);995 try renderExpression(r, slice.ast.sentinel, .none);
935 }996 }
936997
937 try renderToken(ais, tree, tree.lastToken(slice_node), space); // rbracket998 try renderToken(r, tree.lastToken(slice_node), space); // rbracket
938}999}
9391000
940fn renderAsmOutput(1001fn renderAsmOutput(
941 gpa: Allocator,1002 r: *Render,
942 ais: *Ais,
943 tree: Ast,
944 asm_output: Ast.Node.Index,1003 asm_output: Ast.Node.Index,
945 space: Space,1004 space: Space,
946) Error!void {1005) Error!void {
1006 const tree = r.tree;
947 const token_tags = tree.tokens.items(.tag);1007 const token_tags = tree.tokens.items(.tag);
948 const node_tags = tree.nodes.items(.tag);1008 const node_tags = tree.nodes.items(.tag);
949 const main_tokens = tree.nodes.items(.main_token);1009 const main_tokens = tree.nodes.items(.main_token);
...@@ -951,77 +1011,95 @@ fn renderAsmOutput(...@@ -951,77 +1011,95 @@ fn renderAsmOutput(
951 assert(node_tags[asm_output] == .asm_output);1011 assert(node_tags[asm_output] == .asm_output);
952 const symbolic_name = main_tokens[asm_output];1012 const symbolic_name = main_tokens[asm_output];
9531013
954 try renderToken(ais, tree, symbolic_name - 1, .none); // lbracket1014 try renderToken(r, symbolic_name - 1, .none); // lbracket
955 try renderIdentifier(ais, tree, symbolic_name, .none, .eagerly_unquote); // ident1015 try renderIdentifier(r, symbolic_name, .none, .eagerly_unquote); // ident
956 try renderToken(ais, tree, symbolic_name + 1, .space); // rbracket1016 try renderToken(r, symbolic_name + 1, .space); // rbracket
957 try renderToken(ais, tree, symbolic_name + 2, .space); // "constraint"1017 try renderToken(r, symbolic_name + 2, .space); // "constraint"
958 try renderToken(ais, tree, symbolic_name + 3, .none); // lparen1018 try renderToken(r, symbolic_name + 3, .none); // lparen
9591019
960 if (token_tags[symbolic_name + 4] == .arrow) {1020 if (token_tags[symbolic_name + 4] == .arrow) {
961 try renderToken(ais, tree, symbolic_name + 4, .space); // ->1021 try renderToken(r, symbolic_name + 4, .space); // ->
962 try renderExpression(gpa, ais, tree, datas[asm_output].lhs, Space.none);1022 try renderExpression(r, datas[asm_output].lhs, Space.none);
963 return renderToken(ais, tree, datas[asm_output].rhs, space); // rparen1023 return renderToken(r, datas[asm_output].rhs, space); // rparen
964 } else {1024 } else {
965 try renderIdentifier(ais, tree, symbolic_name + 4, .none, .eagerly_unquote); // ident1025 try renderIdentifier(r, symbolic_name + 4, .none, .eagerly_unquote); // ident
966 return renderToken(ais, tree, symbolic_name + 5, space); // rparen1026 return renderToken(r, symbolic_name + 5, space); // rparen
967 }1027 }
968}1028}
9691029
970fn renderAsmInput(1030fn renderAsmInput(
971 gpa: Allocator,1031 r: *Render,
972 ais: *Ais,
973 tree: Ast,
974 asm_input: Ast.Node.Index,1032 asm_input: Ast.Node.Index,
975 space: Space,1033 space: Space,
976) Error!void {1034) Error!void {
1035 const tree = r.tree;
977 const node_tags = tree.nodes.items(.tag);1036 const node_tags = tree.nodes.items(.tag);
978 const main_tokens = tree.nodes.items(.main_token);1037 const main_tokens = tree.nodes.items(.main_token);
979 const datas = tree.nodes.items(.data);1038 const datas = tree.nodes.items(.data);
980 assert(node_tags[asm_input] == .asm_input);1039 assert(node_tags[asm_input] == .asm_input);
981 const symbolic_name = main_tokens[asm_input];1040 const symbolic_name = main_tokens[asm_input];
9821041
983 try renderToken(ais, tree, symbolic_name - 1, .none); // lbracket1042 try renderToken(r, symbolic_name - 1, .none); // lbracket
984 try renderIdentifier(ais, tree, symbolic_name, .none, .eagerly_unquote); // ident1043 try renderIdentifier(r, symbolic_name, .none, .eagerly_unquote); // ident
985 try renderToken(ais, tree, symbolic_name + 1, .space); // rbracket1044 try renderToken(r, symbolic_name + 1, .space); // rbracket
986 try renderToken(ais, tree, symbolic_name + 2, .space); // "constraint"1045 try renderToken(r, symbolic_name + 2, .space); // "constraint"
987 try renderToken(ais, tree, symbolic_name + 3, .none); // lparen1046 try renderToken(r, symbolic_name + 3, .none); // lparen
988 try renderExpression(gpa, ais, tree, datas[asm_input].lhs, Space.none);1047 try renderExpression(r, datas[asm_input].lhs, Space.none);
989 return renderToken(ais, tree, datas[asm_input].rhs, space); // rparen1048 return renderToken(r, datas[asm_input].rhs, space); // rparen
990}1049}
9911050
992fn renderVarDecl(1051fn renderVarDecl(
993 gpa: Allocator,1052 r: *Render,
994 ais: *Ais,1053 var_decl: Ast.full.VarDecl,
995 tree: Ast,1054 /// Destructures intentionally ignore leading `comptime` tokens.
1055 ignore_comptime_token: bool,
1056 /// `comma_space` and `space` are used for destructure LHS decls.
1057 space: Space,
1058) Error!void {
1059 try renderVarDeclWithoutFixups(r, var_decl, ignore_comptime_token, space);
1060 if (r.fixups.unused_var_decls.contains(var_decl.ast.mut_token)) {
1061 // Discard the variable like this: `_ = foo;`
1062 const w = r.ais.writer();
1063 try w.writeAll("_ = ");
1064 try w.writeAll(tokenSliceForRender(r.tree, var_decl.ast.mut_token + 1));
1065 try w.writeAll(";\n");
1066 }
1067}
1068
1069fn renderVarDeclWithoutFixups(
1070 r: *Render,
996 var_decl: Ast.full.VarDecl,1071 var_decl: Ast.full.VarDecl,
997 /// Destructures intentionally ignore leading `comptime` tokens.1072 /// Destructures intentionally ignore leading `comptime` tokens.
998 ignore_comptime_token: bool,1073 ignore_comptime_token: bool,
999 /// `comma_space` and `space` are used for destructure LHS decls.1074 /// `comma_space` and `space` are used for destructure LHS decls.
1000 space: Space,1075 space: Space,
1001) Error!void {1076) Error!void {
1077 const tree = r.tree;
1078 const ais = r.ais;
1079
1002 if (var_decl.visib_token) |visib_token| {1080 if (var_decl.visib_token) |visib_token| {
1003 try renderToken(ais, tree, visib_token, Space.space); // pub1081 try renderToken(r, visib_token, Space.space); // pub
1004 }1082 }
10051083
1006 if (var_decl.extern_export_token) |extern_export_token| {1084 if (var_decl.extern_export_token) |extern_export_token| {
1007 try renderToken(ais, tree, extern_export_token, Space.space); // extern1085 try renderToken(r, extern_export_token, Space.space); // extern
10081086
1009 if (var_decl.lib_name) |lib_name| {1087 if (var_decl.lib_name) |lib_name| {
1010 try renderToken(ais, tree, lib_name, Space.space); // "lib"1088 try renderToken(r, lib_name, Space.space); // "lib"
1011 }1089 }
1012 }1090 }
10131091
1014 if (var_decl.threadlocal_token) |thread_local_token| {1092 if (var_decl.threadlocal_token) |thread_local_token| {
1015 try renderToken(ais, tree, thread_local_token, Space.space); // threadlocal1093 try renderToken(r, thread_local_token, Space.space); // threadlocal
1016 }1094 }
10171095
1018 if (!ignore_comptime_token) {1096 if (!ignore_comptime_token) {
1019 if (var_decl.comptime_token) |comptime_token| {1097 if (var_decl.comptime_token) |comptime_token| {
1020 try renderToken(ais, tree, comptime_token, Space.space); // comptime1098 try renderToken(r, comptime_token, Space.space); // comptime
1021 }1099 }
1022 }1100 }
10231101
1024 try renderToken(ais, tree, var_decl.ast.mut_token, .space); // var1102 try renderToken(r, var_decl.ast.mut_token, .space); // var
10251103
1026 if (var_decl.ast.type_node != 0 or var_decl.ast.align_node != 0 or1104 if (var_decl.ast.type_node != 0 or var_decl.ast.align_node != 0 or
1027 var_decl.ast.addrspace_node != 0 or var_decl.ast.section_node != 0 or1105 var_decl.ast.addrspace_node != 0 or var_decl.ast.section_node != 0 or
...@@ -1036,19 +1114,19 @@ fn renderVarDecl(...@@ -1036,19 +1114,19 @@ fn renderVarDecl(
1036 else1114 else
1037 Space.none;1115 Space.none;
10381116
1039 try renderIdentifier(ais, tree, var_decl.ast.mut_token + 1, name_space, .preserve_when_shadowing); // name1117 try renderIdentifier(r, var_decl.ast.mut_token + 1, name_space, .preserve_when_shadowing); // name
1040 } else {1118 } else {
1041 return renderIdentifier(ais, tree, var_decl.ast.mut_token + 1, space, .preserve_when_shadowing); // name1119 return renderIdentifier(r, var_decl.ast.mut_token + 1, space, .preserve_when_shadowing); // name
1042 }1120 }
10431121
1044 if (var_decl.ast.type_node != 0) {1122 if (var_decl.ast.type_node != 0) {
1045 try renderToken(ais, tree, var_decl.ast.mut_token + 2, Space.space); // :1123 try renderToken(r, var_decl.ast.mut_token + 2, Space.space); // :
1046 if (var_decl.ast.align_node != 0 or var_decl.ast.addrspace_node != 0 or1124 if (var_decl.ast.align_node != 0 or var_decl.ast.addrspace_node != 0 or
1047 var_decl.ast.section_node != 0 or var_decl.ast.init_node != 0)1125 var_decl.ast.section_node != 0 or var_decl.ast.init_node != 0)
1048 {1126 {
1049 try renderExpression(gpa, ais, tree, var_decl.ast.type_node, .space);1127 try renderExpression(r, var_decl.ast.type_node, .space);
1050 } else {1128 } else {
1051 return renderExpression(gpa, ais, tree, var_decl.ast.type_node, space);1129 return renderExpression(r, var_decl.ast.type_node, space);
1052 }1130 }
1053 }1131 }
10541132
...@@ -1056,15 +1134,15 @@ fn renderVarDecl(...@@ -1056,15 +1134,15 @@ fn renderVarDecl(
1056 const lparen = tree.firstToken(var_decl.ast.align_node) - 1;1134 const lparen = tree.firstToken(var_decl.ast.align_node) - 1;
1057 const align_kw = lparen - 1;1135 const align_kw = lparen - 1;
1058 const rparen = tree.lastToken(var_decl.ast.align_node) + 1;1136 const rparen = tree.lastToken(var_decl.ast.align_node) + 1;
1059 try renderToken(ais, tree, align_kw, Space.none); // align1137 try renderToken(r, align_kw, Space.none); // align
1060 try renderToken(ais, tree, lparen, Space.none); // (1138 try renderToken(r, lparen, Space.none); // (
1061 try renderExpression(gpa, ais, tree, var_decl.ast.align_node, Space.none);1139 try renderExpression(r, var_decl.ast.align_node, Space.none);
1062 if (var_decl.ast.addrspace_node != 0 or var_decl.ast.section_node != 0 or1140 if (var_decl.ast.addrspace_node != 0 or var_decl.ast.section_node != 0 or
1063 var_decl.ast.init_node != 0)1141 var_decl.ast.init_node != 0)
1064 {1142 {
1065 try renderToken(ais, tree, rparen, .space); // )1143 try renderToken(r, rparen, .space); // )
1066 } else {1144 } else {
1067 return renderToken(ais, tree, rparen, space); // )1145 return renderToken(r, rparen, space); // )
1068 }1146 }
1069 }1147 }
10701148
...@@ -1072,14 +1150,14 @@ fn renderVarDecl(...@@ -1072,14 +1150,14 @@ fn renderVarDecl(
1072 const lparen = tree.firstToken(var_decl.ast.addrspace_node) - 1;1150 const lparen = tree.firstToken(var_decl.ast.addrspace_node) - 1;
1073 const addrspace_kw = lparen - 1;1151 const addrspace_kw = lparen - 1;
1074 const rparen = tree.lastToken(var_decl.ast.addrspace_node) + 1;1152 const rparen = tree.lastToken(var_decl.ast.addrspace_node) + 1;
1075 try renderToken(ais, tree, addrspace_kw, Space.none); // addrspace1153 try renderToken(r, addrspace_kw, Space.none); // addrspace
1076 try renderToken(ais, tree, lparen, Space.none); // (1154 try renderToken(r, lparen, Space.none); // (
1077 try renderExpression(gpa, ais, tree, var_decl.ast.addrspace_node, Space.none);1155 try renderExpression(r, var_decl.ast.addrspace_node, Space.none);
1078 if (var_decl.ast.section_node != 0 or var_decl.ast.init_node != 0) {1156 if (var_decl.ast.section_node != 0 or var_decl.ast.init_node != 0) {
1079 try renderToken(ais, tree, rparen, .space); // )1157 try renderToken(r, rparen, .space); // )
1080 } else {1158 } else {
1081 try renderToken(ais, tree, rparen, .none); // )1159 try renderToken(r, rparen, .none); // )
1082 return renderToken(ais, tree, rparen + 1, Space.newline); // ;1160 return renderToken(r, rparen + 1, Space.newline); // ;
1083 }1161 }
1084 }1162 }
10851163
...@@ -1087,13 +1165,13 @@ fn renderVarDecl(...@@ -1087,13 +1165,13 @@ fn renderVarDecl(
1087 const lparen = tree.firstToken(var_decl.ast.section_node) - 1;1165 const lparen = tree.firstToken(var_decl.ast.section_node) - 1;
1088 const section_kw = lparen - 1;1166 const section_kw = lparen - 1;
1089 const rparen = tree.lastToken(var_decl.ast.section_node) + 1;1167 const rparen = tree.lastToken(var_decl.ast.section_node) + 1;
1090 try renderToken(ais, tree, section_kw, Space.none); // linksection1168 try renderToken(r, section_kw, Space.none); // linksection
1091 try renderToken(ais, tree, lparen, Space.none); // (1169 try renderToken(r, lparen, Space.none); // (
1092 try renderExpression(gpa, ais, tree, var_decl.ast.section_node, Space.none);1170 try renderExpression(r, var_decl.ast.section_node, Space.none);
1093 if (var_decl.ast.init_node != 0) {1171 if (var_decl.ast.init_node != 0) {
1094 try renderToken(ais, tree, rparen, .space); // )1172 try renderToken(r, rparen, .space); // )
1095 } else {1173 } else {
1096 return renderToken(ais, tree, rparen, space); // )1174 return renderToken(r, rparen, space); // )
1097 }1175 }
1098 }1176 }
10991177
...@@ -1103,15 +1181,15 @@ fn renderVarDecl(...@@ -1103,15 +1181,15 @@ fn renderVarDecl(
1103 const eq_space: Space = if (tree.tokensOnSameLine(eq_token, eq_token + 1)) .space else .newline;1181 const eq_space: Space = if (tree.tokensOnSameLine(eq_token, eq_token + 1)) .space else .newline;
1104 {1182 {
1105 ais.pushIndent();1183 ais.pushIndent();
1106 try renderToken(ais, tree, eq_token, eq_space); // =1184 try renderToken(r, eq_token, eq_space); // =
1107 ais.popIndent();1185 ais.popIndent();
1108 }1186 }
1109 ais.pushIndentOneShot();1187 ais.pushIndentOneShot();
1110 return renderExpression(gpa, ais, tree, var_decl.ast.init_node, space); // ;1188 return renderExpression(r, var_decl.ast.init_node, space); // ;
1111}1189}
11121190
1113fn renderIf(gpa: Allocator, ais: *Ais, tree: Ast, if_node: Ast.full.If, space: Space) Error!void {1191fn renderIf(r: *Render, if_node: Ast.full.If, space: Space) Error!void {
1114 return renderWhile(gpa, ais, tree, .{1192 return renderWhile(r, .{
1115 .ast = .{1193 .ast = .{
1116 .while_token = if_node.ast.if_token,1194 .while_token = if_node.ast.if_token,
1117 .cond_expr = if_node.ast.cond_expr,1195 .cond_expr = if_node.ast.cond_expr,
...@@ -1129,40 +1207,41 @@ fn renderIf(gpa: Allocator, ais: *Ais, tree: Ast, if_node: Ast.full.If, space: S...@@ -1129,40 +1207,41 @@ fn renderIf(gpa: Allocator, ais: *Ais, tree: Ast, if_node: Ast.full.If, space: S
11291207
1130/// Note that this function is additionally used to render if expressions, with1208/// Note that this function is additionally used to render if expressions, with
1131/// respective values set to null.1209/// respective values set to null.
1132fn renderWhile(gpa: Allocator, ais: *Ais, tree: Ast, while_node: Ast.full.While, space: Space) Error!void {1210fn renderWhile(r: *Render, while_node: Ast.full.While, space: Space) Error!void {
1211 const tree = r.tree;
1133 const token_tags = tree.tokens.items(.tag);1212 const token_tags = tree.tokens.items(.tag);
11341213
1135 if (while_node.label_token) |label| {1214 if (while_node.label_token) |label| {
1136 try renderIdentifier(ais, tree, label, .none, .eagerly_unquote); // label1215 try renderIdentifier(r, label, .none, .eagerly_unquote); // label
1137 try renderToken(ais, tree, label + 1, .space); // :1216 try renderToken(r, label + 1, .space); // :
1138 }1217 }
11391218
1140 if (while_node.inline_token) |inline_token| {1219 if (while_node.inline_token) |inline_token| {
1141 try renderToken(ais, tree, inline_token, .space); // inline1220 try renderToken(r, inline_token, .space); // inline
1142 }1221 }
11431222
1144 try renderToken(ais, tree, while_node.ast.while_token, .space); // if/for/while1223 try renderToken(r, while_node.ast.while_token, .space); // if/for/while
1145 try renderToken(ais, tree, while_node.ast.while_token + 1, .none); // lparen1224 try renderToken(r, while_node.ast.while_token + 1, .none); // lparen
1146 try renderExpression(gpa, ais, tree, while_node.ast.cond_expr, .none); // condition1225 try renderExpression(r, while_node.ast.cond_expr, .none); // condition
11471226
1148 var last_prefix_token = tree.lastToken(while_node.ast.cond_expr) + 1; // rparen1227 var last_prefix_token = tree.lastToken(while_node.ast.cond_expr) + 1; // rparen
11491228
1150 if (while_node.payload_token) |payload_token| {1229 if (while_node.payload_token) |payload_token| {
1151 try renderToken(ais, tree, last_prefix_token, .space);1230 try renderToken(r, last_prefix_token, .space);
1152 try renderToken(ais, tree, payload_token - 1, .none); // |1231 try renderToken(r, payload_token - 1, .none); // |
1153 const ident = blk: {1232 const ident = blk: {
1154 if (token_tags[payload_token] == .asterisk) {1233 if (token_tags[payload_token] == .asterisk) {
1155 try renderToken(ais, tree, payload_token, .none); // *1234 try renderToken(r, payload_token, .none); // *
1156 break :blk payload_token + 1;1235 break :blk payload_token + 1;
1157 } else {1236 } else {
1158 break :blk payload_token;1237 break :blk payload_token;
1159 }1238 }
1160 };1239 };
1161 try renderIdentifier(ais, tree, ident, .none, .preserve_when_shadowing); // identifier1240 try renderIdentifier(r, ident, .none, .preserve_when_shadowing); // identifier
1162 const pipe = blk: {1241 const pipe = blk: {
1163 if (token_tags[ident + 1] == .comma) {1242 if (token_tags[ident + 1] == .comma) {
1164 try renderToken(ais, tree, ident + 1, .space); // ,1243 try renderToken(r, ident + 1, .space); // ,
1165 try renderIdentifier(ais, tree, ident + 2, .none, .preserve_when_shadowing); // index1244 try renderIdentifier(r, ident + 2, .none, .preserve_when_shadowing); // index
1166 break :blk ident + 3;1245 break :blk ident + 3;
1167 } else {1246 } else {
1168 break :blk ident + 1;1247 break :blk ident + 1;
...@@ -1172,18 +1251,16 @@ fn renderWhile(gpa: Allocator, ais: *Ais, tree: Ast, while_node: Ast.full.While,...@@ -1172,18 +1251,16 @@ fn renderWhile(gpa: Allocator, ais: *Ais, tree: Ast, while_node: Ast.full.While,
1172 }1251 }
11731252
1174 if (while_node.ast.cont_expr != 0) {1253 if (while_node.ast.cont_expr != 0) {
1175 try renderToken(ais, tree, last_prefix_token, .space);1254 try renderToken(r, last_prefix_token, .space);
1176 const lparen = tree.firstToken(while_node.ast.cont_expr) - 1;1255 const lparen = tree.firstToken(while_node.ast.cont_expr) - 1;
1177 try renderToken(ais, tree, lparen - 1, .space); // :1256 try renderToken(r, lparen - 1, .space); // :
1178 try renderToken(ais, tree, lparen, .none); // lparen1257 try renderToken(r, lparen, .none); // lparen
1179 try renderExpression(gpa, ais, tree, while_node.ast.cont_expr, .none);1258 try renderExpression(r, while_node.ast.cont_expr, .none);
1180 last_prefix_token = tree.lastToken(while_node.ast.cont_expr) + 1; // rparen1259 last_prefix_token = tree.lastToken(while_node.ast.cont_expr) + 1; // rparen
1181 }1260 }
11821261
1183 try renderThenElse(1262 try renderThenElse(
1184 gpa,1263 r,
1185 ais,
1186 tree,
1187 last_prefix_token,1264 last_prefix_token,
1188 while_node.ast.then_expr,1265 while_node.ast.then_expr,
1189 while_node.else_token,1266 while_node.else_token,
...@@ -1194,9 +1271,7 @@ fn renderWhile(gpa: Allocator, ais: *Ais, tree: Ast, while_node: Ast.full.While,...@@ -1194,9 +1271,7 @@ fn renderWhile(gpa: Allocator, ais: *Ais, tree: Ast, while_node: Ast.full.While,
1194}1271}
11951272
1196fn renderThenElse(1273fn renderThenElse(
1197 gpa: Allocator,1274 r: *Render,
1198 ais: *Ais,
1199 tree: Ast,
1200 last_prefix_token: Ast.TokenIndex,1275 last_prefix_token: Ast.TokenIndex,
1201 then_expr: Ast.Node.Index,1276 then_expr: Ast.Node.Index,
1202 else_token: Ast.TokenIndex,1277 else_token: Ast.TokenIndex,
...@@ -1204,33 +1279,35 @@ fn renderThenElse(...@@ -1204,33 +1279,35 @@ fn renderThenElse(
1204 else_expr: Ast.Node.Index,1279 else_expr: Ast.Node.Index,
1205 space: Space,1280 space: Space,
1206) Error!void {1281) Error!void {
1282 const tree = r.tree;
1283 const ais = r.ais;
1207 const node_tags = tree.nodes.items(.tag);1284 const node_tags = tree.nodes.items(.tag);
1208 const then_expr_is_block = nodeIsBlock(node_tags[then_expr]);1285 const then_expr_is_block = nodeIsBlock(node_tags[then_expr]);
1209 const indent_then_expr = !then_expr_is_block and1286 const indent_then_expr = !then_expr_is_block and
1210 !tree.tokensOnSameLine(last_prefix_token, tree.firstToken(then_expr));1287 !tree.tokensOnSameLine(last_prefix_token, tree.firstToken(then_expr));
1211 if (indent_then_expr or (then_expr_is_block and ais.isLineOverIndented())) {1288 if (indent_then_expr or (then_expr_is_block and ais.isLineOverIndented())) {
1212 ais.pushIndentNextLine();1289 ais.pushIndentNextLine();
1213 try renderToken(ais, tree, last_prefix_token, .newline);1290 try renderToken(r, last_prefix_token, .newline);
1214 ais.popIndent();1291 ais.popIndent();
1215 } else {1292 } else {
1216 try renderToken(ais, tree, last_prefix_token, .space);1293 try renderToken(r, last_prefix_token, .space);
1217 }1294 }
12181295
1219 if (else_expr != 0) {1296 if (else_expr != 0) {
1220 if (indent_then_expr) {1297 if (indent_then_expr) {
1221 ais.pushIndent();1298 ais.pushIndent();
1222 try renderExpression(gpa, ais, tree, then_expr, .newline);1299 try renderExpression(r, then_expr, .newline);
1223 ais.popIndent();1300 ais.popIndent();
1224 } else {1301 } else {
1225 try renderExpression(gpa, ais, tree, then_expr, .space);1302 try renderExpression(r, then_expr, .space);
1226 }1303 }
12271304
1228 var last_else_token = else_token;1305 var last_else_token = else_token;
12291306
1230 if (maybe_error_token) |error_token| {1307 if (maybe_error_token) |error_token| {
1231 try renderToken(ais, tree, else_token, .space); // else1308 try renderToken(r, else_token, .space); // else
1232 try renderToken(ais, tree, error_token - 1, .none); // |1309 try renderToken(r, error_token - 1, .none); // |
1233 try renderIdentifier(ais, tree, error_token, .none, .preserve_when_shadowing); // identifier1310 try renderIdentifier(r, error_token, .none, .preserve_when_shadowing); // identifier
1234 last_else_token = error_token + 1; // |1311 last_else_token = error_token + 1; // |
1235 }1312 }
12361313
...@@ -1239,53 +1316,55 @@ fn renderThenElse(...@@ -1239,53 +1316,55 @@ fn renderThenElse(
1239 !nodeIsIfForWhileSwitch(node_tags[else_expr]);1316 !nodeIsIfForWhileSwitch(node_tags[else_expr]);
1240 if (indent_else_expr) {1317 if (indent_else_expr) {
1241 ais.pushIndentNextLine();1318 ais.pushIndentNextLine();
1242 try renderToken(ais, tree, last_else_token, .newline);1319 try renderToken(r, last_else_token, .newline);
1243 ais.popIndent();1320 ais.popIndent();
1244 try renderExpressionIndented(gpa, ais, tree, else_expr, space);1321 try renderExpressionIndented(r, else_expr, space);
1245 } else {1322 } else {
1246 try renderToken(ais, tree, last_else_token, .space);1323 try renderToken(r, last_else_token, .space);
1247 try renderExpression(gpa, ais, tree, else_expr, space);1324 try renderExpression(r, else_expr, space);
1248 }1325 }
1249 } else {1326 } else {
1250 if (indent_then_expr) {1327 if (indent_then_expr) {
1251 try renderExpressionIndented(gpa, ais, tree, then_expr, space);1328 try renderExpressionIndented(r, then_expr, space);
1252 } else {1329 } else {
1253 try renderExpression(gpa, ais, tree, then_expr, space);1330 try renderExpression(r, then_expr, space);
1254 }1331 }
1255 }1332 }
1256}1333}
12571334
1258fn renderFor(gpa: Allocator, ais: *Ais, tree: Ast, for_node: Ast.full.For, space: Space) Error!void {1335fn renderFor(r: *Render, for_node: Ast.full.For, space: Space) Error!void {
1336 const tree = r.tree;
1337 const ais = r.ais;
1259 const token_tags = tree.tokens.items(.tag);1338 const token_tags = tree.tokens.items(.tag);
12601339
1261 if (for_node.label_token) |label| {1340 if (for_node.label_token) |label| {
1262 try renderIdentifier(ais, tree, label, .none, .eagerly_unquote); // label1341 try renderIdentifier(r, label, .none, .eagerly_unquote); // label
1263 try renderToken(ais, tree, label + 1, .space); // :1342 try renderToken(r, label + 1, .space); // :
1264 }1343 }
12651344
1266 if (for_node.inline_token) |inline_token| {1345 if (for_node.inline_token) |inline_token| {
1267 try renderToken(ais, tree, inline_token, .space); // inline1346 try renderToken(r, inline_token, .space); // inline
1268 }1347 }
12691348
1270 try renderToken(ais, tree, for_node.ast.for_token, .space); // if/for/while1349 try renderToken(r, for_node.ast.for_token, .space); // if/for/while
12711350
1272 const lparen = for_node.ast.for_token + 1;1351 const lparen = for_node.ast.for_token + 1;
1273 try renderParamList(gpa, ais, tree, lparen, for_node.ast.inputs, .space);1352 try renderParamList(r, lparen, for_node.ast.inputs, .space);
12741353
1275 var cur = for_node.payload_token;1354 var cur = for_node.payload_token;
1276 const pipe = std.mem.indexOfScalarPos(std.zig.Token.Tag, token_tags, cur, .pipe).?;1355 const pipe = std.mem.indexOfScalarPos(std.zig.Token.Tag, token_tags, cur, .pipe).?;
1277 if (token_tags[pipe - 1] == .comma) {1356 if (token_tags[pipe - 1] == .comma) {
1278 ais.pushIndentNextLine();1357 ais.pushIndentNextLine();
1279 try renderToken(ais, tree, cur - 1, .newline); // |1358 try renderToken(r, cur - 1, .newline); // |
1280 while (true) {1359 while (true) {
1281 if (token_tags[cur] == .asterisk) {1360 if (token_tags[cur] == .asterisk) {
1282 try renderToken(ais, tree, cur, .none); // *1361 try renderToken(r, cur, .none); // *
1283 cur += 1;1362 cur += 1;
1284 }1363 }
1285 try renderIdentifier(ais, tree, cur, .none, .preserve_when_shadowing); // identifier1364 try renderIdentifier(r, cur, .none, .preserve_when_shadowing); // identifier
1286 cur += 1;1365 cur += 1;
1287 if (token_tags[cur] == .comma) {1366 if (token_tags[cur] == .comma) {
1288 try renderToken(ais, tree, cur, .newline); // ,1367 try renderToken(r, cur, .newline); // ,
1289 cur += 1;1368 cur += 1;
1290 }1369 }
1291 if (token_tags[cur] == .pipe) {1370 if (token_tags[cur] == .pipe) {
...@@ -1294,16 +1373,16 @@ fn renderFor(gpa: Allocator, ais: *Ais, tree: Ast, for_node: Ast.full.For, space...@@ -1294,16 +1373,16 @@ fn renderFor(gpa: Allocator, ais: *Ais, tree: Ast, for_node: Ast.full.For, space
1294 }1373 }
1295 ais.popIndent();1374 ais.popIndent();
1296 } else {1375 } else {
1297 try renderToken(ais, tree, cur - 1, .none); // |1376 try renderToken(r, cur - 1, .none); // |
1298 while (true) {1377 while (true) {
1299 if (token_tags[cur] == .asterisk) {1378 if (token_tags[cur] == .asterisk) {
1300 try renderToken(ais, tree, cur, .none); // *1379 try renderToken(r, cur, .none); // *
1301 cur += 1;1380 cur += 1;
1302 }1381 }
1303 try renderIdentifier(ais, tree, cur, .none, .preserve_when_shadowing); // identifier1382 try renderIdentifier(r, cur, .none, .preserve_when_shadowing); // identifier
1304 cur += 1;1383 cur += 1;
1305 if (token_tags[cur] == .comma) {1384 if (token_tags[cur] == .comma) {
1306 try renderToken(ais, tree, cur, .space); // ,1385 try renderToken(r, cur, .space); // ,
1307 cur += 1;1386 cur += 1;
1308 }1387 }
1309 if (token_tags[cur] == .pipe) {1388 if (token_tags[cur] == .pipe) {
...@@ -1313,9 +1392,7 @@ fn renderFor(gpa: Allocator, ais: *Ais, tree: Ast, for_node: Ast.full.For, space...@@ -1313,9 +1392,7 @@ fn renderFor(gpa: Allocator, ais: *Ais, tree: Ast, for_node: Ast.full.For, space
1313 }1392 }
13141393
1315 try renderThenElse(1394 try renderThenElse(
1316 gpa,1395 r,
1317 ais,
1318 tree,
1319 cur,1396 cur,
1320 for_node.ast.then_expr,1397 for_node.ast.then_expr,
1321 for_node.else_token,1398 for_node.else_token,
...@@ -1326,13 +1403,13 @@ fn renderFor(gpa: Allocator, ais: *Ais, tree: Ast, for_node: Ast.full.For, space...@@ -1326,13 +1403,13 @@ fn renderFor(gpa: Allocator, ais: *Ais, tree: Ast, for_node: Ast.full.For, space
1326}1403}
13271404
1328fn renderContainerField(1405fn renderContainerField(
1329 gpa: Allocator,1406 r: *Render,
1330 ais: *Ais,
1331 tree: Ast,
1332 container: Container,1407 container: Container,
1333 field_param: Ast.full.ContainerField,1408 field_param: Ast.full.ContainerField,
1334 space: Space,1409 space: Space,
1335) Error!void {1410) Error!void {
1411 const tree = r.tree;
1412 const ais = r.ais;
1336 var field = field_param;1413 var field = field_param;
1337 if (container != .tuple) field.convertToNonTupleLike(tree.nodes);1414 if (container != .tuple) field.convertToNonTupleLike(tree.nodes);
1338 const quote: QuoteBehavior = switch (container) {1415 const quote: QuoteBehavior = switch (container) {
...@@ -1341,102 +1418,102 @@ fn renderContainerField(...@@ -1341,102 +1418,102 @@ fn renderContainerField(
1341 };1418 };
13421419
1343 if (field.comptime_token) |t| {1420 if (field.comptime_token) |t| {
1344 try renderToken(ais, tree, t, .space); // comptime1421 try renderToken(r, t, .space); // comptime
1345 }1422 }
1346 if (field.ast.type_expr == 0 and field.ast.value_expr == 0) {1423 if (field.ast.type_expr == 0 and field.ast.value_expr == 0) {
1347 if (field.ast.align_expr != 0) {1424 if (field.ast.align_expr != 0) {
1348 try renderIdentifier(ais, tree, field.ast.main_token, .space, quote); // name1425 try renderIdentifier(r, field.ast.main_token, .space, quote); // name
1349 const lparen_token = tree.firstToken(field.ast.align_expr) - 1;1426 const lparen_token = tree.firstToken(field.ast.align_expr) - 1;
1350 const align_kw = lparen_token - 1;1427 const align_kw = lparen_token - 1;
1351 const rparen_token = tree.lastToken(field.ast.align_expr) + 1;1428 const rparen_token = tree.lastToken(field.ast.align_expr) + 1;
1352 try renderToken(ais, tree, align_kw, .none); // align1429 try renderToken(r, align_kw, .none); // align
1353 try renderToken(ais, tree, lparen_token, .none); // (1430 try renderToken(r, lparen_token, .none); // (
1354 try renderExpression(gpa, ais, tree, field.ast.align_expr, .none); // alignment1431 try renderExpression(r, field.ast.align_expr, .none); // alignment
1355 return renderToken(ais, tree, rparen_token, .space); // )1432 return renderToken(r, rparen_token, .space); // )
1356 }1433 }
1357 return renderIdentifierComma(ais, tree, field.ast.main_token, space, quote); // name1434 return renderIdentifierComma(r, field.ast.main_token, space, quote); // name
1358 }1435 }
1359 if (field.ast.type_expr != 0 and field.ast.value_expr == 0) {1436 if (field.ast.type_expr != 0 and field.ast.value_expr == 0) {
1360 if (!field.ast.tuple_like) {1437 if (!field.ast.tuple_like) {
1361 try renderIdentifier(ais, tree, field.ast.main_token, .none, quote); // name1438 try renderIdentifier(r, field.ast.main_token, .none, quote); // name
1362 try renderToken(ais, tree, field.ast.main_token + 1, .space); // :1439 try renderToken(r, field.ast.main_token + 1, .space); // :
1363 }1440 }
13641441
1365 if (field.ast.align_expr != 0) {1442 if (field.ast.align_expr != 0) {
1366 try renderExpression(gpa, ais, tree, field.ast.type_expr, .space); // type1443 try renderExpression(r, field.ast.type_expr, .space); // type
1367 const align_token = tree.firstToken(field.ast.align_expr) - 2;1444 const align_token = tree.firstToken(field.ast.align_expr) - 2;
1368 try renderToken(ais, tree, align_token, .none); // align1445 try renderToken(r, align_token, .none); // align
1369 try renderToken(ais, tree, align_token + 1, .none); // (1446 try renderToken(r, align_token + 1, .none); // (
1370 try renderExpression(gpa, ais, tree, field.ast.align_expr, .none); // alignment1447 try renderExpression(r, field.ast.align_expr, .none); // alignment
1371 const rparen = tree.lastToken(field.ast.align_expr) + 1;1448 const rparen = tree.lastToken(field.ast.align_expr) + 1;
1372 return renderTokenComma(ais, tree, rparen, space); // )1449 return renderTokenComma(r, rparen, space); // )
1373 } else {1450 } else {
1374 return renderExpressionComma(gpa, ais, tree, field.ast.type_expr, space); // type1451 return renderExpressionComma(r, field.ast.type_expr, space); // type
1375 }1452 }
1376 }1453 }
1377 if (field.ast.type_expr == 0 and field.ast.value_expr != 0) {1454 if (field.ast.type_expr == 0 and field.ast.value_expr != 0) {
1378 try renderIdentifier(ais, tree, field.ast.main_token, .space, quote); // name1455 try renderIdentifier(r, field.ast.main_token, .space, quote); // name
1379 if (field.ast.align_expr != 0) {1456 if (field.ast.align_expr != 0) {
1380 const lparen_token = tree.firstToken(field.ast.align_expr) - 1;1457 const lparen_token = tree.firstToken(field.ast.align_expr) - 1;
1381 const align_kw = lparen_token - 1;1458 const align_kw = lparen_token - 1;
1382 const rparen_token = tree.lastToken(field.ast.align_expr) + 1;1459 const rparen_token = tree.lastToken(field.ast.align_expr) + 1;
1383 try renderToken(ais, tree, align_kw, .none); // align1460 try renderToken(r, align_kw, .none); // align
1384 try renderToken(ais, tree, lparen_token, .none); // (1461 try renderToken(r, lparen_token, .none); // (
1385 try renderExpression(gpa, ais, tree, field.ast.align_expr, .none); // alignment1462 try renderExpression(r, field.ast.align_expr, .none); // alignment
1386 try renderToken(ais, tree, rparen_token, .space); // )1463 try renderToken(r, rparen_token, .space); // )
1387 }1464 }
1388 try renderToken(ais, tree, field.ast.main_token + 1, .space); // =1465 try renderToken(r, field.ast.main_token + 1, .space); // =
1389 return renderExpressionComma(gpa, ais, tree, field.ast.value_expr, space); // value1466 return renderExpressionComma(r, field.ast.value_expr, space); // value
1390 }1467 }
1391 if (!field.ast.tuple_like) {1468 if (!field.ast.tuple_like) {
1392 try renderIdentifier(ais, tree, field.ast.main_token, .none, quote); // name1469 try renderIdentifier(r, field.ast.main_token, .none, quote); // name
1393 try renderToken(ais, tree, field.ast.main_token + 1, .space); // :1470 try renderToken(r, field.ast.main_token + 1, .space); // :
1394 }1471 }
1395 try renderExpression(gpa, ais, tree, field.ast.type_expr, .space); // type1472 try renderExpression(r, field.ast.type_expr, .space); // type
13961473
1397 if (field.ast.align_expr != 0) {1474 if (field.ast.align_expr != 0) {
1398 const lparen_token = tree.firstToken(field.ast.align_expr) - 1;1475 const lparen_token = tree.firstToken(field.ast.align_expr) - 1;
1399 const align_kw = lparen_token - 1;1476 const align_kw = lparen_token - 1;
1400 const rparen_token = tree.lastToken(field.ast.align_expr) + 1;1477 const rparen_token = tree.lastToken(field.ast.align_expr) + 1;
1401 try renderToken(ais, tree, align_kw, .none); // align1478 try renderToken(r, align_kw, .none); // align
1402 try renderToken(ais, tree, lparen_token, .none); // (1479 try renderToken(r, lparen_token, .none); // (
1403 try renderExpression(gpa, ais, tree, field.ast.align_expr, .none); // alignment1480 try renderExpression(r, field.ast.align_expr, .none); // alignment
1404 try renderToken(ais, tree, rparen_token, .space); // )1481 try renderToken(r, rparen_token, .space); // )
1405 }1482 }
1406 const eq_token = tree.firstToken(field.ast.value_expr) - 1;1483 const eq_token = tree.firstToken(field.ast.value_expr) - 1;
1407 const eq_space: Space = if (tree.tokensOnSameLine(eq_token, eq_token + 1)) .space else .newline;1484 const eq_space: Space = if (tree.tokensOnSameLine(eq_token, eq_token + 1)) .space else .newline;
1408 {1485 {
1409 ais.pushIndent();1486 ais.pushIndent();
1410 try renderToken(ais, tree, eq_token, eq_space); // =1487 try renderToken(r, eq_token, eq_space); // =
1411 ais.popIndent();1488 ais.popIndent();
1412 }1489 }
14131490
1414 if (eq_space == .space)1491 if (eq_space == .space)
1415 return renderExpressionComma(gpa, ais, tree, field.ast.value_expr, space); // value1492 return renderExpressionComma(r, field.ast.value_expr, space); // value
14161493
1417 const token_tags = tree.tokens.items(.tag);1494 const token_tags = tree.tokens.items(.tag);
1418 const maybe_comma = tree.lastToken(field.ast.value_expr) + 1;1495 const maybe_comma = tree.lastToken(field.ast.value_expr) + 1;
14191496
1420 if (token_tags[maybe_comma] == .comma) {1497 if (token_tags[maybe_comma] == .comma) {
1421 ais.pushIndent();1498 ais.pushIndent();
1422 try renderExpression(gpa, ais, tree, field.ast.value_expr, .none); // value1499 try renderExpression(r, field.ast.value_expr, .none); // value
1423 ais.popIndent();1500 ais.popIndent();
1424 try renderToken(ais, tree, maybe_comma, .newline);1501 try renderToken(r, maybe_comma, .newline);
1425 } else {1502 } else {
1426 ais.pushIndent();1503 ais.pushIndent();
1427 try renderExpression(gpa, ais, tree, field.ast.value_expr, space); // value1504 try renderExpression(r, field.ast.value_expr, space); // value
1428 ais.popIndent();1505 ais.popIndent();
1429 }1506 }
1430}1507}
14311508
1432fn renderBuiltinCall(1509fn renderBuiltinCall(
1433 gpa: Allocator,1510 r: *Render,
1434 ais: *Ais,
1435 tree: Ast,
1436 builtin_token: Ast.TokenIndex,1511 builtin_token: Ast.TokenIndex,
1437 params: []const Ast.Node.Index,1512 params: []const Ast.Node.Index,
1438 space: Space,1513 space: Space,
1439) Error!void {1514) Error!void {
1515 const tree = r.tree;
1516 const ais = r.ais;
1440 const token_tags = tree.tokens.items(.tag);1517 const token_tags = tree.tokens.items(.tag);
14411518
1442 // TODO remove before release of 0.12.01519 // TODO remove before release of 0.12.0
...@@ -1465,14 +1542,14 @@ fn renderBuiltinCall(...@@ -1465,14 +1542,14 @@ fn renderBuiltinCall(
1465 if (token_tags[after_last_param_token] != .comma) {1542 if (token_tags[after_last_param_token] != .comma) {
1466 // Render all on one line, no trailing comma.1543 // Render all on one line, no trailing comma.
1467 try ais.writer().writeAll("@as");1544 try ais.writer().writeAll("@as");
1468 try renderToken(ais, tree, builtin_token + 1, .none); // (1545 try renderToken(r, builtin_token + 1, .none); // (
1469 try renderExpression(gpa, ais, tree, params[0], .comma_space);1546 try renderExpression(r, params[0], .comma_space);
1470 } else {1547 } else {
1471 // Render one param per line.1548 // Render one param per line.
1472 try ais.writer().writeAll("@as");1549 try ais.writer().writeAll("@as");
1473 ais.pushIndent();1550 ais.pushIndent();
1474 try renderToken(ais, tree, builtin_token + 1, .newline); // (1551 try renderToken(r, builtin_token + 1, .newline); // (
1475 try renderExpression(gpa, ais, tree, params[0], .comma);1552 try renderExpression(r, params[0], .comma);
1476 }1553 }
1477 }1554 }
1478 // Corresponding logic below builtin name rewrite below1555 // Corresponding logic below builtin name rewrite below
...@@ -1507,29 +1584,29 @@ fn renderBuiltinCall(...@@ -1507,29 +1584,29 @@ fn renderBuiltinCall(
1507 } else if (mem.eql(u8, slice, "@errSetCast")) {1584 } else if (mem.eql(u8, slice, "@errSetCast")) {
1508 try ais.writer().writeAll("@errorCast");1585 try ais.writer().writeAll("@errorCast");
1509 } else {1586 } else {
1510 try renderToken(ais, tree, builtin_token, .none); // @name1587 try renderToken(r, builtin_token, .none); // @name
1511 }1588 }
15121589
1513 if (rewrite_two_param_cast) {1590 if (rewrite_two_param_cast) {
1514 // Matches with corresponding logic above builtin name rewrite1591 // Matches with corresponding logic above builtin name rewrite
1515 const after_last_param_token = tree.lastToken(params[1]) + 1;1592 const after_last_param_token = tree.lastToken(params[1]) + 1;
1516 try ais.writer().writeAll("(");1593 try ais.writer().writeAll("(");
1517 try renderExpression(gpa, ais, tree, params[1], .none);1594 try renderExpression(r, params[1], .none);
1518 try ais.writer().writeAll(")");1595 try ais.writer().writeAll(")");
1519 if (token_tags[after_last_param_token] != .comma) {1596 if (token_tags[after_last_param_token] != .comma) {
1520 // Render all on one line, no trailing comma.1597 // Render all on one line, no trailing comma.
1521 return renderToken(ais, tree, after_last_param_token, space); // )1598 return renderToken(r, after_last_param_token, space); // )
1522 } else {1599 } else {
1523 // Render one param per line.1600 // Render one param per line.
1524 ais.popIndent();1601 ais.popIndent();
1525 try renderToken(ais, tree, after_last_param_token, .newline); // ,1602 try renderToken(r, after_last_param_token, .newline); // ,
1526 return renderToken(ais, tree, after_last_param_token + 1, space); // )1603 return renderToken(r, after_last_param_token + 1, space); // )
1527 }1604 }
1528 }1605 }
15291606
1530 if (params.len == 0) {1607 if (params.len == 0) {
1531 try renderToken(ais, tree, builtin_token + 1, .none); // (1608 try renderToken(r, builtin_token + 1, .none); // (
1532 return renderToken(ais, tree, builtin_token + 2, space); // )1609 return renderToken(r, builtin_token + 2, space); // )
1533 }1610 }
15341611
1535 const last_param = params[params.len - 1];1612 const last_param = params[params.len - 1];
...@@ -1537,7 +1614,7 @@ fn renderBuiltinCall(...@@ -1537,7 +1614,7 @@ fn renderBuiltinCall(
15371614
1538 if (token_tags[after_last_param_token] != .comma) {1615 if (token_tags[after_last_param_token] != .comma) {
1539 // Render all on one line, no trailing comma.1616 // Render all on one line, no trailing comma.
1540 try renderToken(ais, tree, builtin_token + 1, .none); // (1617 try renderToken(r, builtin_token + 1, .none); // (
15411618
1542 for (params, 0..) |param_node, i| {1619 for (params, 0..) |param_node, i| {
1543 const first_param_token = tree.firstToken(param_node);1620 const first_param_token = tree.firstToken(param_node);
...@@ -1546,39 +1623,41 @@ fn renderBuiltinCall(...@@ -1546,39 +1623,41 @@ fn renderBuiltinCall(
1546 {1623 {
1547 ais.pushIndentOneShot();1624 ais.pushIndentOneShot();
1548 }1625 }
1549 try renderExpression(gpa, ais, tree, param_node, .none);1626 try renderExpression(r, param_node, .none);
15501627
1551 if (i + 1 < params.len) {1628 if (i + 1 < params.len) {
1552 const comma_token = tree.lastToken(param_node) + 1;1629 const comma_token = tree.lastToken(param_node) + 1;
1553 try renderToken(ais, tree, comma_token, .space); // ,1630 try renderToken(r, comma_token, .space); // ,
1554 }1631 }
1555 }1632 }
1556 return renderToken(ais, tree, after_last_param_token, space); // )1633 return renderToken(r, after_last_param_token, space); // )
1557 } else {1634 } else {
1558 // Render one param per line.1635 // Render one param per line.
1559 ais.pushIndent();1636 ais.pushIndent();
1560 try renderToken(ais, tree, builtin_token + 1, Space.newline); // (1637 try renderToken(r, builtin_token + 1, Space.newline); // (
15611638
1562 for (params) |param_node| {1639 for (params) |param_node| {
1563 try renderExpression(gpa, ais, tree, param_node, .comma);1640 try renderExpression(r, param_node, .comma);
1564 }1641 }
1565 ais.popIndent();1642 ais.popIndent();
15661643
1567 return renderToken(ais, tree, after_last_param_token + 1, space); // )1644 return renderToken(r, after_last_param_token + 1, space); // )
1568 }1645 }
1569}1646}
15701647
1571fn renderFnProto(gpa: Allocator, ais: *Ais, tree: Ast, fn_proto: Ast.full.FnProto, space: Space) Error!void {1648fn renderFnProto(r: *Render, fn_proto: Ast.full.FnProto, space: Space) Error!void {
1649 const tree = r.tree;
1650 const ais = r.ais;
1572 const token_tags = tree.tokens.items(.tag);1651 const token_tags = tree.tokens.items(.tag);
1573 const token_starts = tree.tokens.items(.start);1652 const token_starts = tree.tokens.items(.start);
15741653
1575 const after_fn_token = fn_proto.ast.fn_token + 1;1654 const after_fn_token = fn_proto.ast.fn_token + 1;
1576 const lparen = if (token_tags[after_fn_token] == .identifier) blk: {1655 const lparen = if (token_tags[after_fn_token] == .identifier) blk: {
1577 try renderToken(ais, tree, fn_proto.ast.fn_token, .space); // fn1656 try renderToken(r, fn_proto.ast.fn_token, .space); // fn
1578 try renderIdentifier(ais, tree, after_fn_token, .none, .preserve_when_shadowing); // name1657 try renderIdentifier(r, after_fn_token, .none, .preserve_when_shadowing); // name
1579 break :blk after_fn_token + 1;1658 break :blk after_fn_token + 1;
1580 } else blk: {1659 } else blk: {
1581 try renderToken(ais, tree, fn_proto.ast.fn_token, .space); // fn1660 try renderToken(r, fn_proto.ast.fn_token, .space); // fn
1582 break :blk fn_proto.ast.fn_token + 1;1661 break :blk fn_proto.ast.fn_token + 1;
1583 };1662 };
1584 assert(token_tags[lparen] == .l_paren);1663 assert(token_tags[lparen] == .l_paren);
...@@ -1630,7 +1709,7 @@ fn renderFnProto(gpa: Allocator, ais: *Ais, tree: Ast, fn_proto: Ast.full.FnProt...@@ -1630,7 +1709,7 @@ fn renderFnProto(gpa: Allocator, ais: *Ais, tree: Ast, fn_proto: Ast.full.FnProt
1630 const trailing_comma = token_tags[rparen - 1] == .comma;1709 const trailing_comma = token_tags[rparen - 1] == .comma;
1631 if (!trailing_comma and !hasComment(tree, lparen, rparen)) {1710 if (!trailing_comma and !hasComment(tree, lparen, rparen)) {
1632 // Render all on one line, no trailing comma.1711 // Render all on one line, no trailing comma.
1633 try renderToken(ais, tree, lparen, .none); // (1712 try renderToken(r, lparen, .none); // (
16341713
1635 var param_i: usize = 0;1714 var param_i: usize = 0;
1636 var last_param_token = lparen;1715 var last_param_token = lparen;
...@@ -1638,25 +1717,25 @@ fn renderFnProto(gpa: Allocator, ais: *Ais, tree: Ast, fn_proto: Ast.full.FnProt...@@ -1638,25 +1717,25 @@ fn renderFnProto(gpa: Allocator, ais: *Ais, tree: Ast, fn_proto: Ast.full.FnProt
1638 last_param_token += 1;1717 last_param_token += 1;
1639 switch (token_tags[last_param_token]) {1718 switch (token_tags[last_param_token]) {
1640 .doc_comment => {1719 .doc_comment => {
1641 try renderToken(ais, tree, last_param_token, .newline);1720 try renderToken(r, last_param_token, .newline);
1642 continue;1721 continue;
1643 },1722 },
1644 .ellipsis3 => {1723 .ellipsis3 => {
1645 try renderToken(ais, tree, last_param_token, .none); // ...1724 try renderToken(r, last_param_token, .none); // ...
1646 break;1725 break;
1647 },1726 },
1648 .keyword_noalias, .keyword_comptime => {1727 .keyword_noalias, .keyword_comptime => {
1649 try renderToken(ais, tree, last_param_token, .space);1728 try renderToken(r, last_param_token, .space);
1650 last_param_token += 1;1729 last_param_token += 1;
1651 },1730 },
1652 .identifier => {},1731 .identifier => {},
1653 .keyword_anytype => {1732 .keyword_anytype => {
1654 try renderToken(ais, tree, last_param_token, .none); // anytype1733 try renderToken(r, last_param_token, .none); // anytype
1655 continue;1734 continue;
1656 },1735 },
1657 .r_paren => break,1736 .r_paren => break,
1658 .comma => {1737 .comma => {
1659 try renderToken(ais, tree, last_param_token, .space); // ,1738 try renderToken(r, last_param_token, .space); // ,
1660 continue;1739 continue;
1661 },1740 },
1662 else => {}, // Parameter type without a name.1741 else => {}, // Parameter type without a name.
...@@ -1664,24 +1743,24 @@ fn renderFnProto(gpa: Allocator, ais: *Ais, tree: Ast, fn_proto: Ast.full.FnProt...@@ -1664,24 +1743,24 @@ fn renderFnProto(gpa: Allocator, ais: *Ais, tree: Ast, fn_proto: Ast.full.FnProt
1664 if (token_tags[last_param_token] == .identifier and1743 if (token_tags[last_param_token] == .identifier and
1665 token_tags[last_param_token + 1] == .colon)1744 token_tags[last_param_token + 1] == .colon)
1666 {1745 {
1667 try renderIdentifier(ais, tree, last_param_token, .none, .preserve_when_shadowing); // name1746 try renderIdentifier(r, last_param_token, .none, .preserve_when_shadowing); // name
1668 last_param_token += 1;1747 last_param_token += 1;
1669 try renderToken(ais, tree, last_param_token, .space); // :1748 try renderToken(r, last_param_token, .space); // :
1670 last_param_token += 1;1749 last_param_token += 1;
1671 }1750 }
1672 if (token_tags[last_param_token] == .keyword_anytype) {1751 if (token_tags[last_param_token] == .keyword_anytype) {
1673 try renderToken(ais, tree, last_param_token, .none); // anytype1752 try renderToken(r, last_param_token, .none); // anytype
1674 continue;1753 continue;
1675 }1754 }
1676 const param = fn_proto.ast.params[param_i];1755 const param = fn_proto.ast.params[param_i];
1677 param_i += 1;1756 param_i += 1;
1678 try renderExpression(gpa, ais, tree, param, .none);1757 try renderExpression(r, param, .none);
1679 last_param_token = tree.lastToken(param);1758 last_param_token = tree.lastToken(param);
1680 }1759 }
1681 } else {1760 } else {
1682 // One param per line.1761 // One param per line.
1683 ais.pushIndent();1762 ais.pushIndent();
1684 try renderToken(ais, tree, lparen, .newline); // (1763 try renderToken(r, lparen, .newline); // (
16851764
1686 var param_i: usize = 0;1765 var param_i: usize = 0;
1687 var last_param_token = lparen;1766 var last_param_token = lparen;
...@@ -1689,20 +1768,20 @@ fn renderFnProto(gpa: Allocator, ais: *Ais, tree: Ast, fn_proto: Ast.full.FnProt...@@ -1689,20 +1768,20 @@ fn renderFnProto(gpa: Allocator, ais: *Ais, tree: Ast, fn_proto: Ast.full.FnProt
1689 last_param_token += 1;1768 last_param_token += 1;
1690 switch (token_tags[last_param_token]) {1769 switch (token_tags[last_param_token]) {
1691 .doc_comment => {1770 .doc_comment => {
1692 try renderToken(ais, tree, last_param_token, .newline);1771 try renderToken(r, last_param_token, .newline);
1693 continue;1772 continue;
1694 },1773 },
1695 .ellipsis3 => {1774 .ellipsis3 => {
1696 try renderToken(ais, tree, last_param_token, .comma); // ...1775 try renderToken(r, last_param_token, .comma); // ...
1697 break;1776 break;
1698 },1777 },
1699 .keyword_noalias, .keyword_comptime => {1778 .keyword_noalias, .keyword_comptime => {
1700 try renderToken(ais, tree, last_param_token, .space);1779 try renderToken(r, last_param_token, .space);
1701 last_param_token += 1;1780 last_param_token += 1;
1702 },1781 },
1703 .identifier => {},1782 .identifier => {},
1704 .keyword_anytype => {1783 .keyword_anytype => {
1705 try renderToken(ais, tree, last_param_token, .comma); // anytype1784 try renderToken(r, last_param_token, .comma); // anytype
1706 if (token_tags[last_param_token + 1] == .comma)1785 if (token_tags[last_param_token + 1] == .comma)
1707 last_param_token += 1;1786 last_param_token += 1;
1708 continue;1787 continue;
...@@ -1713,56 +1792,56 @@ fn renderFnProto(gpa: Allocator, ais: *Ais, tree: Ast, fn_proto: Ast.full.FnProt...@@ -1713,56 +1792,56 @@ fn renderFnProto(gpa: Allocator, ais: *Ais, tree: Ast, fn_proto: Ast.full.FnProt
1713 if (token_tags[last_param_token] == .identifier and1792 if (token_tags[last_param_token] == .identifier and
1714 token_tags[last_param_token + 1] == .colon)1793 token_tags[last_param_token + 1] == .colon)
1715 {1794 {
1716 try renderIdentifier(ais, tree, last_param_token, .none, .preserve_when_shadowing); // name1795 try renderIdentifier(r, last_param_token, .none, .preserve_when_shadowing); // name
1717 last_param_token += 1;1796 last_param_token += 1;
1718 try renderToken(ais, tree, last_param_token, .space); // :1797 try renderToken(r, last_param_token, .space); // :
1719 last_param_token += 1;1798 last_param_token += 1;
1720 }1799 }
1721 if (token_tags[last_param_token] == .keyword_anytype) {1800 if (token_tags[last_param_token] == .keyword_anytype) {
1722 try renderToken(ais, tree, last_param_token, .comma); // anytype1801 try renderToken(r, last_param_token, .comma); // anytype
1723 if (token_tags[last_param_token + 1] == .comma)1802 if (token_tags[last_param_token + 1] == .comma)
1724 last_param_token += 1;1803 last_param_token += 1;
1725 continue;1804 continue;
1726 }1805 }
1727 const param = fn_proto.ast.params[param_i];1806 const param = fn_proto.ast.params[param_i];
1728 param_i += 1;1807 param_i += 1;
1729 try renderExpression(gpa, ais, tree, param, .comma);1808 try renderExpression(r, param, .comma);
1730 last_param_token = tree.lastToken(param);1809 last_param_token = tree.lastToken(param);
1731 if (token_tags[last_param_token + 1] == .comma) last_param_token += 1;1810 if (token_tags[last_param_token + 1] == .comma) last_param_token += 1;
1732 }1811 }
1733 ais.popIndent();1812 ais.popIndent();
1734 }1813 }
17351814
1736 try renderToken(ais, tree, rparen, .space); // )1815 try renderToken(r, rparen, .space); // )
17371816
1738 if (fn_proto.ast.align_expr != 0) {1817 if (fn_proto.ast.align_expr != 0) {
1739 const align_lparen = tree.firstToken(fn_proto.ast.align_expr) - 1;1818 const align_lparen = tree.firstToken(fn_proto.ast.align_expr) - 1;
1740 const align_rparen = tree.lastToken(fn_proto.ast.align_expr) + 1;1819 const align_rparen = tree.lastToken(fn_proto.ast.align_expr) + 1;
17411820
1742 try renderToken(ais, tree, align_lparen - 1, .none); // align1821 try renderToken(r, align_lparen - 1, .none); // align
1743 try renderToken(ais, tree, align_lparen, .none); // (1822 try renderToken(r, align_lparen, .none); // (
1744 try renderExpression(gpa, ais, tree, fn_proto.ast.align_expr, .none);1823 try renderExpression(r, fn_proto.ast.align_expr, .none);
1745 try renderToken(ais, tree, align_rparen, .space); // )1824 try renderToken(r, align_rparen, .space); // )
1746 }1825 }
17471826
1748 if (fn_proto.ast.addrspace_expr != 0) {1827 if (fn_proto.ast.addrspace_expr != 0) {
1749 const align_lparen = tree.firstToken(fn_proto.ast.addrspace_expr) - 1;1828 const align_lparen = tree.firstToken(fn_proto.ast.addrspace_expr) - 1;
1750 const align_rparen = tree.lastToken(fn_proto.ast.addrspace_expr) + 1;1829 const align_rparen = tree.lastToken(fn_proto.ast.addrspace_expr) + 1;
17511830
1752 try renderToken(ais, tree, align_lparen - 1, .none); // addrspace1831 try renderToken(r, align_lparen - 1, .none); // addrspace
1753 try renderToken(ais, tree, align_lparen, .none); // (1832 try renderToken(r, align_lparen, .none); // (
1754 try renderExpression(gpa, ais, tree, fn_proto.ast.addrspace_expr, .none);1833 try renderExpression(r, fn_proto.ast.addrspace_expr, .none);
1755 try renderToken(ais, tree, align_rparen, .space); // )1834 try renderToken(r, align_rparen, .space); // )
1756 }1835 }
17571836
1758 if (fn_proto.ast.section_expr != 0) {1837 if (fn_proto.ast.section_expr != 0) {
1759 const section_lparen = tree.firstToken(fn_proto.ast.section_expr) - 1;1838 const section_lparen = tree.firstToken(fn_proto.ast.section_expr) - 1;
1760 const section_rparen = tree.lastToken(fn_proto.ast.section_expr) + 1;1839 const section_rparen = tree.lastToken(fn_proto.ast.section_expr) + 1;
17611840
1762 try renderToken(ais, tree, section_lparen - 1, .none); // section1841 try renderToken(r, section_lparen - 1, .none); // section
1763 try renderToken(ais, tree, section_lparen, .none); // (1842 try renderToken(r, section_lparen, .none); // (
1764 try renderExpression(gpa, ais, tree, fn_proto.ast.section_expr, .none);1843 try renderExpression(r, fn_proto.ast.section_expr, .none);
1765 try renderToken(ais, tree, section_rparen, .space); // )1844 try renderToken(r, section_rparen, .space); // )
1766 }1845 }
17671846
1768 const is_callconv_inline = mem.eql(u8, "Inline", tree.tokenSlice(tree.nodes.items(.main_token)[fn_proto.ast.callconv_expr]));1847 const is_callconv_inline = mem.eql(u8, "Inline", tree.tokenSlice(tree.nodes.items(.main_token)[fn_proto.ast.callconv_expr]));
...@@ -1771,25 +1850,24 @@ fn renderFnProto(gpa: Allocator, ais: *Ais, tree: Ast, fn_proto: Ast.full.FnProt...@@ -1771,25 +1850,24 @@ fn renderFnProto(gpa: Allocator, ais: *Ais, tree: Ast, fn_proto: Ast.full.FnProt
1771 const callconv_lparen = tree.firstToken(fn_proto.ast.callconv_expr) - 1;1850 const callconv_lparen = tree.firstToken(fn_proto.ast.callconv_expr) - 1;
1772 const callconv_rparen = tree.lastToken(fn_proto.ast.callconv_expr) + 1;1851 const callconv_rparen = tree.lastToken(fn_proto.ast.callconv_expr) + 1;
17731852
1774 try renderToken(ais, tree, callconv_lparen - 1, .none); // callconv1853 try renderToken(r, callconv_lparen - 1, .none); // callconv
1775 try renderToken(ais, tree, callconv_lparen, .none); // (1854 try renderToken(r, callconv_lparen, .none); // (
1776 try renderExpression(gpa, ais, tree, fn_proto.ast.callconv_expr, .none);1855 try renderExpression(r, fn_proto.ast.callconv_expr, .none);
1777 try renderToken(ais, tree, callconv_rparen, .space); // )1856 try renderToken(r, callconv_rparen, .space); // )
1778 }1857 }
17791858
1780 if (token_tags[maybe_bang] == .bang) {1859 if (token_tags[maybe_bang] == .bang) {
1781 try renderToken(ais, tree, maybe_bang, .none); // !1860 try renderToken(r, maybe_bang, .none); // !
1782 }1861 }
1783 return renderExpression(gpa, ais, tree, fn_proto.ast.return_type, space);1862 return renderExpression(r, fn_proto.ast.return_type, space);
1784}1863}
17851864
1786fn renderSwitchCase(1865fn renderSwitchCase(
1787 gpa: Allocator,1866 r: *Render,
1788 ais: *Ais,
1789 tree: Ast,
1790 switch_case: Ast.full.SwitchCase,1867 switch_case: Ast.full.SwitchCase,
1791 space: Space,1868 space: Space,
1792) Error!void {1869) Error!void {
1870 const tree = r.tree;
1793 const node_tags = tree.nodes.items(.tag);1871 const node_tags = tree.nodes.items(.tag);
1794 const token_tags = tree.tokens.items(.tag);1872 const token_tags = tree.tokens.items(.tag);
1795 const trailing_comma = token_tags[switch_case.ast.arrow_token - 1] == .comma;1873 const trailing_comma = token_tags[switch_case.ast.arrow_token - 1] == .comma;
...@@ -1800,22 +1878,22 @@ fn renderSwitchCase(...@@ -1800,22 +1878,22 @@ fn renderSwitchCase(
18001878
1801 // render inline keyword1879 // render inline keyword
1802 if (switch_case.inline_token) |some| {1880 if (switch_case.inline_token) |some| {
1803 try renderToken(ais, tree, some, .space);1881 try renderToken(r, some, .space);
1804 }1882 }
18051883
1806 // Render everything before the arrow1884 // Render everything before the arrow
1807 if (switch_case.ast.values.len == 0) {1885 if (switch_case.ast.values.len == 0) {
1808 try renderToken(ais, tree, switch_case.ast.arrow_token - 1, .space); // else keyword1886 try renderToken(r, switch_case.ast.arrow_token - 1, .space); // else keyword
1809 } else if (switch_case.ast.values.len == 1 and !has_comment_before_arrow) {1887 } else if (switch_case.ast.values.len == 1 and !has_comment_before_arrow) {
1810 // render on one line and drop the trailing comma if any1888 // render on one line and drop the trailing comma if any
1811 try renderExpression(gpa, ais, tree, switch_case.ast.values[0], .space);1889 try renderExpression(r, switch_case.ast.values[0], .space);
1812 } else if (trailing_comma or has_comment_before_arrow) {1890 } else if (trailing_comma or has_comment_before_arrow) {
1813 // Render each value on a new line1891 // Render each value on a new line
1814 try renderExpressions(gpa, ais, tree, switch_case.ast.values, .comma);1892 try renderExpressions(r, switch_case.ast.values, .comma);
1815 } else {1893 } else {
1816 // Render on one line1894 // Render on one line
1817 for (switch_case.ast.values) |value_expr| {1895 for (switch_case.ast.values) |value_expr| {
1818 try renderExpression(gpa, ais, tree, value_expr, .comma_space);1896 try renderExpression(r, value_expr, .comma_space);
1819 }1897 }
1820 }1898 }
18211899
...@@ -1826,35 +1904,35 @@ fn renderSwitchCase(...@@ -1826,35 +1904,35 @@ fn renderSwitchCase(
1826 else1904 else
1827 Space.space;1905 Space.space;
1828 const after_arrow_space: Space = if (switch_case.payload_token == null) pre_target_space else .space;1906 const after_arrow_space: Space = if (switch_case.payload_token == null) pre_target_space else .space;
1829 try renderToken(ais, tree, switch_case.ast.arrow_token, after_arrow_space); // =>1907 try renderToken(r, switch_case.ast.arrow_token, after_arrow_space); // =>
18301908
1831 if (switch_case.payload_token) |payload_token| {1909 if (switch_case.payload_token) |payload_token| {
1832 try renderToken(ais, tree, payload_token - 1, .none); // pipe1910 try renderToken(r, payload_token - 1, .none); // pipe
1833 const ident = payload_token + @intFromBool(token_tags[payload_token] == .asterisk);1911 const ident = payload_token + @intFromBool(token_tags[payload_token] == .asterisk);
1834 if (token_tags[payload_token] == .asterisk) {1912 if (token_tags[payload_token] == .asterisk) {
1835 try renderToken(ais, tree, payload_token, .none); // asterisk1913 try renderToken(r, payload_token, .none); // asterisk
1836 }1914 }
1837 try renderIdentifier(ais, tree, ident, .none, .preserve_when_shadowing); // identifier1915 try renderIdentifier(r, ident, .none, .preserve_when_shadowing); // identifier
1838 if (token_tags[ident + 1] == .comma) {1916 if (token_tags[ident + 1] == .comma) {
1839 try renderToken(ais, tree, ident + 1, .space); // ,1917 try renderToken(r, ident + 1, .space); // ,
1840 try renderIdentifier(ais, tree, ident + 2, .none, .preserve_when_shadowing); // identifier1918 try renderIdentifier(r, ident + 2, .none, .preserve_when_shadowing); // identifier
1841 try renderToken(ais, tree, ident + 3, pre_target_space); // pipe1919 try renderToken(r, ident + 3, pre_target_space); // pipe
1842 } else {1920 } else {
1843 try renderToken(ais, tree, ident + 1, pre_target_space); // pipe1921 try renderToken(r, ident + 1, pre_target_space); // pipe
1844 }1922 }
1845 }1923 }
18461924
1847 try renderExpression(gpa, ais, tree, switch_case.ast.target_expr, space);1925 try renderExpression(r, switch_case.ast.target_expr, space);
1848}1926}
18491927
1850fn renderBlock(1928fn renderBlock(
1851 gpa: Allocator,1929 r: *Render,
1852 ais: *Ais,
1853 tree: Ast,
1854 block_node: Ast.Node.Index,1930 block_node: Ast.Node.Index,
1855 statements: []const Ast.Node.Index,1931 statements: []const Ast.Node.Index,
1856 space: Space,1932 space: Space,
1857) Error!void {1933) Error!void {
1934 const tree = r.tree;
1935 const ais = r.ais;
1858 const token_tags = tree.tokens.items(.tag);1936 const token_tags = tree.tokens.items(.tag);
1859 const node_tags = tree.nodes.items(.tag);1937 const node_tags = tree.nodes.items(.tag);
1860 const lbrace = tree.nodes.items(.main_token)[block_node];1938 const lbrace = tree.nodes.items(.main_token)[block_node];
...@@ -1862,51 +1940,51 @@ fn renderBlock(...@@ -1862,51 +1940,51 @@ fn renderBlock(
1862 if (token_tags[lbrace - 1] == .colon and1940 if (token_tags[lbrace - 1] == .colon and
1863 token_tags[lbrace - 2] == .identifier)1941 token_tags[lbrace - 2] == .identifier)
1864 {1942 {
1865 try renderIdentifier(ais, tree, lbrace - 2, .none, .eagerly_unquote); // identifier1943 try renderIdentifier(r, lbrace - 2, .none, .eagerly_unquote); // identifier
1866 try renderToken(ais, tree, lbrace - 1, .space); // :1944 try renderToken(r, lbrace - 1, .space); // :
1867 }1945 }
18681946
1869 ais.pushIndentNextLine();1947 ais.pushIndentNextLine();
1870 if (statements.len == 0) {1948 if (statements.len == 0) {
1871 try renderToken(ais, tree, lbrace, .none);1949 try renderToken(r, lbrace, .none);
1872 } else {1950 } else {
1873 try renderToken(ais, tree, lbrace, .newline);1951 try renderToken(r, lbrace, .newline);
1874 for (statements, 0..) |stmt, i| {1952 for (statements, 0..) |stmt, i| {
1875 if (i != 0) try renderExtraNewline(ais, tree, stmt);1953 if (i != 0) try renderExtraNewline(r, stmt);
1876 switch (node_tags[stmt]) {1954 switch (node_tags[stmt]) {
1877 .global_var_decl,1955 .global_var_decl,
1878 .local_var_decl,1956 .local_var_decl,
1879 .simple_var_decl,1957 .simple_var_decl,
1880 .aligned_var_decl,1958 .aligned_var_decl,
1881 => try renderVarDecl(gpa, ais, tree, tree.fullVarDecl(stmt).?, false, .semicolon),1959 => try renderVarDecl(r, tree.fullVarDecl(stmt).?, false, .semicolon),
1882 else => try renderExpression(gpa, ais, tree, stmt, .semicolon),1960 else => try renderExpression(r, stmt, .semicolon),
1883 }1961 }
1884 }1962 }
1885 }1963 }
1886 ais.popIndent();1964 ais.popIndent();
18871965
1888 try renderToken(ais, tree, tree.lastToken(block_node), space); // rbrace1966 try renderToken(r, tree.lastToken(block_node), space); // rbrace
1889}1967}
18901968
1891fn renderStructInit(1969fn renderStructInit(
1892 gpa: Allocator,1970 r: *Render,
1893 ais: *Ais,
1894 tree: Ast,
1895 struct_node: Ast.Node.Index,1971 struct_node: Ast.Node.Index,
1896 struct_init: Ast.full.StructInit,1972 struct_init: Ast.full.StructInit,
1897 space: Space,1973 space: Space,
1898) Error!void {1974) Error!void {
1975 const tree = r.tree;
1976 const ais = r.ais;
1899 const token_tags = tree.tokens.items(.tag);1977 const token_tags = tree.tokens.items(.tag);
1900 if (struct_init.ast.type_expr == 0) {1978 if (struct_init.ast.type_expr == 0) {
1901 try renderToken(ais, tree, struct_init.ast.lbrace - 1, .none); // .1979 try renderToken(r, struct_init.ast.lbrace - 1, .none); // .
1902 } else {1980 } else {
1903 try renderExpression(gpa, ais, tree, struct_init.ast.type_expr, .none); // T1981 try renderExpression(r, struct_init.ast.type_expr, .none); // T
1904 }1982 }
1905 if (struct_init.ast.fields.len == 0) {1983 if (struct_init.ast.fields.len == 0) {
1906 ais.pushIndentNextLine();1984 ais.pushIndentNextLine();
1907 try renderToken(ais, tree, struct_init.ast.lbrace, .none); // lbrace1985 try renderToken(r, struct_init.ast.lbrace, .none); // lbrace
1908 ais.popIndent();1986 ais.popIndent();
1909 return renderToken(ais, tree, struct_init.ast.lbrace + 1, space); // rbrace1987 return renderToken(r, struct_init.ast.lbrace + 1, space); // rbrace
1910 }1988 }
19111989
1912 const rbrace = tree.lastToken(struct_node);1990 const rbrace = tree.lastToken(struct_node);
...@@ -1914,65 +1992,66 @@ fn renderStructInit(...@@ -1914,65 +1992,66 @@ fn renderStructInit(
1914 if (trailing_comma or hasComment(tree, struct_init.ast.lbrace, rbrace)) {1992 if (trailing_comma or hasComment(tree, struct_init.ast.lbrace, rbrace)) {
1915 // Render one field init per line.1993 // Render one field init per line.
1916 ais.pushIndentNextLine();1994 ais.pushIndentNextLine();
1917 try renderToken(ais, tree, struct_init.ast.lbrace, .newline);1995 try renderToken(r, struct_init.ast.lbrace, .newline);
19181996
1919 try renderToken(ais, tree, struct_init.ast.lbrace + 1, .none); // .1997 try renderToken(r, struct_init.ast.lbrace + 1, .none); // .
1920 try renderIdentifier(ais, tree, struct_init.ast.lbrace + 2, .space, .eagerly_unquote); // name1998 try renderIdentifier(r, struct_init.ast.lbrace + 2, .space, .eagerly_unquote); // name
1921 // Don't output a space after the = if expression is a multiline string,1999 // Don't output a space after the = if expression is a multiline string,
1922 // since then it will start on the next line.2000 // since then it will start on the next line.
1923 const nodes = tree.nodes.items(.tag);2001 const nodes = tree.nodes.items(.tag);
1924 const expr = nodes[struct_init.ast.fields[0]];2002 const expr = nodes[struct_init.ast.fields[0]];
1925 var space_after_equal: Space = if (expr == .multiline_string_literal) .none else .space;2003 var space_after_equal: Space = if (expr == .multiline_string_literal) .none else .space;
1926 try renderToken(ais, tree, struct_init.ast.lbrace + 3, space_after_equal); // =2004 try renderToken(r, struct_init.ast.lbrace + 3, space_after_equal); // =
1927 try renderExpression(gpa, ais, tree, struct_init.ast.fields[0], .comma);2005 try renderExpression(r, struct_init.ast.fields[0], .comma);
19282006
1929 for (struct_init.ast.fields[1..]) |field_init| {2007 for (struct_init.ast.fields[1..]) |field_init| {
1930 const init_token = tree.firstToken(field_init);2008 const init_token = tree.firstToken(field_init);
1931 try renderExtraNewlineToken(ais, tree, init_token - 3);2009 try renderExtraNewlineToken(r, init_token - 3);
1932 try renderToken(ais, tree, init_token - 3, .none); // .2010 try renderToken(r, init_token - 3, .none); // .
1933 try renderIdentifier(ais, tree, init_token - 2, .space, .eagerly_unquote); // name2011 try renderIdentifier(r, init_token - 2, .space, .eagerly_unquote); // name
1934 space_after_equal = if (nodes[field_init] == .multiline_string_literal) .none else .space;2012 space_after_equal = if (nodes[field_init] == .multiline_string_literal) .none else .space;
1935 try renderToken(ais, tree, init_token - 1, space_after_equal); // =2013 try renderToken(r, init_token - 1, space_after_equal); // =
1936 try renderExpression(gpa, ais, tree, field_init, .comma);2014 try renderExpression(r, field_init, .comma);
1937 }2015 }
19382016
1939 ais.popIndent();2017 ais.popIndent();
1940 } else {2018 } else {
1941 // Render all on one line, no trailing comma.2019 // Render all on one line, no trailing comma.
1942 try renderToken(ais, tree, struct_init.ast.lbrace, .space);2020 try renderToken(r, struct_init.ast.lbrace, .space);
19432021
1944 for (struct_init.ast.fields) |field_init| {2022 for (struct_init.ast.fields) |field_init| {
1945 const init_token = tree.firstToken(field_init);2023 const init_token = tree.firstToken(field_init);
1946 try renderToken(ais, tree, init_token - 3, .none); // .2024 try renderToken(r, init_token - 3, .none); // .
1947 try renderIdentifier(ais, tree, init_token - 2, .space, .eagerly_unquote); // name2025 try renderIdentifier(r, init_token - 2, .space, .eagerly_unquote); // name
1948 try renderToken(ais, tree, init_token - 1, .space); // =2026 try renderToken(r, init_token - 1, .space); // =
1949 try renderExpression(gpa, ais, tree, field_init, .comma_space);2027 try renderExpression(r, field_init, .comma_space);
1950 }2028 }
1951 }2029 }
19522030
1953 return renderToken(ais, tree, rbrace, space);2031 return renderToken(r, rbrace, space);
1954}2032}
19552033
1956fn renderArrayInit(2034fn renderArrayInit(
1957 gpa: Allocator,2035 r: *Render,
1958 ais: *Ais,
1959 tree: Ast,
1960 array_init: Ast.full.ArrayInit,2036 array_init: Ast.full.ArrayInit,
1961 space: Space,2037 space: Space,
1962) Error!void {2038) Error!void {
2039 const tree = r.tree;
2040 const ais = r.ais;
2041 const gpa = r.gpa;
1963 const token_tags = tree.tokens.items(.tag);2042 const token_tags = tree.tokens.items(.tag);
19642043
1965 if (array_init.ast.type_expr == 0) {2044 if (array_init.ast.type_expr == 0) {
1966 try renderToken(ais, tree, array_init.ast.lbrace - 1, .none); // .2045 try renderToken(r, array_init.ast.lbrace - 1, .none); // .
1967 } else {2046 } else {
1968 try renderExpression(gpa, ais, tree, array_init.ast.type_expr, .none); // T2047 try renderExpression(r, array_init.ast.type_expr, .none); // T
1969 }2048 }
19702049
1971 if (array_init.ast.elements.len == 0) {2050 if (array_init.ast.elements.len == 0) {
1972 ais.pushIndentNextLine();2051 ais.pushIndentNextLine();
1973 try renderToken(ais, tree, array_init.ast.lbrace, .none); // lbrace2052 try renderToken(r, array_init.ast.lbrace, .none); // lbrace
1974 ais.popIndent();2053 ais.popIndent();
1975 return renderToken(ais, tree, array_init.ast.lbrace + 1, space); // rbrace2054 return renderToken(r, array_init.ast.lbrace + 1, space); // rbrace
1976 }2055 }
19772056
1978 const last_elem = array_init.ast.elements[array_init.ast.elements.len - 1];2057 const last_elem = array_init.ast.elements[array_init.ast.elements.len - 1];
...@@ -1987,9 +2066,9 @@ fn renderArrayInit(...@@ -1987,9 +2066,9 @@ fn renderArrayInit(
1987 if (token_tags[first_token] != .multiline_string_literal_line and2066 if (token_tags[first_token] != .multiline_string_literal_line and
1988 !anythingBetween(tree, last_elem_token, rbrace))2067 !anythingBetween(tree, last_elem_token, rbrace))
1989 {2068 {
1990 try renderToken(ais, tree, array_init.ast.lbrace, .none);2069 try renderToken(r, array_init.ast.lbrace, .none);
1991 try renderExpression(gpa, ais, tree, only_elem, .none);2070 try renderExpression(r, only_elem, .none);
1992 return renderToken(ais, tree, rbrace, space);2071 return renderToken(r, rbrace, space);
1993 }2072 }
1994 }2073 }
19952074
...@@ -2000,19 +2079,19 @@ fn renderArrayInit(...@@ -2000,19 +2079,19 @@ fn renderArrayInit(
2000 // Render all on one line, no trailing comma.2079 // Render all on one line, no trailing comma.
2001 if (array_init.ast.elements.len == 1) {2080 if (array_init.ast.elements.len == 1) {
2002 // If there is only one element, we don't use spaces2081 // If there is only one element, we don't use spaces
2003 try renderToken(ais, tree, array_init.ast.lbrace, .none);2082 try renderToken(r, array_init.ast.lbrace, .none);
2004 try renderExpression(gpa, ais, tree, array_init.ast.elements[0], .none);2083 try renderExpression(r, array_init.ast.elements[0], .none);
2005 } else {2084 } else {
2006 try renderToken(ais, tree, array_init.ast.lbrace, .space);2085 try renderToken(r, array_init.ast.lbrace, .space);
2007 for (array_init.ast.elements) |elem| {2086 for (array_init.ast.elements) |elem| {
2008 try renderExpression(gpa, ais, tree, elem, .comma_space);2087 try renderExpression(r, elem, .comma_space);
2009 }2088 }
2010 }2089 }
2011 return renderToken(ais, tree, last_elem_token + 1, space); // rbrace2090 return renderToken(r, last_elem_token + 1, space); // rbrace
2012 }2091 }
20132092
2014 ais.pushIndentNextLine();2093 ais.pushIndentNextLine();
2015 try renderToken(ais, tree, array_init.ast.lbrace, .newline);2094 try renderToken(r, array_init.ast.lbrace, .newline);
20162095
2017 var expr_index: usize = 0;2096 var expr_index: usize = 0;
2018 while (true) {2097 while (true) {
...@@ -2068,6 +2147,12 @@ fn renderArrayInit(...@@ -2068,6 +2147,12 @@ fn renderArrayInit(
2068 .indent_delta = indent_delta,2147 .indent_delta = indent_delta,
2069 .underlying_writer = sub_expr_buffer.writer(),2148 .underlying_writer = sub_expr_buffer.writer(),
2070 };2149 };
2150 var sub_render: Render = .{
2151 .gpa = r.gpa,
2152 .ais = &auto_indenting_stream,
2153 .tree = r.tree,
2154 .fixups = r.fixups,
2155 };
20712156
2072 // Calculate size of columns in current section2157 // Calculate size of columns in current section
2073 var column_counter: usize = 0;2158 var column_counter: usize = 0;
...@@ -2078,7 +2163,7 @@ fn renderArrayInit(...@@ -2078,7 +2163,7 @@ fn renderArrayInit(
2078 sub_expr_buffer_starts[i] = start;2163 sub_expr_buffer_starts[i] = start;
20792164
2080 if (i + 1 < section_exprs.len) {2165 if (i + 1 < section_exprs.len) {
2081 try renderExpression(gpa, &auto_indenting_stream, tree, expr, .none);2166 try renderExpression(&sub_render, expr, .none);
2082 const width = sub_expr_buffer.items.len - start;2167 const width = sub_expr_buffer.items.len - start;
2083 const this_contains_newline = mem.indexOfScalar(u8, sub_expr_buffer.items[start..], '\n') != null;2168 const this_contains_newline = mem.indexOfScalar(u8, sub_expr_buffer.items[start..], '\n') != null;
2084 contains_newline = contains_newline or this_contains_newline;2169 contains_newline = contains_newline or this_contains_newline;
...@@ -2098,7 +2183,7 @@ fn renderArrayInit(...@@ -2098,7 +2183,7 @@ fn renderArrayInit(
2098 column_counter = 0;2183 column_counter = 0;
2099 }2184 }
2100 } else {2185 } else {
2101 try renderExpression(gpa, &auto_indenting_stream, tree, expr, .comma);2186 try renderExpression(&sub_render, expr, .comma);
2102 const width = sub_expr_buffer.items.len - start - 2;2187 const width = sub_expr_buffer.items.len - start - 2;
2103 const this_contains_newline = mem.indexOfScalar(u8, sub_expr_buffer.items[start .. sub_expr_buffer.items.len - 1], '\n') != null;2188 const this_contains_newline = mem.indexOfScalar(u8, sub_expr_buffer.items[start .. sub_expr_buffer.items.len - 1], '\n') != null;
2104 contains_newline = contains_newline or this_contains_newline;2189 contains_newline = contains_newline or this_contains_newline;
...@@ -2143,7 +2228,7 @@ fn renderArrayInit(...@@ -2143,7 +2228,7 @@ fn renderArrayInit(
2143 if (column_counter != row_size - 1) {2228 if (column_counter != row_size - 1) {
2144 if (!expr_newlines[i] and !expr_newlines[i + 1]) {2229 if (!expr_newlines[i] and !expr_newlines[i + 1]) {
2145 // Neither the current or next expression is multiline2230 // Neither the current or next expression is multiline
2146 try renderToken(ais, tree, comma, .space); // ,2231 try renderToken(r, comma, .space); // ,
2147 assert(column_widths[column_counter % row_size] >= expr_widths[i]);2232 assert(column_widths[column_counter % row_size] >= expr_widths[i]);
2148 const padding = column_widths[column_counter % row_size] - expr_widths[i];2233 const padding = column_widths[column_counter % row_size] - expr_widths[i];
2149 try ais.writer().writeByteNTimes(' ', padding);2234 try ais.writer().writeByteNTimes(' ', padding);
...@@ -2154,13 +2239,13 @@ fn renderArrayInit(...@@ -2154,13 +2239,13 @@ fn renderArrayInit(
2154 }2239 }
21552240
2156 if (single_line and row_size != 1) {2241 if (single_line and row_size != 1) {
2157 try renderToken(ais, tree, comma, .space); // ,2242 try renderToken(r, comma, .space); // ,
2158 continue;2243 continue;
2159 }2244 }
21602245
2161 column_counter = 0;2246 column_counter = 0;
2162 try renderToken(ais, tree, comma, .newline); // ,2247 try renderToken(r, comma, .newline); // ,
2163 try renderExtraNewline(ais, tree, next_expr);2248 try renderExtraNewline(r, next_expr);
2164 }2249 }
2165 }2250 }
21662251
...@@ -2169,21 +2254,21 @@ fn renderArrayInit(...@@ -2169,21 +2254,21 @@ fn renderArrayInit(
2169 }2254 }
21702255
2171 ais.popIndent();2256 ais.popIndent();
2172 return renderToken(ais, tree, rbrace, space); // rbrace2257 return renderToken(r, rbrace, space); // rbrace
2173}2258}
21742259
2175fn renderContainerDecl(2260fn renderContainerDecl(
2176 gpa: Allocator,2261 r: *Render,
2177 ais: *Ais,
2178 tree: Ast,
2179 container_decl_node: Ast.Node.Index,2262 container_decl_node: Ast.Node.Index,
2180 container_decl: Ast.full.ContainerDecl,2263 container_decl: Ast.full.ContainerDecl,
2181 space: Space,2264 space: Space,
2182) Error!void {2265) Error!void {
2266 const tree = r.tree;
2267 const ais = r.ais;
2183 const token_tags = tree.tokens.items(.tag);2268 const token_tags = tree.tokens.items(.tag);
21842269
2185 if (container_decl.layout_token) |layout_token| {2270 if (container_decl.layout_token) |layout_token| {
2186 try renderToken(ais, tree, layout_token, .space);2271 try renderToken(r, layout_token, .space);
2187 }2272 }
21882273
2189 const container: Container = switch (token_tags[container_decl.ast.main_token]) {2274 const container: Container = switch (token_tags[container_decl.ast.main_token]) {
...@@ -2196,29 +2281,29 @@ fn renderContainerDecl(...@@ -2196,29 +2281,29 @@ fn renderContainerDecl(
21962281
2197 var lbrace: Ast.TokenIndex = undefined;2282 var lbrace: Ast.TokenIndex = undefined;
2198 if (container_decl.ast.enum_token) |enum_token| {2283 if (container_decl.ast.enum_token) |enum_token| {
2199 try renderToken(ais, tree, container_decl.ast.main_token, .none); // union2284 try renderToken(r, container_decl.ast.main_token, .none); // union
2200 try renderToken(ais, tree, enum_token - 1, .none); // lparen2285 try renderToken(r, enum_token - 1, .none); // lparen
2201 try renderToken(ais, tree, enum_token, .none); // enum2286 try renderToken(r, enum_token, .none); // enum
2202 if (container_decl.ast.arg != 0) {2287 if (container_decl.ast.arg != 0) {
2203 try renderToken(ais, tree, enum_token + 1, .none); // lparen2288 try renderToken(r, enum_token + 1, .none); // lparen
2204 try renderExpression(gpa, ais, tree, container_decl.ast.arg, .none);2289 try renderExpression(r, container_decl.ast.arg, .none);
2205 const rparen = tree.lastToken(container_decl.ast.arg) + 1;2290 const rparen = tree.lastToken(container_decl.ast.arg) + 1;
2206 try renderToken(ais, tree, rparen, .none); // rparen2291 try renderToken(r, rparen, .none); // rparen
2207 try renderToken(ais, tree, rparen + 1, .space); // rparen2292 try renderToken(r, rparen + 1, .space); // rparen
2208 lbrace = rparen + 2;2293 lbrace = rparen + 2;
2209 } else {2294 } else {
2210 try renderToken(ais, tree, enum_token + 1, .space); // rparen2295 try renderToken(r, enum_token + 1, .space); // rparen
2211 lbrace = enum_token + 2;2296 lbrace = enum_token + 2;
2212 }2297 }
2213 } else if (container_decl.ast.arg != 0) {2298 } else if (container_decl.ast.arg != 0) {
2214 try renderToken(ais, tree, container_decl.ast.main_token, .none); // union2299 try renderToken(r, container_decl.ast.main_token, .none); // union
2215 try renderToken(ais, tree, container_decl.ast.main_token + 1, .none); // lparen2300 try renderToken(r, container_decl.ast.main_token + 1, .none); // lparen
2216 try renderExpression(gpa, ais, tree, container_decl.ast.arg, .none);2301 try renderExpression(r, container_decl.ast.arg, .none);
2217 const rparen = tree.lastToken(container_decl.ast.arg) + 1;2302 const rparen = tree.lastToken(container_decl.ast.arg) + 1;
2218 try renderToken(ais, tree, rparen, .space); // rparen2303 try renderToken(r, rparen, .space); // rparen
2219 lbrace = rparen + 1;2304 lbrace = rparen + 1;
2220 } else {2305 } else {
2221 try renderToken(ais, tree, container_decl.ast.main_token, .space); // union2306 try renderToken(r, container_decl.ast.main_token, .space); // union
2222 lbrace = container_decl.ast.main_token + 1;2307 lbrace = container_decl.ast.main_token + 1;
2223 }2308 }
22242309
...@@ -2226,13 +2311,13 @@ fn renderContainerDecl(...@@ -2226,13 +2311,13 @@ fn renderContainerDecl(
2226 if (container_decl.ast.members.len == 0) {2311 if (container_decl.ast.members.len == 0) {
2227 ais.pushIndentNextLine();2312 ais.pushIndentNextLine();
2228 if (token_tags[lbrace + 1] == .container_doc_comment) {2313 if (token_tags[lbrace + 1] == .container_doc_comment) {
2229 try renderToken(ais, tree, lbrace, .newline); // lbrace2314 try renderToken(r, lbrace, .newline); // lbrace
2230 try renderContainerDocComments(ais, tree, lbrace + 1);2315 try renderContainerDocComments(r, lbrace + 1);
2231 } else {2316 } else {
2232 try renderToken(ais, tree, lbrace, .none); // lbrace2317 try renderToken(r, lbrace, .none); // lbrace
2233 }2318 }
2234 ais.popIndent();2319 ais.popIndent();
2235 return renderToken(ais, tree, rbrace, space); // rbrace2320 return renderToken(r, rbrace, space); // rbrace
2236 }2321 }
22372322
2238 const src_has_trailing_comma = token_tags[rbrace - 1] == .comma;2323 const src_has_trailing_comma = token_tags[rbrace - 1] == .comma;
...@@ -2258,52 +2343,52 @@ fn renderContainerDecl(...@@ -2258,52 +2343,52 @@ fn renderContainerDecl(
2258 }2343 }
22592344
2260 // Print all the declarations on the same line.2345 // Print all the declarations on the same line.
2261 try renderToken(ais, tree, lbrace, .space); // lbrace2346 try renderToken(r, lbrace, .space); // lbrace
2262 for (container_decl.ast.members) |member| {2347 for (container_decl.ast.members) |member| {
2263 try renderMember(gpa, ais, tree, container, member, .space);2348 try renderMember(r, container, member, .space);
2264 }2349 }
2265 return renderToken(ais, tree, rbrace, space); // rbrace2350 return renderToken(r, rbrace, space); // rbrace
2266 }2351 }
22672352
2268 // One member per line.2353 // One member per line.
2269 ais.pushIndentNextLine();2354 ais.pushIndentNextLine();
2270 try renderToken(ais, tree, lbrace, .newline); // lbrace2355 try renderToken(r, lbrace, .newline); // lbrace
2271 if (token_tags[lbrace + 1] == .container_doc_comment) {2356 if (token_tags[lbrace + 1] == .container_doc_comment) {
2272 try renderContainerDocComments(ais, tree, lbrace + 1);2357 try renderContainerDocComments(r, lbrace + 1);
2273 }2358 }
2274 for (container_decl.ast.members, 0..) |member, i| {2359 for (container_decl.ast.members, 0..) |member, i| {
2275 if (i != 0) try renderExtraNewline(ais, tree, member);2360 if (i != 0) try renderExtraNewline(r, member);
2276 switch (tree.nodes.items(.tag)[member]) {2361 switch (tree.nodes.items(.tag)[member]) {
2277 // For container fields, ensure a trailing comma is added if necessary.2362 // For container fields, ensure a trailing comma is added if necessary.
2278 .container_field_init,2363 .container_field_init,
2279 .container_field_align,2364 .container_field_align,
2280 .container_field,2365 .container_field,
2281 => try renderMember(gpa, ais, tree, container, member, .comma),2366 => try renderMember(r, container, member, .comma),
22822367
2283 else => try renderMember(gpa, ais, tree, container, member, .newline),2368 else => try renderMember(r, container, member, .newline),
2284 }2369 }
2285 }2370 }
2286 ais.popIndent();2371 ais.popIndent();
22872372
2288 return renderToken(ais, tree, rbrace, space); // rbrace2373 return renderToken(r, rbrace, space); // rbrace
2289}2374}
22902375
2291fn renderAsm(2376fn renderAsm(
2292 gpa: Allocator,2377 r: *Render,
2293 ais: *Ais,
2294 tree: Ast,
2295 asm_node: Ast.full.Asm,2378 asm_node: Ast.full.Asm,
2296 space: Space,2379 space: Space,
2297) Error!void {2380) Error!void {
2381 const tree = r.tree;
2382 const ais = r.ais;
2298 const token_tags = tree.tokens.items(.tag);2383 const token_tags = tree.tokens.items(.tag);
22992384
2300 try renderToken(ais, tree, asm_node.ast.asm_token, .space); // asm2385 try renderToken(r, asm_node.ast.asm_token, .space); // asm
23012386
2302 if (asm_node.volatile_token) |volatile_token| {2387 if (asm_node.volatile_token) |volatile_token| {
2303 try renderToken(ais, tree, volatile_token, .space); // volatile2388 try renderToken(r, volatile_token, .space); // volatile
2304 try renderToken(ais, tree, volatile_token + 1, .none); // lparen2389 try renderToken(r, volatile_token + 1, .none); // lparen
2305 } else {2390 } else {
2306 try renderToken(ais, tree, asm_node.ast.asm_token + 1, .none); // lparen2391 try renderToken(r, asm_node.ast.asm_token + 1, .none); // lparen
2307 }2392 }
23082393
2309 if (asm_node.ast.items.len == 0) {2394 if (asm_node.ast.items.len == 0) {
...@@ -2311,27 +2396,27 @@ fn renderAsm(...@@ -2311,27 +2396,27 @@ fn renderAsm(
2311 if (asm_node.first_clobber) |first_clobber| {2396 if (asm_node.first_clobber) |first_clobber| {
2312 // asm ("foo" ::: "a", "b")2397 // asm ("foo" ::: "a", "b")
2313 // asm ("foo" ::: "a", "b",)2398 // asm ("foo" ::: "a", "b",)
2314 try renderExpression(gpa, ais, tree, asm_node.ast.template, .space);2399 try renderExpression(r, asm_node.ast.template, .space);
2315 // Render the three colons.2400 // Render the three colons.
2316 try renderToken(ais, tree, first_clobber - 3, .none);2401 try renderToken(r, first_clobber - 3, .none);
2317 try renderToken(ais, tree, first_clobber - 2, .none);2402 try renderToken(r, first_clobber - 2, .none);
2318 try renderToken(ais, tree, first_clobber - 1, .space);2403 try renderToken(r, first_clobber - 1, .space);
23192404
2320 var tok_i = first_clobber;2405 var tok_i = first_clobber;
2321 while (true) : (tok_i += 1) {2406 while (true) : (tok_i += 1) {
2322 try renderToken(ais, tree, tok_i, .none);2407 try renderToken(r, tok_i, .none);
2323 tok_i += 1;2408 tok_i += 1;
2324 switch (token_tags[tok_i]) {2409 switch (token_tags[tok_i]) {
2325 .r_paren => {2410 .r_paren => {
2326 ais.popIndent();2411 ais.popIndent();
2327 return renderToken(ais, tree, tok_i, space);2412 return renderToken(r, tok_i, space);
2328 },2413 },
2329 .comma => {2414 .comma => {
2330 if (token_tags[tok_i + 1] == .r_paren) {2415 if (token_tags[tok_i + 1] == .r_paren) {
2331 ais.popIndent();2416 ais.popIndent();
2332 return renderToken(ais, tree, tok_i + 1, space);2417 return renderToken(r, tok_i + 1, space);
2333 } else {2418 } else {
2334 try renderToken(ais, tree, tok_i, .space);2419 try renderToken(r, tok_i, .space);
2335 }2420 }
2336 },2421 },
2337 else => unreachable,2422 else => unreachable,
...@@ -2339,40 +2424,40 @@ fn renderAsm(...@@ -2339,40 +2424,40 @@ fn renderAsm(
2339 }2424 }
2340 } else {2425 } else {
2341 // asm ("foo")2426 // asm ("foo")
2342 try renderExpression(gpa, ais, tree, asm_node.ast.template, .none);2427 try renderExpression(r, asm_node.ast.template, .none);
2343 ais.popIndent();2428 ais.popIndent();
2344 return renderToken(ais, tree, asm_node.ast.rparen, space); // rparen2429 return renderToken(r, asm_node.ast.rparen, space); // rparen
2345 }2430 }
2346 }2431 }
23472432
2348 ais.pushIndent();2433 ais.pushIndent();
2349 try renderExpression(gpa, ais, tree, asm_node.ast.template, .newline);2434 try renderExpression(r, asm_node.ast.template, .newline);
2350 ais.setIndentDelta(asm_indent_delta);2435 ais.setIndentDelta(asm_indent_delta);
2351 const colon1 = tree.lastToken(asm_node.ast.template) + 1;2436 const colon1 = tree.lastToken(asm_node.ast.template) + 1;
23522437
2353 const colon2 = if (asm_node.outputs.len == 0) colon2: {2438 const colon2 = if (asm_node.outputs.len == 0) colon2: {
2354 try renderToken(ais, tree, colon1, .newline); // :2439 try renderToken(r, colon1, .newline); // :
2355 break :colon2 colon1 + 1;2440 break :colon2 colon1 + 1;
2356 } else colon2: {2441 } else colon2: {
2357 try renderToken(ais, tree, colon1, .space); // :2442 try renderToken(r, colon1, .space); // :
23582443
2359 ais.pushIndent();2444 ais.pushIndent();
2360 for (asm_node.outputs, 0..) |asm_output, i| {2445 for (asm_node.outputs, 0..) |asm_output, i| {
2361 if (i + 1 < asm_node.outputs.len) {2446 if (i + 1 < asm_node.outputs.len) {
2362 const next_asm_output = asm_node.outputs[i + 1];2447 const next_asm_output = asm_node.outputs[i + 1];
2363 try renderAsmOutput(gpa, ais, tree, asm_output, .none);2448 try renderAsmOutput(r, asm_output, .none);
23642449
2365 const comma = tree.firstToken(next_asm_output) - 1;2450 const comma = tree.firstToken(next_asm_output) - 1;
2366 try renderToken(ais, tree, comma, .newline); // ,2451 try renderToken(r, comma, .newline); // ,
2367 try renderExtraNewlineToken(ais, tree, tree.firstToken(next_asm_output));2452 try renderExtraNewlineToken(r, tree.firstToken(next_asm_output));
2368 } else if (asm_node.inputs.len == 0 and asm_node.first_clobber == null) {2453 } else if (asm_node.inputs.len == 0 and asm_node.first_clobber == null) {
2369 try renderAsmOutput(gpa, ais, tree, asm_output, .comma);2454 try renderAsmOutput(r, asm_output, .comma);
2370 ais.popIndent();2455 ais.popIndent();
2371 ais.setIndentDelta(indent_delta);2456 ais.setIndentDelta(indent_delta);
2372 ais.popIndent();2457 ais.popIndent();
2373 return renderToken(ais, tree, asm_node.ast.rparen, space); // rparen2458 return renderToken(r, asm_node.ast.rparen, space); // rparen
2374 } else {2459 } else {
2375 try renderAsmOutput(gpa, ais, tree, asm_output, .comma);2460 try renderAsmOutput(r, asm_output, .comma);
2376 const comma_or_colon = tree.lastToken(asm_output) + 1;2461 const comma_or_colon = tree.lastToken(asm_output) + 1;
2377 ais.popIndent();2462 ais.popIndent();
2378 break :colon2 switch (token_tags[comma_or_colon]) {2463 break :colon2 switch (token_tags[comma_or_colon]) {
...@@ -2384,27 +2469,27 @@ fn renderAsm(...@@ -2384,27 +2469,27 @@ fn renderAsm(
2384 };2469 };
23852470
2386 const colon3 = if (asm_node.inputs.len == 0) colon3: {2471 const colon3 = if (asm_node.inputs.len == 0) colon3: {
2387 try renderToken(ais, tree, colon2, .newline); // :2472 try renderToken(r, colon2, .newline); // :
2388 break :colon3 colon2 + 1;2473 break :colon3 colon2 + 1;
2389 } else colon3: {2474 } else colon3: {
2390 try renderToken(ais, tree, colon2, .space); // :2475 try renderToken(r, colon2, .space); // :
2391 ais.pushIndent();2476 ais.pushIndent();
2392 for (asm_node.inputs, 0..) |asm_input, i| {2477 for (asm_node.inputs, 0..) |asm_input, i| {
2393 if (i + 1 < asm_node.inputs.len) {2478 if (i + 1 < asm_node.inputs.len) {
2394 const next_asm_input = asm_node.inputs[i + 1];2479 const next_asm_input = asm_node.inputs[i + 1];
2395 try renderAsmInput(gpa, ais, tree, asm_input, .none);2480 try renderAsmInput(r, asm_input, .none);
23962481
2397 const first_token = tree.firstToken(next_asm_input);2482 const first_token = tree.firstToken(next_asm_input);
2398 try renderToken(ais, tree, first_token - 1, .newline); // ,2483 try renderToken(r, first_token - 1, .newline); // ,
2399 try renderExtraNewlineToken(ais, tree, first_token);2484 try renderExtraNewlineToken(r, first_token);
2400 } else if (asm_node.first_clobber == null) {2485 } else if (asm_node.first_clobber == null) {
2401 try renderAsmInput(gpa, ais, tree, asm_input, .comma);2486 try renderAsmInput(r, asm_input, .comma);
2402 ais.popIndent();2487 ais.popIndent();
2403 ais.setIndentDelta(indent_delta);2488 ais.setIndentDelta(indent_delta);
2404 ais.popIndent();2489 ais.popIndent();
2405 return renderToken(ais, tree, asm_node.ast.rparen, space); // rparen2490 return renderToken(r, asm_node.ast.rparen, space); // rparen
2406 } else {2491 } else {
2407 try renderAsmInput(gpa, ais, tree, asm_input, .comma);2492 try renderAsmInput(r, asm_input, .comma);
2408 const comma_or_colon = tree.lastToken(asm_input) + 1;2493 const comma_or_colon = tree.lastToken(asm_input) + 1;
2409 ais.popIndent();2494 ais.popIndent();
2410 break :colon3 switch (token_tags[comma_or_colon]) {2495 break :colon3 switch (token_tags[comma_or_colon]) {
...@@ -2416,7 +2501,7 @@ fn renderAsm(...@@ -2416,7 +2501,7 @@ fn renderAsm(
2416 unreachable;2501 unreachable;
2417 };2502 };
24182503
2419 try renderToken(ais, tree, colon3, .space); // :2504 try renderToken(r, colon3, .space); // :
2420 const first_clobber = asm_node.first_clobber.?;2505 const first_clobber = asm_node.first_clobber.?;
2421 var tok_i = first_clobber;2506 var tok_i = first_clobber;
2422 while (true) {2507 while (true) {
...@@ -2424,20 +2509,20 @@ fn renderAsm(...@@ -2424,20 +2509,20 @@ fn renderAsm(
2424 .r_paren => {2509 .r_paren => {
2425 ais.setIndentDelta(indent_delta);2510 ais.setIndentDelta(indent_delta);
2426 ais.popIndent();2511 ais.popIndent();
2427 try renderToken(ais, tree, tok_i, .newline);2512 try renderToken(r, tok_i, .newline);
2428 return renderToken(ais, tree, tok_i + 1, space);2513 return renderToken(r, tok_i + 1, space);
2429 },2514 },
2430 .comma => {2515 .comma => {
2431 switch (token_tags[tok_i + 2]) {2516 switch (token_tags[tok_i + 2]) {
2432 .r_paren => {2517 .r_paren => {
2433 ais.setIndentDelta(indent_delta);2518 ais.setIndentDelta(indent_delta);
2434 ais.popIndent();2519 ais.popIndent();
2435 try renderToken(ais, tree, tok_i, .newline);2520 try renderToken(r, tok_i, .newline);
2436 return renderToken(ais, tree, tok_i + 2, space);2521 return renderToken(r, tok_i + 2, space);
2437 },2522 },
2438 else => {2523 else => {
2439 try renderToken(ais, tree, tok_i, .none);2524 try renderToken(r, tok_i, .none);
2440 try renderToken(ais, tree, tok_i + 1, .space);2525 try renderToken(r, tok_i + 1, .space);
2441 tok_i += 2;2526 tok_i += 2;
2442 },2527 },
2443 }2528 }
...@@ -2448,44 +2533,42 @@ fn renderAsm(...@@ -2448,44 +2533,42 @@ fn renderAsm(
2448}2533}
24492534
2450fn renderCall(2535fn renderCall(
2451 gpa: Allocator,2536 r: *Render,
2452 ais: *Ais,
2453 tree: Ast,
2454 call: Ast.full.Call,2537 call: Ast.full.Call,
2455 space: Space,2538 space: Space,
2456) Error!void {2539) Error!void {
2457 if (call.async_token) |async_token| {2540 if (call.async_token) |async_token| {
2458 try renderToken(ais, tree, async_token, .space);2541 try renderToken(r, async_token, .space);
2459 }2542 }
2460 try renderExpression(gpa, ais, tree, call.ast.fn_expr, .none);2543 try renderExpression(r, call.ast.fn_expr, .none);
2461 try renderParamList(gpa, ais, tree, call.ast.lparen, call.ast.params, space);2544 try renderParamList(r, call.ast.lparen, call.ast.params, space);
2462}2545}
24632546
2464fn renderParamList(2547fn renderParamList(
2465 gpa: Allocator,2548 r: *Render,
2466 ais: *Ais,
2467 tree: Ast,
2468 lparen: Ast.TokenIndex,2549 lparen: Ast.TokenIndex,
2469 params: []const Ast.Node.Index,2550 params: []const Ast.Node.Index,
2470 space: Space,2551 space: Space,
2471) Error!void {2552) Error!void {
2553 const tree = r.tree;
2554 const ais = r.ais;
2472 const token_tags = tree.tokens.items(.tag);2555 const token_tags = tree.tokens.items(.tag);
24732556
2474 if (params.len == 0) {2557 if (params.len == 0) {
2475 ais.pushIndentNextLine();2558 ais.pushIndentNextLine();
2476 try renderToken(ais, tree, lparen, .none);2559 try renderToken(r, lparen, .none);
2477 ais.popIndent();2560 ais.popIndent();
2478 return renderToken(ais, tree, lparen + 1, space); // )2561 return renderToken(r, lparen + 1, space); // )
2479 }2562 }
24802563
2481 const last_param = params[params.len - 1];2564 const last_param = params[params.len - 1];
2482 const after_last_param_tok = tree.lastToken(last_param) + 1;2565 const after_last_param_tok = tree.lastToken(last_param) + 1;
2483 if (token_tags[after_last_param_tok] == .comma) {2566 if (token_tags[after_last_param_tok] == .comma) {
2484 ais.pushIndentNextLine();2567 ais.pushIndentNextLine();
2485 try renderToken(ais, tree, lparen, .newline); // (2568 try renderToken(r, lparen, .newline); // (
2486 for (params, 0..) |param_node, i| {2569 for (params, 0..) |param_node, i| {
2487 if (i + 1 < params.len) {2570 if (i + 1 < params.len) {
2488 try renderExpression(gpa, ais, tree, param_node, .none);2571 try renderExpression(r, param_node, .none);
24892572
2490 // Unindent the comma for multiline string literals.2573 // Unindent the comma for multiline string literals.
2491 const is_multiline_string =2574 const is_multiline_string =
...@@ -2493,20 +2576,20 @@ fn renderParamList(...@@ -2493,20 +2576,20 @@ fn renderParamList(
2493 if (is_multiline_string) ais.popIndent();2576 if (is_multiline_string) ais.popIndent();
24942577
2495 const comma = tree.lastToken(param_node) + 1;2578 const comma = tree.lastToken(param_node) + 1;
2496 try renderToken(ais, tree, comma, .newline); // ,2579 try renderToken(r, comma, .newline); // ,
24972580
2498 if (is_multiline_string) ais.pushIndent();2581 if (is_multiline_string) ais.pushIndent();
24992582
2500 try renderExtraNewline(ais, tree, params[i + 1]);2583 try renderExtraNewline(r, params[i + 1]);
2501 } else {2584 } else {
2502 try renderExpression(gpa, ais, tree, param_node, .comma);2585 try renderExpression(r, param_node, .comma);
2503 }2586 }
2504 }2587 }
2505 ais.popIndent();2588 ais.popIndent();
2506 return renderToken(ais, tree, after_last_param_tok + 1, space); // )2589 return renderToken(r, after_last_param_tok + 1, space); // )
2507 }2590 }
25082591
2509 try renderToken(ais, tree, lparen, .none); // (2592 try renderToken(r, lparen, .none); // (
25102593
2511 for (params, 0..) |param_node, i| {2594 for (params, 0..) |param_node, i| {
2512 const first_param_token = tree.firstToken(param_node);2595 const first_param_token = tree.firstToken(param_node);
...@@ -2515,23 +2598,25 @@ fn renderParamList(...@@ -2515,23 +2598,25 @@ fn renderParamList(
2515 {2598 {
2516 ais.pushIndentOneShot();2599 ais.pushIndentOneShot();
2517 }2600 }
2518 try renderExpression(gpa, ais, tree, param_node, .none);2601 try renderExpression(r, param_node, .none);
25192602
2520 if (i + 1 < params.len) {2603 if (i + 1 < params.len) {
2521 const comma = tree.lastToken(param_node) + 1;2604 const comma = tree.lastToken(param_node) + 1;
2522 const next_multiline_string =2605 const next_multiline_string =
2523 token_tags[tree.firstToken(params[i + 1])] == .multiline_string_literal_line;2606 token_tags[tree.firstToken(params[i + 1])] == .multiline_string_literal_line;
2524 const comma_space: Space = if (next_multiline_string) .none else .space;2607 const comma_space: Space = if (next_multiline_string) .none else .space;
2525 try renderToken(ais, tree, comma, comma_space);2608 try renderToken(r, comma, comma_space);
2526 }2609 }
2527 }2610 }
25282611
2529 return renderToken(ais, tree, after_last_param_tok, space); // )2612 return renderToken(r, after_last_param_tok, space); // )
2530}2613}
25312614
2532/// Renders the given expression indented, popping the indent before rendering2615/// Renders the given expression indented, popping the indent before rendering
2533/// any following line comments2616/// any following line comments
2534fn renderExpressionIndented(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index, space: Space) Error!void {2617fn renderExpressionIndented(r: *Render, node: Ast.Node.Index, space: Space) Error!void {
2618 const tree = r.tree;
2619 const ais = r.ais;
2535 const token_starts = tree.tokens.items(.start);2620 const token_starts = tree.tokens.items(.start);
2536 const token_tags = tree.tokens.items(.tag);2621 const token_tags = tree.tokens.items(.tag);
25372622
...@@ -2545,24 +2630,24 @@ fn renderExpressionIndented(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node...@@ -2545,24 +2630,24 @@ fn renderExpressionIndented(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node
2545 .semicolon => token_tags[last_token + 1] == .semicolon,2630 .semicolon => token_tags[last_token + 1] == .semicolon,
2546 };2631 };
25472632
2548 try renderExpression(gpa, ais, tree, node, if (punctuation) .none else .skip);2633 try renderExpression(r, node, if (punctuation) .none else .skip);
25492634
2550 switch (space) {2635 switch (space) {
2551 .none, .space, .newline, .skip => {},2636 .none, .space, .newline, .skip => {},
2552 .comma => {2637 .comma => {
2553 if (token_tags[last_token + 1] == .comma) {2638 if (token_tags[last_token + 1] == .comma) {
2554 try renderToken(ais, tree, last_token + 1, .skip);2639 try renderToken(r, last_token + 1, .skip);
2555 last_token += 1;2640 last_token += 1;
2556 } else {2641 } else {
2557 try ais.writer().writeByte(',');2642 try ais.writer().writeByte(',');
2558 }2643 }
2559 },2644 },
2560 .comma_space => if (token_tags[last_token + 1] == .comma) {2645 .comma_space => if (token_tags[last_token + 1] == .comma) {
2561 try renderToken(ais, tree, last_token + 1, .skip);2646 try renderToken(r, last_token + 1, .skip);
2562 last_token += 1;2647 last_token += 1;
2563 },2648 },
2564 .semicolon => if (token_tags[last_token + 1] == .semicolon) {2649 .semicolon => if (token_tags[last_token + 1] == .semicolon) {
2565 try renderToken(ais, tree, last_token + 1, .skip);2650 try renderToken(r, last_token + 1, .skip);
2566 last_token += 1;2651 last_token += 1;
2567 },2652 },
2568 }2653 }
...@@ -2572,7 +2657,7 @@ fn renderExpressionIndented(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node...@@ -2572,7 +2657,7 @@ fn renderExpressionIndented(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node
2572 if (space == .skip) return;2657 if (space == .skip) return;
25732658
2574 const comment_start = token_starts[last_token] + tokenSliceForRender(tree, last_token).len;2659 const comment_start = token_starts[last_token] + tokenSliceForRender(tree, last_token).len;
2575 const comment = try renderComments(ais, tree, comment_start, token_starts[last_token + 1]);2660 const comment = try renderComments(r, comment_start, token_starts[last_token + 1]);
25762661
2577 if (!comment) switch (space) {2662 if (!comment) switch (space) {
2578 .none => {},2663 .none => {},
...@@ -2589,40 +2674,43 @@ fn renderExpressionIndented(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node...@@ -2589,40 +2674,43 @@ fn renderExpressionIndented(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node
25892674
2590/// Render an expression, and the comma that follows it, if it is present in the source.2675/// Render an expression, and the comma that follows it, if it is present in the source.
2591/// If a comma is present, and `space` is `Space.comma`, render only a single comma.2676/// If a comma is present, and `space` is `Space.comma`, render only a single comma.
2592fn renderExpressionComma(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index, space: Space) Error!void {2677fn renderExpressionComma(r: *Render, node: Ast.Node.Index, space: Space) Error!void {
2678 const tree = r.tree;
2593 const token_tags = tree.tokens.items(.tag);2679 const token_tags = tree.tokens.items(.tag);
2594 const maybe_comma = tree.lastToken(node) + 1;2680 const maybe_comma = tree.lastToken(node) + 1;
2595 if (token_tags[maybe_comma] == .comma and space != .comma) {2681 if (token_tags[maybe_comma] == .comma and space != .comma) {
2596 try renderExpression(gpa, ais, tree, node, .none);2682 try renderExpression(r, node, .none);
2597 return renderToken(ais, tree, maybe_comma, space);2683 return renderToken(r, maybe_comma, space);
2598 } else {2684 } else {
2599 return renderExpression(gpa, ais, tree, node, space);2685 return renderExpression(r, node, space);
2600 }2686 }
2601}2687}
26022688
2603/// Render a token, and the comma that follows it, if it is present in the source.2689/// Render a token, and the comma that follows it, if it is present in the source.
2604/// If a comma is present, and `space` is `Space.comma`, render only a single comma.2690/// If a comma is present, and `space` is `Space.comma`, render only a single comma.
2605fn renderTokenComma(ais: *Ais, tree: Ast, token: Ast.TokenIndex, space: Space) Error!void {2691fn renderTokenComma(r: *Render, token: Ast.TokenIndex, space: Space) Error!void {
2692 const tree = r.tree;
2606 const token_tags = tree.tokens.items(.tag);2693 const token_tags = tree.tokens.items(.tag);
2607 const maybe_comma = token + 1;2694 const maybe_comma = token + 1;
2608 if (token_tags[maybe_comma] == .comma and space != .comma) {2695 if (token_tags[maybe_comma] == .comma and space != .comma) {
2609 try renderToken(ais, tree, token, .none);2696 try renderToken(r, token, .none);
2610 return renderToken(ais, tree, maybe_comma, space);2697 return renderToken(r, maybe_comma, space);
2611 } else {2698 } else {
2612 return renderToken(ais, tree, token, space);2699 return renderToken(r, token, space);
2613 }2700 }
2614}2701}
26152702
2616/// Render an identifier, and the comma that follows it, if it is present in the source.2703/// Render an identifier, and the comma that follows it, if it is present in the source.
2617/// If a comma is present, and `space` is `Space.comma`, render only a single comma.2704/// If a comma is present, and `space` is `Space.comma`, render only a single comma.
2618fn renderIdentifierComma(ais: *Ais, tree: Ast, token: Ast.TokenIndex, space: Space, quote: QuoteBehavior) Error!void {2705fn renderIdentifierComma(r: *Render, token: Ast.TokenIndex, space: Space, quote: QuoteBehavior) Error!void {
2706 const tree = r.tree;
2619 const token_tags = tree.tokens.items(.tag);2707 const token_tags = tree.tokens.items(.tag);
2620 const maybe_comma = token + 1;2708 const maybe_comma = token + 1;
2621 if (token_tags[maybe_comma] == .comma and space != .comma) {2709 if (token_tags[maybe_comma] == .comma and space != .comma) {
2622 try renderIdentifier(ais, tree, token, .none, quote);2710 try renderIdentifier(r, token, .none, quote);
2623 return renderToken(ais, tree, maybe_comma, space);2711 return renderToken(r, maybe_comma, space);
2624 } else {2712 } else {
2625 return renderIdentifier(ais, tree, token, space, quote);2713 return renderIdentifier(r, token, space, quote);
2626 }2714 }
2627}2715}
26282716
...@@ -2647,13 +2735,17 @@ const Space = enum {...@@ -2647,13 +2735,17 @@ const Space = enum {
2647 skip,2735 skip,
2648};2736};
26492737
2650fn renderToken(ais: *Ais, tree: Ast, token_index: Ast.TokenIndex, space: Space) Error!void {2738fn renderToken(r: *Render, token_index: Ast.TokenIndex, space: Space) Error!void {
2739 const tree = r.tree;
2740 const ais = r.ais;
2651 const lexeme = tokenSliceForRender(tree, token_index);2741 const lexeme = tokenSliceForRender(tree, token_index);
2652 try ais.writer().writeAll(lexeme);2742 try ais.writer().writeAll(lexeme);
2653 try renderSpace(ais, tree, token_index, lexeme.len, space);2743 try renderSpace(r, token_index, lexeme.len, space);
2654}2744}
26552745
2656fn renderSpace(ais: *Ais, tree: Ast, token_index: Ast.TokenIndex, lexeme_len: usize, space: Space) Error!void {2746fn renderSpace(r: *Render, token_index: Ast.TokenIndex, lexeme_len: usize, space: Space) Error!void {
2747 const tree = r.tree;
2748 const ais = r.ais;
2657 const token_tags = tree.tokens.items(.tag);2749 const token_tags = tree.tokens.items(.tag);
2658 const token_starts = tree.tokens.items(.start);2750 const token_starts = tree.tokens.items(.start);
26592751
...@@ -2665,26 +2757,26 @@ fn renderSpace(ais: *Ais, tree: Ast, token_index: Ast.TokenIndex, lexeme_len: us...@@ -2665,26 +2757,26 @@ fn renderSpace(ais: *Ais, tree: Ast, token_index: Ast.TokenIndex, lexeme_len: us
2665 try ais.writer().writeByte(',');2757 try ais.writer().writeByte(',');
2666 }2758 }
26672759
2668 const comment = try renderComments(ais, tree, token_start + lexeme_len, token_starts[token_index + 1]);2760 const comment = try renderComments(r, token_start + lexeme_len, token_starts[token_index + 1]);
2669 switch (space) {2761 switch (space) {
2670 .none => {},2762 .none => {},
2671 .space => if (!comment) try ais.writer().writeByte(' '),2763 .space => if (!comment) try ais.writer().writeByte(' '),
2672 .newline => if (!comment) try ais.insertNewline(),2764 .newline => if (!comment) try ais.insertNewline(),
26732765
2674 .comma => if (token_tags[token_index + 1] == .comma) {2766 .comma => if (token_tags[token_index + 1] == .comma) {
2675 try renderToken(ais, tree, token_index + 1, .newline);2767 try renderToken(r, token_index + 1, .newline);
2676 } else if (!comment) {2768 } else if (!comment) {
2677 try ais.insertNewline();2769 try ais.insertNewline();
2678 },2770 },
26792771
2680 .comma_space => if (token_tags[token_index + 1] == .comma) {2772 .comma_space => if (token_tags[token_index + 1] == .comma) {
2681 try renderToken(ais, tree, token_index + 1, .space);2773 try renderToken(r, token_index + 1, .space);
2682 } else if (!comment) {2774 } else if (!comment) {
2683 try ais.writer().writeByte(' ');2775 try ais.writer().writeByte(' ');
2684 },2776 },
26852777
2686 .semicolon => if (token_tags[token_index + 1] == .semicolon) {2778 .semicolon => if (token_tags[token_index + 1] == .semicolon) {
2687 try renderToken(ais, tree, token_index + 1, .newline);2779 try renderToken(r, token_index + 1, .newline);
2688 } else if (!comment) {2780 } else if (!comment) {
2689 try ais.insertNewline();2781 try ais.insertNewline();
2690 },2782 },
...@@ -2693,18 +2785,32 @@ fn renderSpace(ais: *Ais, tree: Ast, token_index: Ast.TokenIndex, lexeme_len: us...@@ -2693,18 +2785,32 @@ fn renderSpace(ais: *Ais, tree: Ast, token_index: Ast.TokenIndex, lexeme_len: us
2693 }2785 }
2694}2786}
26952787
2788fn renderOnlySpace(r: *Render, space: Space) Error!void {
2789 const ais = r.ais;
2790 switch (space) {
2791 .none => {},
2792 .space => try ais.writer().writeByte(' '),
2793 .newline => try ais.insertNewline(),
2794 .comma => try ais.writer().writeAll(",\n"),
2795 .comma_space => try ais.writer().writeAll(", "),
2796 .semicolon => try ais.writer().writeAll(";\n"),
2797 .skip => unreachable,
2798 }
2799}
2800
2696const QuoteBehavior = enum {2801const QuoteBehavior = enum {
2697 preserve_when_shadowing,2802 preserve_when_shadowing,
2698 eagerly_unquote,2803 eagerly_unquote,
2699 eagerly_unquote_except_underscore,2804 eagerly_unquote_except_underscore,
2700};2805};
27012806
2702fn renderIdentifier(ais: *Ais, tree: Ast, token_index: Ast.TokenIndex, space: Space, quote: QuoteBehavior) Error!void {2807fn renderIdentifier(r: *Render, token_index: Ast.TokenIndex, space: Space, quote: QuoteBehavior) Error!void {
2808 const tree = r.tree;
2703 const token_tags = tree.tokens.items(.tag);2809 const token_tags = tree.tokens.items(.tag);
2704 assert(token_tags[token_index] == .identifier);2810 assert(token_tags[token_index] == .identifier);
2705 const lexeme = tokenSliceForRender(tree, token_index);2811 const lexeme = tokenSliceForRender(tree, token_index);
2706 if (lexeme[0] != '@') {2812 if (lexeme[0] != '@') {
2707 return renderToken(ais, tree, token_index, space);2813 return renderToken(r, token_index, space);
2708 }2814 }
27092815
2710 assert(lexeme.len >= 3);2816 assert(lexeme.len >= 3);
...@@ -2715,15 +2821,15 @@ fn renderIdentifier(ais: *Ais, tree: Ast, token_index: Ast.TokenIndex, space: Sp...@@ -2715,15 +2821,15 @@ fn renderIdentifier(ais: *Ais, tree: Ast, token_index: Ast.TokenIndex, space: Sp
27152821
2716 // Empty name can't be unquoted.2822 // Empty name can't be unquoted.
2717 if (contents.len == 0) {2823 if (contents.len == 0) {
2718 return renderQuotedIdentifier(ais, tree, token_index, space, false);2824 return renderQuotedIdentifier(r, token_index, space, false);
2719 }2825 }
27202826
2721 // Special case for _ which would incorrectly be rejected by isValidId below.2827 // Special case for _ which would incorrectly be rejected by isValidId below.
2722 if (contents.len == 1 and contents[0] == '_') switch (quote) {2828 if (contents.len == 1 and contents[0] == '_') switch (quote) {
2723 .eagerly_unquote => return renderQuotedIdentifier(ais, tree, token_index, space, true),2829 .eagerly_unquote => return renderQuotedIdentifier(r, token_index, space, true),
2724 .eagerly_unquote_except_underscore,2830 .eagerly_unquote_except_underscore,
2725 .preserve_when_shadowing,2831 .preserve_when_shadowing,
2726 => return renderQuotedIdentifier(ais, tree, token_index, space, false),2832 => return renderQuotedIdentifier(r, token_index, space, false),
2727 };2833 };
27282834
2729 // Scan the entire name for characters that would (after un-escaping) be illegal in a symbol,2835 // Scan the entire name for characters that would (after un-escaping) be illegal in a symbol,
...@@ -2731,23 +2837,23 @@ fn renderIdentifier(ais: *Ais, tree: Ast, token_index: Ast.TokenIndex, space: Sp...@@ -2731,23 +2837,23 @@ fn renderIdentifier(ais: *Ais, tree: Ast, token_index: Ast.TokenIndex, space: Sp
2731 var contents_i: usize = 0;2837 var contents_i: usize = 0;
2732 while (contents_i < contents.len) {2838 while (contents_i < contents.len) {
2733 switch (contents[contents_i]) {2839 switch (contents[contents_i]) {
2734 '0'...'9' => if (contents_i == 0) return renderQuotedIdentifier(ais, tree, token_index, space, false),2840 '0'...'9' => if (contents_i == 0) return renderQuotedIdentifier(r, token_index, space, false),
2735 'A'...'Z', 'a'...'z', '_' => {},2841 'A'...'Z', 'a'...'z', '_' => {},
2736 '\\' => {2842 '\\' => {
2737 var esc_offset = contents_i;2843 var esc_offset = contents_i;
2738 const res = std.zig.string_literal.parseEscapeSequence(contents, &esc_offset);2844 const res = std.zig.string_literal.parseEscapeSequence(contents, &esc_offset);
2739 switch (res) {2845 switch (res) {
2740 .success => |char| switch (char) {2846 .success => |char| switch (char) {
2741 '0'...'9' => if (contents_i == 0) return renderQuotedIdentifier(ais, tree, token_index, space, false),2847 '0'...'9' => if (contents_i == 0) return renderQuotedIdentifier(r, token_index, space, false),
2742 'A'...'Z', 'a'...'z', '_' => {},2848 'A'...'Z', 'a'...'z', '_' => {},
2743 else => return renderQuotedIdentifier(ais, tree, token_index, space, false),2849 else => return renderQuotedIdentifier(r, token_index, space, false),
2744 },2850 },
2745 .failure => return renderQuotedIdentifier(ais, tree, token_index, space, false),2851 .failure => return renderQuotedIdentifier(r, token_index, space, false),
2746 }2852 }
2747 contents_i += esc_offset;2853 contents_i += esc_offset;
2748 continue;2854 continue;
2749 },2855 },
2750 else => return renderQuotedIdentifier(ais, tree, token_index, space, false),2856 else => return renderQuotedIdentifier(r, token_index, space, false),
2751 }2857 }
2752 contents_i += 1;2858 contents_i += 1;
2753 }2859 }
...@@ -2784,23 +2890,25 @@ fn renderIdentifier(ais: *Ais, tree: Ast, token_index: Ast.TokenIndex, space: Sp...@@ -2784,23 +2890,25 @@ fn renderIdentifier(ais: *Ais, tree: Ast, token_index: Ast.TokenIndex, space: Sp
2784 // We read the whole thing, so it could be a keyword or primitive.2890 // We read the whole thing, so it could be a keyword or primitive.
2785 if (contents_i == contents.len) {2891 if (contents_i == contents.len) {
2786 if (!std.zig.isValidId(buf[0..buf_i])) {2892 if (!std.zig.isValidId(buf[0..buf_i])) {
2787 return renderQuotedIdentifier(ais, tree, token_index, space, false);2893 return renderQuotedIdentifier(r, token_index, space, false);
2788 }2894 }
2789 if (primitives.isPrimitive(buf[0..buf_i])) switch (quote) {2895 if (primitives.isPrimitive(buf[0..buf_i])) switch (quote) {
2790 .eagerly_unquote,2896 .eagerly_unquote,
2791 .eagerly_unquote_except_underscore,2897 .eagerly_unquote_except_underscore,
2792 => return renderQuotedIdentifier(ais, tree, token_index, space, true),2898 => return renderQuotedIdentifier(r, token_index, space, true),
2793 .preserve_when_shadowing => return renderQuotedIdentifier(ais, tree, token_index, space, false),2899 .preserve_when_shadowing => return renderQuotedIdentifier(r, token_index, space, false),
2794 };2900 };
2795 }2901 }
27962902
2797 try renderQuotedIdentifier(ais, tree, token_index, space, true);2903 try renderQuotedIdentifier(r, token_index, space, true);
2798}2904}
27992905
2800// Renders a @"" quoted identifier, normalizing escapes.2906// Renders a @"" quoted identifier, normalizing escapes.
2801// Unnecessary escapes are un-escaped, and \u escapes are normalized to \x when they fit.2907// Unnecessary escapes are un-escaped, and \u escapes are normalized to \x when they fit.
2802// If unquote is true, the @"" is removed and the result is a bare symbol whose validity is asserted.2908// If unquote is true, the @"" is removed and the result is a bare symbol whose validity is asserted.
2803fn renderQuotedIdentifier(ais: *Ais, tree: Ast, token_index: Ast.TokenIndex, space: Space, comptime unquote: bool) !void {2909fn renderQuotedIdentifier(r: *Render, token_index: Ast.TokenIndex, space: Space, comptime unquote: bool) !void {
2910 const tree = r.tree;
2911 const ais = r.ais;
2804 const token_tags = tree.tokens.items(.tag);2912 const token_tags = tree.tokens.items(.tag);
2805 assert(token_tags[token_index] == .identifier);2913 assert(token_tags[token_index] == .identifier);
2806 const lexeme = tokenSliceForRender(tree, token_index);2914 const lexeme = tokenSliceForRender(tree, token_index);
...@@ -2811,7 +2919,7 @@ fn renderQuotedIdentifier(ais: *Ais, tree: Ast, token_index: Ast.TokenIndex, spa...@@ -2811,7 +2919,7 @@ fn renderQuotedIdentifier(ais: *Ais, tree: Ast, token_index: Ast.TokenIndex, spa
2811 try renderIdentifierContents(ais.writer(), contents);2919 try renderIdentifierContents(ais.writer(), contents);
2812 if (!unquote) try ais.writer().writeByte('\"');2920 if (!unquote) try ais.writer().writeByte('\"');
28132921
2814 try renderSpace(ais, tree, token_index, lexeme.len, space);2922 try renderSpace(r, token_index, lexeme.len, space);
2815}2923}
28162924
2817fn renderIdentifierContents(writer: anytype, bytes: []const u8) !void {2925fn renderIdentifierContents(writer: anytype, bytes: []const u8) !void {
...@@ -2884,7 +2992,10 @@ fn hasMultilineString(tree: Ast, start_token: Ast.TokenIndex, end_token: Ast.Tok...@@ -2884,7 +2992,10 @@ fn hasMultilineString(tree: Ast, start_token: Ast.TokenIndex, end_token: Ast.Tok
28842992
2885/// Assumes that start is the first byte past the previous token and2993/// Assumes that start is the first byte past the previous token and
2886/// that end is the last byte before the next token.2994/// that end is the last byte before the next token.
2887fn renderComments(ais: *Ais, tree: Ast, start: usize, end: usize) Error!bool {2995fn renderComments(r: *Render, start: usize, end: usize) Error!bool {
2996 const tree = r.tree;
2997 const ais = r.ais;
2998
2888 var index: usize = start;2999 var index: usize = start;
2889 while (mem.indexOf(u8, tree.source[index..end], "//")) |offset| {3000 while (mem.indexOf(u8, tree.source[index..end], "//")) |offset| {
2890 const comment_start = index + offset;3001 const comment_start = index + offset;
...@@ -2944,12 +3055,14 @@ fn renderComments(ais: *Ais, tree: Ast, start: usize, end: usize) Error!bool {...@@ -2944,12 +3055,14 @@ fn renderComments(ais: *Ais, tree: Ast, start: usize, end: usize) Error!bool {
2944 return index != start;3055 return index != start;
2945}3056}
29463057
2947fn renderExtraNewline(ais: *Ais, tree: Ast, node: Ast.Node.Index) Error!void {3058fn renderExtraNewline(r: *Render, node: Ast.Node.Index) Error!void {
2948 return renderExtraNewlineToken(ais, tree, tree.firstToken(node));3059 return renderExtraNewlineToken(r, r.tree.firstToken(node));
2949}3060}
29503061
2951/// Check if there is an empty line immediately before the given token. If so, render it.3062/// Check if there is an empty line immediately before the given token. If so, render it.
2952fn renderExtraNewlineToken(ais: *Ais, tree: Ast, token_index: Ast.TokenIndex) Error!void {3063fn renderExtraNewlineToken(r: *Render, token_index: Ast.TokenIndex) Error!void {
3064 const tree = r.tree;
3065 const ais = r.ais;
2953 const token_starts = tree.tokens.items(.start);3066 const token_starts = tree.tokens.items(.start);
2954 const token_start = token_starts[token_index];3067 const token_start = token_starts[token_index];
2955 if (token_start == 0) return;3068 if (token_start == 0) return;
...@@ -2974,7 +3087,8 @@ fn renderExtraNewlineToken(ais: *Ais, tree: Ast, token_index: Ast.TokenIndex) Er...@@ -2974,7 +3087,8 @@ fn renderExtraNewlineToken(ais: *Ais, tree: Ast, token_index: Ast.TokenIndex) Er
29743087
2975/// end_token is the token one past the last doc comment token. This function3088/// end_token is the token one past the last doc comment token. This function
2976/// searches backwards from there.3089/// searches backwards from there.
2977fn renderDocComments(ais: *Ais, tree: Ast, end_token: Ast.TokenIndex) Error!void {3090fn renderDocComments(r: *Render, end_token: Ast.TokenIndex) Error!void {
3091 const tree = r.tree;
2978 // Search backwards for the first doc comment.3092 // Search backwards for the first doc comment.
2979 const token_tags = tree.tokens.items(.tag);3093 const token_tags = tree.tokens.items(.tag);
2980 if (end_token == 0) return;3094 if (end_token == 0) return;
...@@ -2995,27 +3109,45 @@ fn renderDocComments(ais: *Ais, tree: Ast, end_token: Ast.TokenIndex) Error!void...@@ -2995,27 +3109,45 @@ fn renderDocComments(ais: *Ais, tree: Ast, end_token: Ast.TokenIndex) Error!void
2995 assert(prev_token_tag != .l_paren);3109 assert(prev_token_tag != .l_paren);
29963110
2997 if (prev_token_tag != .l_brace) {3111 if (prev_token_tag != .l_brace) {
2998 try renderExtraNewlineToken(ais, tree, first_tok);3112 try renderExtraNewlineToken(r, first_tok);
2999 }3113 }
3000 }3114 }
30013115
3002 while (token_tags[tok] == .doc_comment) : (tok += 1) {3116 while (token_tags[tok] == .doc_comment) : (tok += 1) {
3003 try renderToken(ais, tree, tok, .newline);3117 try renderToken(r, tok, .newline);
3004 }3118 }
3005}3119}
30063120
3007/// start_token is first container doc comment token.3121/// start_token is first container doc comment token.
3008fn renderContainerDocComments(ais: *Ais, tree: Ast, start_token: Ast.TokenIndex) Error!void {3122fn renderContainerDocComments(r: *Render, start_token: Ast.TokenIndex) Error!void {
3123 const tree = r.tree;
3009 const token_tags = tree.tokens.items(.tag);3124 const token_tags = tree.tokens.items(.tag);
3010 var tok = start_token;3125 var tok = start_token;
3011 while (token_tags[tok] == .container_doc_comment) : (tok += 1) {3126 while (token_tags[tok] == .container_doc_comment) : (tok += 1) {
3012 try renderToken(ais, tree, tok, .newline);3127 try renderToken(r, tok, .newline);
3013 }3128 }
3014 // Render extra newline if there is one between final container doc comment and3129 // Render extra newline if there is one between final container doc comment and
3015 // the next token. If the next token is a doc comment, that code path3130 // the next token. If the next token is a doc comment, that code path
3016 // will have its own logic to insert a newline.3131 // will have its own logic to insert a newline.
3017 if (token_tags[tok] != .doc_comment) {3132 if (token_tags[tok] != .doc_comment) {
3018 try renderExtraNewlineToken(ais, tree, tok);3133 try renderExtraNewlineToken(r, tok);
3134 }
3135}
3136
3137fn discardAllParams(r: *Render, fn_proto_node: Ast.Node.Index) Error!void {
3138 const tree = &r.tree;
3139 const ais = r.ais;
3140 var buf: [1]Ast.Node.Index = undefined;
3141 const fn_proto = tree.fullFnProto(&buf, fn_proto_node).?;
3142 const token_tags = tree.tokens.items(.tag);
3143 var it = fn_proto.iterate(tree);
3144 while (it.next()) |param| {
3145 const name_ident = param.name_token.?;
3146 assert(token_tags[name_ident] == .identifier);
3147 const w = ais.writer();
3148 try w.writeAll("_ = ");
3149 try w.writeAll(tokenSliceForRender(r.tree, name_ident));
3150 try w.writeAll(";\n");
3019 }3151 }
3020}3152}
30213153
src/main.zig+11-1
...@@ -212,6 +212,14 @@ pub fn main() anyerror!void {...@@ -212,6 +212,14 @@ pub fn main() anyerror!void {
212 }212 }
213 }213 }
214214
215 if (build_options.only_reduce) {
216 if (mem.eql(u8, args[1], "reduce")) {
217 return @import("reduce.zig").main(gpa, arena, args);
218 } else {
219 @panic("only reduce is supported in a -Donly-reduce build");
220 }
221 }
222
215 return mainArgs(gpa, arena, args);223 return mainArgs(gpa, arena, args);
216}224}
217225
...@@ -328,6 +336,8 @@ pub fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi...@@ -328,6 +336,8 @@ pub fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi
328 } else if (mem.eql(u8, cmd, "env")) {336 } else if (mem.eql(u8, cmd, "env")) {
329 verifyLibcxxCorrectlyLinked();337 verifyLibcxxCorrectlyLinked();
330 return @import("print_env.zig").cmdEnv(arena, cmd_args, io.getStdOut().writer());338 return @import("print_env.zig").cmdEnv(arena, cmd_args, io.getStdOut().writer());
339 } else if (mem.eql(u8, cmd, "reduce")) {
340 return @import("reduce.zig").main(gpa, arena, args);
331 } else if (mem.eql(u8, cmd, "zen")) {341 } else if (mem.eql(u8, cmd, "zen")) {
332 return io.getStdOut().writeAll(info_zen);342 return io.getStdOut().writeAll(info_zen);
333 } else if (mem.eql(u8, cmd, "help") or mem.eql(u8, cmd, "-h") or mem.eql(u8, cmd, "--help")) {343 } else if (mem.eql(u8, cmd, "help") or mem.eql(u8, cmd, "-h") or mem.eql(u8, cmd, "--help")) {
...@@ -5766,7 +5776,7 @@ fn fmtPathFile(...@@ -5766,7 +5776,7 @@ fn fmtPathFile(
5766 fmt.out_buffer.shrinkRetainingCapacity(0);5776 fmt.out_buffer.shrinkRetainingCapacity(0);
5767 try fmt.out_buffer.ensureTotalCapacity(source_code.len);5777 try fmt.out_buffer.ensureTotalCapacity(source_code.len);
57685778
5769 try tree.renderToArrayList(&fmt.out_buffer);5779 try tree.renderToArrayList(&fmt.out_buffer, .{});
5770 if (mem.eql(u8, fmt.out_buffer.items, source_code))5780 if (mem.eql(u8, fmt.out_buffer.items, source_code))
5771 return;5781 return;
57725782
src/reduce.zig created+280
...@@ -0,0 +1,280 @@
1const std = @import("std");
2const mem = std.mem;
3const Allocator = std.mem.Allocator;
4const assert = std.debug.assert;
5const fatal = @import("./main.zig").fatal;
6const Ast = std.zig.Ast;
7const Walk = @import("reduce/Walk.zig");
8
9const usage =
10 \\zig reduce [options] ./checker root_source_file.zig [-- [argv]]
11 \\
12 \\root_source_file.zig is relative to --main-mod-path.
13 \\
14 \\checker:
15 \\ An executable that communicates interestingness by returning these exit codes:
16 \\ exit(0): interesting
17 \\ exit(1): unknown (infinite loop or other mishap)
18 \\ exit(other): not interesting
19 \\
20 \\options:
21 \\ --seed [integer] Override the random seed. Defaults to 0
22 \\ --skip-smoke-test Skip interestingness check smoke test
23 \\ --mod [name]:[deps]:[src] Make a module available for dependency under the given name
24 \\ deps: [dep],[dep],...
25 \\ dep: [[import=]name]
26 \\ --deps [dep],[dep],... Set dependency names for the root package
27 \\ dep: [[import=]name]
28 \\ --main-mod-path Set the directory of the root module
29 \\
30 \\argv:
31 \\ Forwarded directly to the interestingness script.
32 \\
33;
34
35const Interestingness = enum { interesting, unknown, boring };
36
37// Roadmap:
38// - add thread pool
39// - add support for parsing the module flags
40// - more fancy transformations
41// - @import inlining of modules
42// - @import inlining of files
43// - deleting unused functions and other globals
44// - removing statements or blocks of code
45// - replacing operands of `and` and `or` with `true` and `false`
46// - replacing if conditions with `true` and `false`
47// - reduce flags sent to the compiler
48// - integrate with the build system?
49
50pub fn main(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
51 var opt_checker_path: ?[]const u8 = null;
52 var opt_root_source_file_path: ?[]const u8 = null;
53 var argv: []const []const u8 = &.{};
54 var seed: u32 = 0;
55 var skip_smoke_test = false;
56
57 {
58 var i: usize = 2; // skip over "zig" and "reduce"
59 while (i < args.len) : (i += 1) {
60 const arg = args[i];
61 if (mem.startsWith(u8, arg, "-")) {
62 if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) {
63 const stdout = std.io.getStdOut().writer();
64 try stdout.writeAll(usage);
65 return std.process.cleanExit();
66 } else if (mem.eql(u8, arg, "--")) {
67 argv = args[i + 1 ..];
68 break;
69 } else if (mem.eql(u8, arg, "--skip-smoke-test")) {
70 skip_smoke_test = true;
71 } else if (mem.eql(u8, arg, "--main-mod-path")) {
72 @panic("TODO: implement --main-mod-path");
73 } else if (mem.eql(u8, arg, "--mod")) {
74 @panic("TODO: implement --mod");
75 } else if (mem.eql(u8, arg, "--deps")) {
76 @panic("TODO: implement --deps");
77 } else if (mem.eql(u8, arg, "--seed")) {
78 i += 1;
79 if (i >= args.len) fatal("expected 32-bit integer after {s}", .{arg});
80 const next_arg = args[i];
81 seed = std.fmt.parseUnsigned(u32, next_arg, 0) catch |err| {
82 fatal("unable to parse seed '{s}' as 32-bit integer: {s}", .{
83 next_arg, @errorName(err),
84 });
85 };
86 } else {
87 fatal("unrecognized parameter: '{s}'", .{arg});
88 }
89 } else if (opt_checker_path == null) {
90 opt_checker_path = arg;
91 } else if (opt_root_source_file_path == null) {
92 opt_root_source_file_path = arg;
93 } else {
94 fatal("unexpected extra parameter: '{s}'", .{arg});
95 }
96 }
97 }
98
99 const checker_path = opt_checker_path orelse
100 fatal("missing interestingness checker argument; see -h for usage", .{});
101 const root_source_file_path = opt_root_source_file_path orelse
102 fatal("missing root source file path argument; see -h for usage", .{});
103
104 var interestingness_argv: std.ArrayListUnmanaged([]const u8) = .{};
105 try interestingness_argv.ensureUnusedCapacity(arena, argv.len + 1);
106 interestingness_argv.appendAssumeCapacity(checker_path);
107 interestingness_argv.appendSliceAssumeCapacity(argv);
108
109 var rendered = std.ArrayList(u8).init(gpa);
110 defer rendered.deinit();
111
112 var tree = try parse(gpa, arena, root_source_file_path);
113 defer tree.deinit(gpa);
114
115 if (!skip_smoke_test) {
116 std.debug.print("smoke testing the interestingness check...\n", .{});
117 switch (try runCheck(arena, interestingness_argv.items)) {
118 .interesting => {},
119 .boring, .unknown => |t| {
120 fatal("interestingness check returned {s} for unmodified input\n", .{
121 @tagName(t),
122 });
123 },
124 }
125 }
126
127 var fixups: Ast.Fixups = .{};
128 defer fixups.deinit(gpa);
129 var rng = std.rand.DefaultPrng.init(seed);
130
131 // 1. Walk the AST of the source file looking for independent
132 // reductions and collecting them all into an array list.
133 // 2. Randomize the list of transformations. A future enhancement will add
134 // priority weights to the sorting but for now they are completely
135 // shuffled.
136 // 3. Apply a subset consisting of 1/2 of the transformations and check for
137 // interestingness.
138 // 4. If not interesting, half the subset size again and check again.
139 // 5. Repeat until the subset size is 1, then march the transformation
140 // index forward by 1 with each non-interesting attempt.
141 //
142 // At any point if a subset of transformations succeeds in producing an interesting
143 // result, restart the whole process, reparsing the AST and re-generating the list
144 // of all possible transformations and shuffling it again.
145
146 var transformations = std.ArrayList(Walk.Transformation).init(gpa);
147 defer transformations.deinit();
148 try Walk.findTransformations(&tree, &transformations);
149 sortTransformations(transformations.items, rng.random());
150
151 fresh: while (transformations.items.len > 0) {
152 std.debug.print("found {d} possible transformations\n", .{
153 transformations.items.len,
154 });
155 var subset_size: usize = transformations.items.len;
156 var start_index: usize = 0;
157
158 while (start_index < transformations.items.len) {
159 subset_size = @max(1, subset_size / 2);
160
161 const this_set = transformations.items[start_index..][0..subset_size];
162 try transformationsToFixups(gpa, this_set, &fixups);
163
164 rendered.clearRetainingCapacity();
165 try tree.renderToArrayList(&rendered, fixups);
166 try std.fs.cwd().writeFile(root_source_file_path, rendered.items);
167
168 const interestingness = try runCheck(arena, interestingness_argv.items);
169 std.debug.print("{d} random transformations: {s}. {d} remaining\n", .{
170 subset_size, @tagName(interestingness), transformations.items.len - start_index,
171 });
172 switch (interestingness) {
173 .interesting => {
174 const new_tree = try parse(gpa, arena, root_source_file_path);
175 tree.deinit(gpa);
176 tree = new_tree;
177
178 try Walk.findTransformations(&tree, &transformations);
179 // Resetting based on the seed again means we will get the same
180 // results if restarting the reduction process from this new point.
181 rng = std.rand.DefaultPrng.init(seed);
182 sortTransformations(transformations.items, rng.random());
183
184 continue :fresh;
185 },
186 .unknown, .boring => {
187 // Continue to try the next set of transformations.
188 // If we tested only one transformation, move on to the next one.
189 if (subset_size == 1) {
190 start_index += 1;
191 }
192 },
193 }
194 }
195 std.debug.print("all {d} remaining transformations are uninteresting\n", .{
196 transformations.items.len,
197 });
198
199 // Revert the source back to not be transformed.
200 fixups.clearRetainingCapacity();
201 rendered.clearRetainingCapacity();
202 try tree.renderToArrayList(&rendered, fixups);
203 try std.fs.cwd().writeFile(root_source_file_path, rendered.items);
204
205 return std.process.cleanExit();
206 }
207 std.debug.print("no more transformations found\n", .{});
208 return std.process.cleanExit();
209}
210
211fn sortTransformations(transformations: []Walk.Transformation, rng: std.rand.Random) void {
212 rng.shuffle(Walk.Transformation, transformations);
213 // Stable sort based on priority to keep randomness as the secondary sort.
214 // TODO: introduce transformation priorities
215 // std.mem.sort(transformations);
216}
217
218fn termToInteresting(term: std.process.Child.Term) Interestingness {
219 return switch (term) {
220 .Exited => |code| switch (code) {
221 0 => .interesting,
222 1 => .unknown,
223 else => .boring,
224 },
225 else => b: {
226 std.debug.print("interestingness check aborted unexpectedly\n", .{});
227 break :b .boring;
228 },
229 };
230}
231
232fn runCheck(arena: std.mem.Allocator, argv: []const []const u8) !Interestingness {
233 const result = try std.process.Child.run(.{
234 .allocator = arena,
235 .argv = argv,
236 });
237 if (result.stderr.len != 0)
238 std.debug.print("{s}", .{result.stderr});
239 return termToInteresting(result.term);
240}
241
242fn transformationsToFixups(
243 gpa: Allocator,
244 transforms: []const Walk.Transformation,
245 fixups: *Ast.Fixups,
246) !void {
247 fixups.clearRetainingCapacity();
248
249 for (transforms) |t| switch (t) {
250 .gut_function => |fn_decl_node| {
251 try fixups.gut_functions.put(gpa, fn_decl_node, {});
252 },
253 .delete_node => |decl_node| {
254 try fixups.omit_nodes.put(gpa, decl_node, {});
255 },
256 .replace_with_undef => |node| {
257 try fixups.replace_nodes.put(gpa, node, {});
258 },
259 };
260}
261
262fn parse(gpa: Allocator, arena: Allocator, root_source_file_path: []const u8) !Ast {
263 const source_code = try std.fs.cwd().readFileAllocOptions(
264 arena,
265 root_source_file_path,
266 std.math.maxInt(u32),
267 null,
268 1,
269 0,
270 );
271
272 var tree = try Ast.parse(gpa, source_code, .zig);
273 errdefer tree.deinit(gpa);
274
275 if (tree.errors.len != 0) {
276 @panic("syntax errors occurred");
277 }
278
279 return tree;
280}
src/reduce/Walk.zig created+893
...@@ -0,0 +1,893 @@
1const std = @import("std");
2const Ast = std.zig.Ast;
3const Walk = @This();
4const assert = std.debug.assert;
5
6ast: *const Ast,
7transformations: *std.ArrayList(Transformation),
8unreferenced_globals: std.StringArrayHashMapUnmanaged(Ast.Node.Index),
9gpa: std.mem.Allocator,
10
11pub const Transformation = union(enum) {
12 /// Replace the fn decl AST Node with one whose body is only `@trap()` with
13 /// discarded parameters.
14 gut_function: Ast.Node.Index,
15 /// Omit a global declaration.
16 delete_node: Ast.Node.Index,
17 /// Replace an expression with `undefined`.
18 replace_with_undef: Ast.Node.Index,
19};
20
21pub const Error = error{OutOfMemory};
22
23/// The result will be priority shuffled.
24pub fn findTransformations(ast: *const Ast, transformations: *std.ArrayList(Transformation)) !void {
25 transformations.clearRetainingCapacity();
26
27 var walk: Walk = .{
28 .ast = ast,
29 .transformations = transformations,
30 .gpa = transformations.allocator,
31 .unreferenced_globals = .{},
32 };
33 defer walk.unreferenced_globals.deinit(walk.gpa);
34
35 try walkMembers(&walk, walk.ast.rootDecls());
36
37 const unreferenced_globals = walk.unreferenced_globals.values();
38 try transformations.ensureUnusedCapacity(unreferenced_globals.len);
39 for (unreferenced_globals) |node| {
40 transformations.appendAssumeCapacity(.{ .delete_node = node });
41 }
42}
43
44fn walkMembers(w: *Walk, members: []const Ast.Node.Index) Error!void {
45 // First we scan for globals so that we can delete them while walking.
46 try scanDecls(w, members);
47
48 for (members) |member| {
49 try walkMember(w, member);
50 }
51}
52
53fn scanDecls(w: *Walk, members: []const Ast.Node.Index) Error!void {
54 const ast = w.ast;
55 const gpa = w.gpa;
56 const node_tags = ast.nodes.items(.tag);
57 const main_tokens = ast.nodes.items(.main_token);
58 const token_tags = ast.tokens.items(.tag);
59
60 for (members) |member_node| {
61 const name_token = switch (node_tags[member_node]) {
62 .global_var_decl,
63 .local_var_decl,
64 .simple_var_decl,
65 .aligned_var_decl,
66 => main_tokens[member_node] + 1,
67
68 .fn_proto_simple,
69 .fn_proto_multi,
70 .fn_proto_one,
71 .fn_proto,
72 .fn_decl,
73 => main_tokens[member_node] + 1,
74
75 else => continue,
76 };
77 assert(token_tags[name_token] == .identifier);
78 const name_bytes = ast.tokenSlice(name_token);
79 try w.unreferenced_globals.put(gpa, name_bytes, member_node);
80 }
81}
82
83fn walkMember(w: *Walk, decl: Ast.Node.Index) Error!void {
84 const ast = w.ast;
85 const datas = ast.nodes.items(.data);
86 switch (ast.nodes.items(.tag)[decl]) {
87 .fn_decl => {
88 const fn_proto = datas[decl].lhs;
89 try walkExpression(w, fn_proto);
90 const body_node = datas[decl].rhs;
91 if (!isFnBodyGutted(ast, body_node)) {
92 try w.transformations.append(.{ .gut_function = decl });
93 }
94 try walkExpression(w, body_node);
95 },
96 .fn_proto_simple,
97 .fn_proto_multi,
98 .fn_proto_one,
99 .fn_proto,
100 => {
101 try walkExpression(w, decl);
102 },
103
104 .@"usingnamespace" => {
105 try w.transformations.append(.{ .delete_node = decl });
106 const expr = datas[decl].lhs;
107 try walkExpression(w, expr);
108 },
109
110 .global_var_decl,
111 .local_var_decl,
112 .simple_var_decl,
113 .aligned_var_decl,
114 => try walkGlobalVarDecl(w, decl, ast.fullVarDecl(decl).?),
115
116 .test_decl => {
117 try w.transformations.append(.{ .delete_node = decl });
118 try walkExpression(w, datas[decl].rhs);
119 },
120
121 .container_field_init,
122 .container_field_align,
123 .container_field,
124 => try walkContainerField(w, ast.fullContainerField(decl).?),
125
126 .@"comptime" => {
127 try w.transformations.append(.{ .delete_node = decl });
128 try walkExpression(w, decl);
129 },
130
131 .root => unreachable,
132 else => unreachable,
133 }
134}
135
136fn walkExpression(w: *Walk, node: Ast.Node.Index) Error!void {
137 const ast = w.ast;
138 const token_tags = ast.tokens.items(.tag);
139 const main_tokens = ast.nodes.items(.main_token);
140 const node_tags = ast.nodes.items(.tag);
141 const datas = ast.nodes.items(.data);
142 switch (node_tags[node]) {
143 .identifier => try walkIdentifier(w, main_tokens[node]),
144
145 .number_literal,
146 .char_literal,
147 .unreachable_literal,
148 .anyframe_literal,
149 .string_literal,
150 => {},
151
152 .multiline_string_literal => {},
153
154 .error_value => {},
155
156 .block_two,
157 .block_two_semicolon,
158 => {
159 const statements = [2]Ast.Node.Index{ datas[node].lhs, datas[node].rhs };
160 if (datas[node].lhs == 0) {
161 return walkBlock(w, node, statements[0..0]);
162 } else if (datas[node].rhs == 0) {
163 return walkBlock(w, node, statements[0..1]);
164 } else {
165 return walkBlock(w, node, statements[0..2]);
166 }
167 },
168 .block,
169 .block_semicolon,
170 => {
171 const statements = ast.extra_data[datas[node].lhs..datas[node].rhs];
172 return walkBlock(w, node, statements);
173 },
174
175 .@"errdefer" => {
176 const expr = datas[node].rhs;
177 return walkExpression(w, expr);
178 },
179
180 .@"defer" => {
181 const expr = datas[node].rhs;
182 return walkExpression(w, expr);
183 },
184 .@"comptime", .@"nosuspend" => {
185 const block = datas[node].lhs;
186 return walkExpression(w, block);
187 },
188
189 .@"suspend" => {
190 const body = datas[node].lhs;
191 return walkExpression(w, body);
192 },
193
194 .@"catch" => {
195 try walkExpression(w, datas[node].lhs); // target
196 try walkExpression(w, datas[node].rhs); // fallback
197 },
198
199 .field_access => {
200 const field_access = datas[node];
201 try walkExpression(w, field_access.lhs);
202 },
203
204 .error_union,
205 .switch_range,
206 => {
207 const infix = datas[node];
208 try walkExpression(w, infix.lhs);
209 return walkExpression(w, infix.rhs);
210 },
211 .for_range => {
212 const infix = datas[node];
213 try walkExpression(w, infix.lhs);
214 if (infix.rhs != 0) {
215 return walkExpression(w, infix.rhs);
216 }
217 },
218
219 .add,
220 .add_wrap,
221 .add_sat,
222 .array_cat,
223 .array_mult,
224 .assign,
225 .assign_bit_and,
226 .assign_bit_or,
227 .assign_shl,
228 .assign_shl_sat,
229 .assign_shr,
230 .assign_bit_xor,
231 .assign_div,
232 .assign_sub,
233 .assign_sub_wrap,
234 .assign_sub_sat,
235 .assign_mod,
236 .assign_add,
237 .assign_add_wrap,
238 .assign_add_sat,
239 .assign_mul,
240 .assign_mul_wrap,
241 .assign_mul_sat,
242 .bang_equal,
243 .bit_and,
244 .bit_or,
245 .shl,
246 .shl_sat,
247 .shr,
248 .bit_xor,
249 .bool_and,
250 .bool_or,
251 .div,
252 .equal_equal,
253 .greater_or_equal,
254 .greater_than,
255 .less_or_equal,
256 .less_than,
257 .merge_error_sets,
258 .mod,
259 .mul,
260 .mul_wrap,
261 .mul_sat,
262 .sub,
263 .sub_wrap,
264 .sub_sat,
265 .@"orelse",
266 => {
267 const infix = datas[node];
268 try walkExpression(w, infix.lhs);
269 try walkExpression(w, infix.rhs);
270 },
271
272 .assign_destructure => {
273 const lhs_count = ast.extra_data[datas[node].lhs];
274 assert(lhs_count > 1);
275 const lhs_exprs = ast.extra_data[datas[node].lhs + 1 ..][0..lhs_count];
276 const rhs = datas[node].rhs;
277
278 for (lhs_exprs) |lhs_node| {
279 switch (node_tags[lhs_node]) {
280 .global_var_decl,
281 .local_var_decl,
282 .simple_var_decl,
283 .aligned_var_decl,
284 => try walkLocalVarDecl(w, ast.fullVarDecl(lhs_node).?),
285
286 else => try walkExpression(w, lhs_node),
287 }
288 }
289 return walkExpression(w, rhs);
290 },
291
292 .bit_not,
293 .bool_not,
294 .negation,
295 .negation_wrap,
296 .optional_type,
297 .address_of,
298 => {
299 return walkExpression(w, datas[node].lhs);
300 },
301
302 .@"try",
303 .@"resume",
304 .@"await",
305 => {
306 return walkExpression(w, datas[node].lhs);
307 },
308
309 .array_type,
310 .array_type_sentinel,
311 => {},
312
313 .ptr_type_aligned,
314 .ptr_type_sentinel,
315 .ptr_type,
316 .ptr_type_bit_range,
317 => {},
318
319 .array_init_one,
320 .array_init_one_comma,
321 .array_init_dot_two,
322 .array_init_dot_two_comma,
323 .array_init_dot,
324 .array_init_dot_comma,
325 .array_init,
326 .array_init_comma,
327 => {
328 var elements: [2]Ast.Node.Index = undefined;
329 return walkArrayInit(w, ast.fullArrayInit(&elements, node).?);
330 },
331
332 .struct_init_one,
333 .struct_init_one_comma,
334 .struct_init_dot_two,
335 .struct_init_dot_two_comma,
336 .struct_init_dot,
337 .struct_init_dot_comma,
338 .struct_init,
339 .struct_init_comma,
340 => {
341 var buf: [2]Ast.Node.Index = undefined;
342 return walkStructInit(w, node, ast.fullStructInit(&buf, node).?);
343 },
344
345 .call_one,
346 .call_one_comma,
347 .async_call_one,
348 .async_call_one_comma,
349 .call,
350 .call_comma,
351 .async_call,
352 .async_call_comma,
353 => {
354 var buf: [1]Ast.Node.Index = undefined;
355 return walkCall(w, ast.fullCall(&buf, node).?);
356 },
357
358 .array_access => {
359 const suffix = datas[node];
360 try walkExpression(w, suffix.lhs);
361 try walkExpression(w, suffix.rhs);
362 },
363
364 .slice_open, .slice, .slice_sentinel => return walkSlice(w, node, ast.fullSlice(node).?),
365
366 .deref => {
367 try walkExpression(w, datas[node].lhs);
368 },
369
370 .unwrap_optional => {
371 try walkExpression(w, datas[node].lhs);
372 },
373
374 .@"break" => {
375 const label_token = datas[node].lhs;
376 const target = datas[node].rhs;
377 if (label_token == 0 and target == 0) {
378 // no expressions
379 } else if (label_token == 0 and target != 0) {
380 try walkExpression(w, target);
381 } else if (label_token != 0 and target == 0) {
382 try walkIdentifier(w, label_token);
383 } else if (label_token != 0 and target != 0) {
384 try walkExpression(w, target);
385 }
386 },
387
388 .@"continue" => {
389 const label = datas[node].lhs;
390 if (label != 0) {
391 return walkIdentifier(w, label); // label
392 }
393 },
394
395 .@"return" => {
396 if (datas[node].lhs != 0) {
397 try walkExpression(w, datas[node].lhs);
398 }
399 },
400
401 .grouped_expression => {
402 try walkExpression(w, datas[node].lhs);
403 },
404
405 .container_decl,
406 .container_decl_trailing,
407 .container_decl_arg,
408 .container_decl_arg_trailing,
409 .container_decl_two,
410 .container_decl_two_trailing,
411 .tagged_union,
412 .tagged_union_trailing,
413 .tagged_union_enum_tag,
414 .tagged_union_enum_tag_trailing,
415 .tagged_union_two,
416 .tagged_union_two_trailing,
417 => {
418 var buf: [2]Ast.Node.Index = undefined;
419 return walkContainerDecl(w, node, ast.fullContainerDecl(&buf, node).?);
420 },
421
422 .error_set_decl => {
423 const error_token = main_tokens[node];
424 const lbrace = error_token + 1;
425 const rbrace = datas[node].rhs;
426
427 var i = lbrace + 1;
428 while (i < rbrace) : (i += 1) {
429 switch (token_tags[i]) {
430 .doc_comment => unreachable, // TODO
431 .identifier => try walkIdentifier(w, i),
432 .comma => {},
433 else => unreachable,
434 }
435 }
436 },
437
438 .builtin_call_two, .builtin_call_two_comma => {
439 if (datas[node].lhs == 0) {
440 return walkBuiltinCall(w, main_tokens[node], &.{});
441 } else if (datas[node].rhs == 0) {
442 return walkBuiltinCall(w, main_tokens[node], &.{datas[node].lhs});
443 } else {
444 return walkBuiltinCall(w, main_tokens[node], &.{ datas[node].lhs, datas[node].rhs });
445 }
446 },
447 .builtin_call, .builtin_call_comma => {
448 const params = ast.extra_data[datas[node].lhs..datas[node].rhs];
449 return walkBuiltinCall(w, main_tokens[node], params);
450 },
451
452 .fn_proto_simple,
453 .fn_proto_multi,
454 .fn_proto_one,
455 .fn_proto,
456 => {
457 var buf: [1]Ast.Node.Index = undefined;
458 return walkFnProto(w, ast.fullFnProto(&buf, node).?);
459 },
460
461 .anyframe_type => {
462 if (datas[node].rhs != 0) {
463 return walkExpression(w, datas[node].rhs);
464 }
465 },
466
467 .@"switch",
468 .switch_comma,
469 => {
470 const condition = datas[node].lhs;
471 const extra = ast.extraData(datas[node].rhs, Ast.Node.SubRange);
472 const cases = ast.extra_data[extra.start..extra.end];
473
474 try walkExpression(w, condition); // condition expression
475 try walkExpressions(w, cases);
476 },
477
478 .switch_case_one,
479 .switch_case_inline_one,
480 .switch_case,
481 .switch_case_inline,
482 => return walkSwitchCase(w, ast.fullSwitchCase(node).?),
483
484 .while_simple,
485 .while_cont,
486 .@"while",
487 => return walkWhile(w, ast.fullWhile(node).?),
488
489 .for_simple,
490 .@"for",
491 => return walkFor(w, ast.fullFor(node).?),
492
493 .if_simple,
494 .@"if",
495 => return walkIf(w, ast.fullIf(node).?),
496
497 .asm_simple,
498 .@"asm",
499 => return walkAsm(w, ast.fullAsm(node).?),
500
501 .enum_literal => {
502 return walkIdentifier(w, main_tokens[node]); // name
503 },
504
505 .fn_decl => unreachable,
506 .container_field => unreachable,
507 .container_field_init => unreachable,
508 .container_field_align => unreachable,
509 .root => unreachable,
510 .global_var_decl => unreachable,
511 .local_var_decl => unreachable,
512 .simple_var_decl => unreachable,
513 .aligned_var_decl => unreachable,
514 .@"usingnamespace" => unreachable,
515 .test_decl => unreachable,
516 .asm_output => unreachable,
517 .asm_input => unreachable,
518 }
519}
520
521fn walkGlobalVarDecl(w: *Walk, decl_node: Ast.Node.Index, var_decl: Ast.full.VarDecl) Error!void {
522 _ = decl_node;
523
524 if (var_decl.ast.type_node != 0) {
525 try walkExpression(w, var_decl.ast.type_node);
526 }
527
528 if (var_decl.ast.align_node != 0) {
529 try walkExpression(w, var_decl.ast.align_node);
530 }
531
532 if (var_decl.ast.addrspace_node != 0) {
533 try walkExpression(w, var_decl.ast.addrspace_node);
534 }
535
536 if (var_decl.ast.section_node != 0) {
537 try walkExpression(w, var_decl.ast.section_node);
538 }
539
540 assert(var_decl.ast.init_node != 0);
541
542 return walkExpression(w, var_decl.ast.init_node);
543}
544
545fn walkLocalVarDecl(w: *Walk, var_decl: Ast.full.VarDecl) Error!void {
546 try walkIdentifierNew(w, var_decl.ast.mut_token + 1); // name
547
548 if (var_decl.ast.type_node != 0) {
549 try walkExpression(w, var_decl.ast.type_node);
550 }
551
552 if (var_decl.ast.align_node != 0) {
553 try walkExpression(w, var_decl.ast.align_node);
554 }
555
556 if (var_decl.ast.addrspace_node != 0) {
557 try walkExpression(w, var_decl.ast.addrspace_node);
558 }
559
560 if (var_decl.ast.section_node != 0) {
561 try walkExpression(w, var_decl.ast.section_node);
562 }
563
564 assert(var_decl.ast.init_node != 0);
565 if (!isUndefinedIdent(w.ast, var_decl.ast.init_node)) {
566 try w.transformations.append(.{ .replace_with_undef = var_decl.ast.init_node });
567 }
568
569 return walkExpression(w, var_decl.ast.init_node);
570}
571
572fn walkContainerField(w: *Walk, field: Ast.full.ContainerField) Error!void {
573 if (field.ast.type_expr != 0) {
574 try walkExpression(w, field.ast.type_expr); // type
575 }
576 if (field.ast.align_expr != 0) {
577 try walkExpression(w, field.ast.align_expr); // alignment
578 }
579 try walkExpression(w, field.ast.value_expr); // value
580}
581
582fn walkBlock(
583 w: *Walk,
584 block_node: Ast.Node.Index,
585 statements: []const Ast.Node.Index,
586) Error!void {
587 _ = block_node;
588 const ast = w.ast;
589 const node_tags = ast.nodes.items(.tag);
590
591 for (statements) |stmt| {
592 switch (node_tags[stmt]) {
593 .global_var_decl,
594 .local_var_decl,
595 .simple_var_decl,
596 .aligned_var_decl,
597 => try walkLocalVarDecl(w, ast.fullVarDecl(stmt).?),
598
599 else => try walkExpression(w, stmt),
600 }
601 }
602}
603
604fn walkArrayType(w: *Walk, array_type: Ast.full.ArrayType) Error!void {
605 try walkExpression(w, array_type.ast.elem_count);
606 if (array_type.ast.sentinel != 0) {
607 try walkExpression(w, array_type.ast.sentinel);
608 }
609 return walkExpression(w, array_type.ast.elem_type);
610}
611
612fn walkArrayInit(w: *Walk, array_init: Ast.full.ArrayInit) Error!void {
613 if (array_init.ast.type_expr != 0) {
614 try walkExpression(w, array_init.ast.type_expr); // T
615 }
616 for (array_init.ast.elements) |elem_init| {
617 try walkExpression(w, elem_init);
618 }
619}
620
621fn walkStructInit(
622 w: *Walk,
623 struct_node: Ast.Node.Index,
624 struct_init: Ast.full.StructInit,
625) Error!void {
626 _ = struct_node;
627 if (struct_init.ast.type_expr != 0) {
628 try walkExpression(w, struct_init.ast.type_expr); // T
629 }
630 for (struct_init.ast.fields) |field_init| {
631 try walkExpression(w, field_init);
632 }
633}
634
635fn walkCall(w: *Walk, call: Ast.full.Call) Error!void {
636 try walkExpression(w, call.ast.fn_expr);
637 try walkParamList(w, call.ast.params);
638}
639
640fn walkSlice(
641 w: *Walk,
642 slice_node: Ast.Node.Index,
643 slice: Ast.full.Slice,
644) Error!void {
645 _ = slice_node;
646 try walkExpression(w, slice.ast.sliced);
647 try walkExpression(w, slice.ast.start);
648 if (slice.ast.end != 0) {
649 try walkExpression(w, slice.ast.end);
650 }
651 if (slice.ast.sentinel != 0) {
652 try walkExpression(w, slice.ast.sentinel);
653 }
654}
655
656fn walkIdentifier(w: *Walk, name_ident: Ast.TokenIndex) Error!void {
657 const ast = w.ast;
658 const token_tags = ast.tokens.items(.tag);
659 assert(token_tags[name_ident] == .identifier);
660 const name_bytes = ast.tokenSlice(name_ident);
661 _ = w.unreferenced_globals.swapRemove(name_bytes);
662}
663
664fn walkIdentifierNew(w: *Walk, name_ident: Ast.TokenIndex) Error!void {
665 _ = w;
666 _ = name_ident;
667}
668
669fn walkContainerDecl(
670 w: *Walk,
671 container_decl_node: Ast.Node.Index,
672 container_decl: Ast.full.ContainerDecl,
673) Error!void {
674 _ = container_decl_node;
675 if (container_decl.ast.arg != 0) {
676 try walkExpression(w, container_decl.ast.arg);
677 }
678 try walkMembers(w, container_decl.ast.members);
679}
680
681fn walkBuiltinCall(
682 w: *Walk,
683 builtin_token: Ast.TokenIndex,
684 params: []const Ast.Node.Index,
685) Error!void {
686 _ = builtin_token;
687 for (params) |param_node| {
688 try walkExpression(w, param_node);
689 }
690}
691
692fn walkFnProto(w: *Walk, fn_proto: Ast.full.FnProto) Error!void {
693 const ast = w.ast;
694
695 {
696 var it = fn_proto.iterate(ast);
697 while (it.next()) |param| {
698 if (param.type_expr != 0) {
699 try walkExpression(w, param.type_expr);
700 }
701 }
702 }
703
704 if (fn_proto.ast.align_expr != 0) {
705 try walkExpression(w, fn_proto.ast.align_expr);
706 }
707
708 if (fn_proto.ast.addrspace_expr != 0) {
709 try walkExpression(w, fn_proto.ast.addrspace_expr);
710 }
711
712 if (fn_proto.ast.section_expr != 0) {
713 try walkExpression(w, fn_proto.ast.section_expr);
714 }
715
716 if (fn_proto.ast.callconv_expr != 0) {
717 try walkExpression(w, fn_proto.ast.callconv_expr);
718 }
719
720 try walkExpression(w, fn_proto.ast.return_type);
721}
722
723fn walkExpressions(w: *Walk, expressions: []const Ast.Node.Index) Error!void {
724 for (expressions) |expression| {
725 try walkExpression(w, expression);
726 }
727}
728
729fn walkSwitchCase(w: *Walk, switch_case: Ast.full.SwitchCase) Error!void {
730 for (switch_case.ast.values) |value_expr| {
731 try walkExpression(w, value_expr);
732 }
733 try walkExpression(w, switch_case.ast.target_expr);
734}
735
736fn walkWhile(w: *Walk, while_node: Ast.full.While) Error!void {
737 try walkExpression(w, while_node.ast.cond_expr); // condition
738
739 if (while_node.ast.cont_expr != 0) {
740 try walkExpression(w, while_node.ast.cont_expr);
741 }
742
743 try walkExpression(w, while_node.ast.cond_expr); // condition
744
745 if (while_node.ast.then_expr != 0) {
746 try walkExpression(w, while_node.ast.then_expr);
747 }
748 if (while_node.ast.else_expr != 0) {
749 try walkExpression(w, while_node.ast.else_expr);
750 }
751}
752
753fn walkFor(w: *Walk, for_node: Ast.full.For) Error!void {
754 try walkParamList(w, for_node.ast.inputs);
755 if (for_node.ast.then_expr != 0) {
756 try walkExpression(w, for_node.ast.then_expr);
757 }
758 if (for_node.ast.else_expr != 0) {
759 try walkExpression(w, for_node.ast.else_expr);
760 }
761}
762
763fn walkIf(w: *Walk, if_node: Ast.full.If) Error!void {
764 try walkExpression(w, if_node.ast.cond_expr); // condition
765
766 if (if_node.ast.then_expr != 0) {
767 try walkExpression(w, if_node.ast.then_expr);
768 }
769 if (if_node.ast.else_expr != 0) {
770 try walkExpression(w, if_node.ast.else_expr);
771 }
772}
773
774fn walkAsm(w: *Walk, asm_node: Ast.full.Asm) Error!void {
775 try walkExpression(w, asm_node.ast.template);
776 for (asm_node.ast.items) |item| {
777 try walkExpression(w, item);
778 }
779}
780
781fn walkParamList(w: *Walk, params: []const Ast.Node.Index) Error!void {
782 for (params) |param_node| {
783 try walkExpression(w, param_node);
784 }
785}
786
787/// Check if it is already gutted (i.e. its body replaced with `@trap()`).
788fn isFnBodyGutted(ast: *const Ast, body_node: Ast.Node.Index) bool {
789 // skip over discards
790 const node_tags = ast.nodes.items(.tag);
791 const datas = ast.nodes.items(.data);
792 var statements_buf: [2]Ast.Node.Index = undefined;
793 const statements = switch (node_tags[body_node]) {
794 .block_two,
795 .block_two_semicolon,
796 => blk: {
797 statements_buf[0..2].* = .{ datas[body_node].lhs, datas[body_node].rhs };
798 break :blk if (datas[body_node].lhs == 0)
799 statements_buf[0..0]
800 else if (datas[body_node].rhs == 0)
801 statements_buf[0..1]
802 else
803 statements_buf[0..2];
804 },
805
806 .block,
807 .block_semicolon,
808 => ast.extra_data[datas[body_node].lhs..datas[body_node].rhs],
809
810 else => return false,
811 };
812 var i: usize = 0;
813 while (i < statements.len) : (i += 1) {
814 switch (categorizeStmt(ast, statements[i])) {
815 .discard_identifier => continue,
816 .trap_call => return i + 1 == statements.len,
817 else => return false,
818 }
819 }
820 return false;
821}
822
823const StmtCategory = enum {
824 discard_identifier,
825 trap_call,
826 other,
827};
828
829fn categorizeStmt(ast: *const Ast, stmt: Ast.Node.Index) StmtCategory {
830 const node_tags = ast.nodes.items(.tag);
831 const datas = ast.nodes.items(.data);
832 const main_tokens = ast.nodes.items(.main_token);
833 switch (node_tags[stmt]) {
834 .builtin_call_two, .builtin_call_two_comma => {
835 if (datas[stmt].lhs == 0) {
836 return categorizeBuiltinCall(ast, main_tokens[stmt], &.{});
837 } else if (datas[stmt].rhs == 0) {
838 return categorizeBuiltinCall(ast, main_tokens[stmt], &.{datas[stmt].lhs});
839 } else {
840 return categorizeBuiltinCall(ast, main_tokens[stmt], &.{ datas[stmt].lhs, datas[stmt].rhs });
841 }
842 },
843 .builtin_call, .builtin_call_comma => {
844 const params = ast.extra_data[datas[stmt].lhs..datas[stmt].rhs];
845 return categorizeBuiltinCall(ast, main_tokens[stmt], params);
846 },
847 .assign => {
848 const infix = datas[stmt];
849 if (isDiscardIdent(ast, infix.lhs) and node_tags[infix.rhs] == .identifier)
850 return .discard_identifier;
851 return .other;
852 },
853 else => return .other,
854 }
855}
856
857fn categorizeBuiltinCall(
858 ast: *const Ast,
859 builtin_token: Ast.TokenIndex,
860 params: []const Ast.Node.Index,
861) StmtCategory {
862 if (params.len != 0) return .other;
863 const name_bytes = ast.tokenSlice(builtin_token);
864 if (std.mem.eql(u8, name_bytes, "@trap"))
865 return .trap_call;
866 return .other;
867}
868
869fn isDiscardIdent(ast: *const Ast, node: Ast.Node.Index) bool {
870 const node_tags = ast.nodes.items(.tag);
871 const main_tokens = ast.nodes.items(.main_token);
872 switch (node_tags[node]) {
873 .identifier => {
874 const token_index = main_tokens[node];
875 const name_bytes = ast.tokenSlice(token_index);
876 return std.mem.eql(u8, name_bytes, "_");
877 },
878 else => return false,
879 }
880}
881
882fn isUndefinedIdent(ast: *const Ast, node: Ast.Node.Index) bool {
883 const node_tags = ast.nodes.items(.tag);
884 const main_tokens = ast.nodes.items(.main_token);
885 switch (node_tags[node]) {
886 .identifier => {
887 const token_index = main_tokens[node];
888 const name_bytes = ast.tokenSlice(token_index);
889 return std.mem.eql(u8, name_bytes, "undefined");
890 },
891 else => return false,
892 }
893}
stage1/config.zig.in+1
...@@ -14,3 +14,4 @@ pub const skip_non_native = false;...@@ -14,3 +14,4 @@ pub const skip_non_native = false;
14pub const only_c = false;14pub const only_c = false;
15pub const force_gpa = false;15pub const force_gpa = false;
16pub const only_core_functionality = true;16pub const only_core_functionality = true;
17pub const only_reduce = false;