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 {
3333 const skip_install_langref = b.option(bool, "no-langref", "skip copying of langref to the installation prefix") orelse skip_install_lib_files;
3434 const skip_install_autodocs = b.option(bool, "no-autodocs", "skip copying of standard library autodocs to the installation prefix") orelse skip_install_lib_files;
3535 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
3738 const docgen_exe = b.addExecutable(.{
3839 .name = "docgen",
......@@ -236,6 +237,7 @@ pub fn build(b: *std.Build) !void {
236237 exe_options.addOption(bool, "force_gpa", force_gpa);
237238 exe_options.addOption(bool, "only_c", only_c);
238239 exe_options.addOption(bool, "only_core_functionality", only_c);
240 exe_options.addOption(bool, "only_reduce", only_reduce);
239241
240242 if (link_libc) {
241243 exe.linkLibC();
......@@ -391,6 +393,7 @@ pub fn build(b: *std.Build) !void {
391393 test_cases_options.addOption(bool, "force_gpa", force_gpa);
392394 test_cases_options.addOption(bool, "only_c", only_c);
393395 test_cases_options.addOption(bool, "only_core_functionality", true);
396 test_cases_options.addOption(bool, "only_reduce", false);
394397 test_cases_options.addOption(bool, "enable_qemu", b.enable_qemu);
395398 test_cases_options.addOption(bool, "enable_wine", b.enable_wine);
396399 test_cases_options.addOption(bool, "enable_wasmtime", b.enable_wasmtime);
......@@ -549,6 +552,7 @@ fn addWasiUpdateStep(b: *std.Build, version: [:0]const u8) !void {
549552 exe_options.addOption(bool, "enable_tracy_allocation", false);
550553 exe_options.addOption(bool, "value_tracing", false);
551554 exe_options.addOption(bool, "only_core_functionality", true);
555 exe_options.addOption(bool, "only_reduce", false);
552556
553557 const run_opt = b.addSystemCommand(&.{
554558 "wasm-opt",
lib/build_runner.zig+1-1
......@@ -203,7 +203,7 @@ pub fn main() !void {
203203 usageAndErr(builder, false, stderr_stream);
204204 };
205205 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", .{
207207 next_arg, @errorName(err),
208208 });
209209 process.exit(1);
lib/std/bit_set.zig+12
......@@ -859,6 +859,18 @@ pub const DynamicBitSetUnmanaged = struct {
859859 self.masks[maskIndex(index)] &= ~maskBit(index);
860860 }
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
862874 /// Flips a specific bit in the bit set
863875 pub fn toggle(self: *Self, index: usize) void {
864876 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 {
113113 var buffer = std.ArrayList(u8).init(gpa);
114114 defer buffer.deinit();
115115
116 try tree.renderToArrayList(&buffer);
116 try tree.renderToArrayList(&buffer, .{});
117117 return buffer.toOwnedSlice();
118118}
119119
120pub fn renderToArrayList(tree: Ast, buffer: *std.ArrayList(u8)) RenderError!void {
121 return @import("./render.zig").renderTree(buffer, tree);
120pub const Fixups = private_render.Fixups;
121
122pub fn renderToArrayList(tree: Ast, buffer: *std.ArrayList(u8), fixups: Fixups) RenderError!void {
123 return @import("./render.zig").renderTree(buffer, tree, fixups);
122124}
123125
124126/// Returns an extra offset for column and byte offset of errors that
......@@ -3530,6 +3532,7 @@ const Token = std.zig.Token;
35303532const Ast = @This();
35313533const Allocator = std.mem.Allocator;
35323534const Parse = @import("Parse.zig");
3535const private_render = @import("./render.zig");
35333536
35343537test {
35353538 testing.refAllDecls(@This());
lib/std/zig/render.zig+792-660
......@@ -14,49 +14,96 @@ pub const Error = Ast.RenderError;
1414
1515const 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 {
1861 assert(tree.errors.len == 0); // Cannot render an invalid tree.
1962 var auto_indenting_stream = Ais{
2063 .indent_delta = indent_delta,
2164 .underlying_writer = buffer.writer(),
2265 };
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
2573 // Render all the line comments at the beginning of the file.
2674 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
2977 if (tree.tokens.items(.tag)[0] == .container_doc_comment) {
30 try renderContainerDocComments(ais, tree, 0);
78 try renderContainerDocComments(&r, 0);
3179 }
3280
3381 if (tree.mode == .zon) {
3482 try renderExpression(
35 buffer.allocator,
36 ais,
37 tree,
83 &r,
3884 tree.nodes.items(.data)[0].lhs,
3985 .newline,
4086 );
4187 } else {
42 try renderMembers(buffer.allocator, ais, tree, tree.rootDecls());
88 try renderMembers(&r, tree.rootDecls());
4389 }
4490
45 if (ais.disabled_offset) |disabled_offset| {
46 try writeFixingWhitespace(ais.underlying_writer, tree.source[disabled_offset..]);
91 if (auto_indenting_stream.disabled_offset) |disabled_offset| {
92 try writeFixingWhitespace(auto_indenting_stream.underlying_writer, tree.source[disabled_offset..]);
4793 }
4894}
4995
5096/// 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;
5299 if (members.len == 0) return;
53100 const container: Container = for (members) |member| {
54101 if (tree.fullContainerField(member)) |field| if (!field.ast.tuple_like) break .other;
55102 } else .tuple;
56 try renderMember(gpa, ais, tree, container, members[0], .newline);
103 try renderMember(r, container, members[0], .newline);
57104 for (members[1..]) |member| {
58 try renderExtraNewline(ais, tree, member);
59 try renderMember(gpa, ais, tree, container, member, .newline);
105 try renderExtraNewline(r, member);
106 try renderMember(r, container, member, .newline);
60107 }
61108}
62109
......@@ -67,17 +114,18 @@ const Container = enum {
67114};
68115
69116fn renderMember(
70 gpa: Allocator,
71 ais: *Ais,
72 tree: Ast,
117 r: *Render,
73118 container: Container,
74119 decl: Ast.Node.Index,
75120 space: Space,
76121) Error!void {
122 const tree = r.tree;
123 const ais = r.ais;
77124 const token_tags = tree.tokens.items(.tag);
78125 const main_tokens = tree.nodes.items(.main_token);
79126 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));
81129 switch (tree.nodes.items(.tag)[decl]) {
82130 .fn_decl => {
83131 // Some examples:
......@@ -105,7 +153,7 @@ fn renderMember(
105153 }
106154 }
107155 while (i < fn_token) : (i += 1) {
108 try renderToken(ais, tree, i, .space);
156 try renderToken(r, i, .space);
109157 }
110158 switch (tree.nodes.items(.tag)[fn_proto]) {
111159 .fn_proto_one, .fn_proto => {
......@@ -123,8 +171,20 @@ fn renderMember(
123171 else => unreachable,
124172 }
125173 assert(datas[decl].rhs != 0);
126 try renderExpression(gpa, ais, tree, fn_proto, .space);
127 return renderExpression(gpa, ais, tree, datas[decl].rhs, space);
174 try renderExpression(r, fn_proto, .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 }
128188 },
129189 .fn_proto_simple,
130190 .fn_proto_multi,
......@@ -153,47 +213,47 @@ fn renderMember(
153213 }
154214 }
155215 while (i < fn_token) : (i += 1) {
156 try renderToken(ais, tree, i, .space);
216 try renderToken(r, i, .space);
157217 }
158 try renderExpression(gpa, ais, tree, decl, .none);
159 return renderToken(ais, tree, tree.lastToken(decl) + 1, space); // semicolon
218 try renderExpression(r, decl, .none);
219 return renderToken(r, tree.lastToken(decl) + 1, space); // semicolon
160220 },
161221
162222 .@"usingnamespace" => {
163223 const main_token = main_tokens[decl];
164224 const expr = datas[decl].lhs;
165225 if (main_token > 0 and token_tags[main_token - 1] == .keyword_pub) {
166 try renderToken(ais, tree, main_token - 1, .space); // pub
226 try renderToken(r, main_token - 1, .space); // pub
167227 }
168 try renderToken(ais, tree, main_token, .space); // usingnamespace
169 try renderExpression(gpa, ais, tree, expr, .none);
170 return renderToken(ais, tree, tree.lastToken(expr) + 1, space); // ;
228 try renderToken(r, main_token, .space); // usingnamespace
229 try renderExpression(r, expr, .none);
230 return renderToken(r, tree.lastToken(expr) + 1, space); // ;
171231 },
172232
173233 .global_var_decl,
174234 .local_var_decl,
175235 .simple_var_decl,
176236 .aligned_var_decl,
177 => return renderVarDecl(gpa, ais, tree, tree.fullVarDecl(decl).?, false, .semicolon),
237 => return renderVarDecl(r, tree.fullVarDecl(decl).?, false, .semicolon),
178238
179239 .test_decl => {
180240 const test_token = main_tokens[decl];
181 try renderToken(ais, tree, test_token, .space);
241 try renderToken(r, test_token, .space);
182242 const test_name_tag = token_tags[test_token + 1];
183243 switch (test_name_tag) {
184 .string_literal => try renderToken(ais, tree, test_token + 1, .space),
185 .identifier => try renderIdentifier(ais, tree, test_token + 1, .space, .preserve_when_shadowing),
244 .string_literal => try renderToken(r, test_token + 1, .space),
245 .identifier => try renderIdentifier(r, test_token + 1, .space, .preserve_when_shadowing),
186246 else => {},
187247 }
188 try renderExpression(gpa, ais, tree, datas[decl].rhs, space);
248 try renderExpression(r, datas[decl].rhs, space);
189249 },
190250
191251 .container_field_init,
192252 .container_field_align,
193253 .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
198258 .root => unreachable,
199259 else => unreachable,
......@@ -201,24 +261,31 @@ fn renderMember(
201261}
202262
203263/// 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 {
205265 if (expressions.len == 0) return;
206 try renderExpression(gpa, ais, tree, expressions[0], space);
266 try renderExpression(r, expressions[0], space);
207267 for (expressions[1..]) |expression| {
208 try renderExtraNewline(ais, tree, expression);
209 try renderExpression(gpa, ais, tree, expression, space);
268 try renderExtraNewline(r, expression);
269 try renderExpression(r, expression, space);
210270 }
211271}
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;
214276 const token_tags = tree.tokens.items(.tag);
215277 const main_tokens = tree.nodes.items(.main_token);
216278 const node_tags = tree.nodes.items(.tag);
217279 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 }
218285 switch (node_tags[node]) {
219286 .identifier => {
220287 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);
222289 },
223290
224291 .number_literal,
......@@ -226,29 +293,29 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,
226293 .unreachable_literal,
227294 .anyframe_literal,
228295 .string_literal,
229 => return renderToken(ais, tree, main_tokens[node], space),
296 => return renderToken(r, main_tokens[node], space),
230297
231298 .multiline_string_literal => {
232299 var locked_indents = ais.lockOneShotIndent();
233300 try ais.maybeInsertNewline();
234301
235302 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
238305 while (locked_indents > 0) : (locked_indents -= 1) ais.popIndent();
239306
240307 switch (space) {
241308 .none, .space, .newline, .skip => {},
242 .semicolon => if (token_tags[i] == .semicolon) try renderToken(ais, tree, i, .newline),
243 .comma => if (token_tags[i] == .comma) try renderToken(ais, tree, i, .newline),
244 .comma_space => if (token_tags[i] == .comma) try renderToken(ais, tree, i, .space),
309 .semicolon => if (token_tags[i] == .semicolon) try renderToken(r, i, .newline),
310 .comma => if (token_tags[i] == .comma) try renderToken(r, i, .newline),
311 .comma_space => if (token_tags[i] == .comma) try renderToken(r, i, .space),
245312 }
246313 },
247314
248315 .error_value => {
249 try renderToken(ais, tree, main_tokens[node], .none);
250 try renderToken(ais, tree, main_tokens[node] + 1, .none);
251 return renderIdentifier(ais, tree, main_tokens[node] + 2, space, .eagerly_unquote);
316 try renderToken(r, main_tokens[node], .none);
317 try renderToken(r, main_tokens[node] + 1, .none);
318 return renderIdentifier(r, main_tokens[node] + 2, space, .eagerly_unquote);
252319 },
253320
254321 .block_two,
......@@ -256,18 +323,18 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,
256323 => {
257324 const statements = [2]Ast.Node.Index{ datas[node].lhs, datas[node].rhs };
258325 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);
260327 } 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);
262329 } else {
263 return renderBlock(gpa, ais, tree, node, statements[0..2], space);
330 return renderBlock(r, node, statements[0..2], space);
264331 }
265332 },
266333 .block,
267334 .block_semicolon,
268335 => {
269336 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);
271338 },
272339
273340 .@"errdefer" => {
......@@ -275,33 +342,33 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,
275342 const payload_token = datas[node].lhs;
276343 const expr = datas[node].rhs;
277344
278 try renderToken(ais, tree, defer_token, .space);
345 try renderToken(r, defer_token, .space);
279346 if (payload_token != 0) {
280 try renderToken(ais, tree, payload_token - 1, .none); // |
281 try renderIdentifier(ais, tree, payload_token, .none, .preserve_when_shadowing); // identifier
282 try renderToken(ais, tree, payload_token + 1, .space); // |
347 try renderToken(r, payload_token - 1, .none); // |
348 try renderIdentifier(r, payload_token, .none, .preserve_when_shadowing); // identifier
349 try renderToken(r, payload_token + 1, .space); // |
283350 }
284 return renderExpression(gpa, ais, tree, expr, space);
351 return renderExpression(r, expr, space);
285352 },
286353
287354 .@"defer" => {
288355 const defer_token = main_tokens[node];
289356 const expr = datas[node].rhs;
290 try renderToken(ais, tree, defer_token, .space);
291 return renderExpression(gpa, ais, tree, expr, space);
357 try renderToken(r, defer_token, .space);
358 return renderExpression(r, expr, space);
292359 },
293360 .@"comptime", .@"nosuspend" => {
294361 const comptime_token = main_tokens[node];
295362 const block = datas[node].lhs;
296 try renderToken(ais, tree, comptime_token, .space);
297 return renderExpression(gpa, ais, tree, block, space);
363 try renderToken(r, comptime_token, .space);
364 return renderExpression(r, block, space);
298365 },
299366
300367 .@"suspend" => {
301368 const suspend_token = main_tokens[node];
302369 const body = datas[node].lhs;
303 try renderToken(ais, tree, suspend_token, .space);
304 return renderExpression(gpa, ais, tree, body, space);
370 try renderToken(r, suspend_token, .space);
371 return renderExpression(r, body, space);
305372 },
306373
307374 .@"catch" => {
......@@ -311,27 +378,27 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,
311378 const same_line = tree.tokensOnSameLine(main_token, fallback_first);
312379 const after_op_space = if (same_line) Space.space else Space.newline;
313380
314 try renderExpression(gpa, ais, tree, datas[node].lhs, .space); // target
381 try renderExpression(r, datas[node].lhs, .space); // target
315382
316383 if (token_tags[fallback_first - 1] == .pipe) {
317 try renderToken(ais, tree, main_token, .space); // catch keyword
318 try renderToken(ais, tree, main_token + 1, .none); // pipe
319 try renderIdentifier(ais, tree, main_token + 2, .none, .preserve_when_shadowing); // payload identifier
320 try renderToken(ais, tree, main_token + 3, after_op_space); // pipe
384 try renderToken(r, main_token, .space); // catch keyword
385 try renderToken(r, main_token + 1, .none); // pipe
386 try renderIdentifier(r, main_token + 2, .none, .preserve_when_shadowing); // payload identifier
387 try renderToken(r, main_token + 3, after_op_space); // pipe
321388 } else {
322389 assert(token_tags[fallback_first - 1] == .keyword_catch);
323 try renderToken(ais, tree, main_token, after_op_space); // catch keyword
390 try renderToken(r, main_token, after_op_space); // catch keyword
324391 }
325392
326393 ais.pushIndentOneShot();
327 try renderExpression(gpa, ais, tree, datas[node].rhs, space); // fallback
394 try renderExpression(r, datas[node].rhs, space); // fallback
328395 },
329396
330397 .field_access => {
331398 const main_token = main_tokens[node];
332399 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
336403 // Allow a line break between the lhs and the dot if the lhs and rhs
337404 // are on different lines.
......@@ -342,7 +409,7 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,
342409 ais.pushIndentOneShot();
343410 }
344411
345 try renderToken(ais, tree, main_token, .none); // .
412 try renderToken(r, main_token, .none); // .
346413
347414 // This check ensures that zag() is indented in the following example:
348415 // const x = foo
......@@ -353,25 +420,25 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,
353420 ais.pushIndentOneShot();
354421 }
355422
356 return renderIdentifier(ais, tree, field_access.rhs, space, .eagerly_unquote); // field
423 return renderIdentifier(r, field_access.rhs, space, .eagerly_unquote); // field
357424 },
358425
359426 .error_union,
360427 .switch_range,
361428 => {
362429 const infix = datas[node];
363 try renderExpression(gpa, ais, tree, infix.lhs, .none);
364 try renderToken(ais, tree, main_tokens[node], .none);
365 return renderExpression(gpa, ais, tree, infix.rhs, space);
430 try renderExpression(r, infix.lhs, .none);
431 try renderToken(r, main_tokens[node], .none);
432 return renderExpression(r, infix.rhs, space);
366433 },
367434 .for_range => {
368435 const infix = datas[node];
369 try renderExpression(gpa, ais, tree, infix.lhs, .none);
436 try renderExpression(r, infix.lhs, .none);
370437 if (infix.rhs != 0) {
371 try renderToken(ais, tree, main_tokens[node], .none);
372 return renderExpression(gpa, ais, tree, infix.rhs, space);
438 try renderToken(r, main_tokens[node], .none);
439 return renderExpression(r, infix.rhs, space);
373440 } else {
374 return renderToken(ais, tree, main_tokens[node], space);
441 return renderToken(r, main_tokens[node], space);
375442 }
376443 },
377444
......@@ -424,17 +491,17 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,
424491 .@"orelse",
425492 => {
426493 const infix = datas[node];
427 try renderExpression(gpa, ais, tree, infix.lhs, .space);
494 try renderExpression(r, infix.lhs, .space);
428495 const op_token = main_tokens[node];
429496 if (tree.tokensOnSameLine(op_token, op_token + 1)) {
430 try renderToken(ais, tree, op_token, .space);
497 try renderToken(r, op_token, .space);
431498 } else {
432499 ais.pushIndent();
433 try renderToken(ais, tree, op_token, .newline);
500 try renderToken(r, op_token, .newline);
434501 ais.popIndent();
435502 }
436503 ais.pushIndentOneShot();
437 return renderExpression(gpa, ais, tree, infix.rhs, space);
504 return renderExpression(r, infix.rhs, space);
438505 },
439506
440507 .assign_destructure => {
......@@ -445,7 +512,7 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,
445512
446513 const maybe_comptime_token = tree.firstToken(node) - 1;
447514 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);
449516 }
450517
451518 for (lhs_exprs, 0..) |lhs_node, i| {
......@@ -456,21 +523,21 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,
456523 .simple_var_decl,
457524 .aligned_var_decl,
458525 => {
459 try renderVarDecl(gpa, ais, tree, tree.fullVarDecl(lhs_node).?, true, lhs_space);
526 try renderVarDecl(r, tree.fullVarDecl(lhs_node).?, true, lhs_space);
460527 },
461 else => try renderExpression(gpa, ais, tree, lhs_node, lhs_space),
528 else => try renderExpression(r, lhs_node, lhs_space),
462529 }
463530 }
464531 const equal_token = main_tokens[node];
465532 if (tree.tokensOnSameLine(equal_token, equal_token + 1)) {
466 try renderToken(ais, tree, equal_token, .space);
533 try renderToken(r, equal_token, .space);
467534 } else {
468535 ais.pushIndent();
469 try renderToken(ais, tree, equal_token, .newline);
536 try renderToken(r, equal_token, .newline);
470537 ais.popIndent();
471538 }
472539 ais.pushIndentOneShot();
473 return renderExpression(gpa, ais, tree, rhs, space);
540 return renderExpression(r, rhs, space);
474541 },
475542
476543 .bit_not,
......@@ -480,27 +547,27 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,
480547 .optional_type,
481548 .address_of,
482549 => {
483 try renderToken(ais, tree, main_tokens[node], .none);
484 return renderExpression(gpa, ais, tree, datas[node].lhs, space);
550 try renderToken(r, main_tokens[node], .none);
551 return renderExpression(r, datas[node].lhs, space);
485552 },
486553
487554 .@"try",
488555 .@"resume",
489556 .@"await",
490557 => {
491 try renderToken(ais, tree, main_tokens[node], .space);
492 return renderExpression(gpa, ais, tree, datas[node].lhs, space);
558 try renderToken(r, main_tokens[node], .space);
559 return renderExpression(r, datas[node].lhs, space);
493560 },
494561
495562 .array_type,
496563 .array_type_sentinel,
497 => return renderArrayType(gpa, ais, tree, tree.fullArrayType(node).?, space),
564 => return renderArrayType(r, tree.fullArrayType(node).?, space),
498565
499566 .ptr_type_aligned,
500567 .ptr_type_sentinel,
501568 .ptr_type,
502569 .ptr_type_bit_range,
503 => return renderPtrType(gpa, ais, tree, tree.fullPtrType(node).?, space),
570 => return renderPtrType(r, tree.fullPtrType(node).?, space),
504571
505572 .array_init_one,
506573 .array_init_one_comma,
......@@ -512,7 +579,7 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,
512579 .array_init_comma,
513580 => {
514581 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);
516583 },
517584
518585 .struct_init_one,
......@@ -525,7 +592,7 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,
525592 .struct_init_comma,
526593 => {
527594 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);
529596 },
530597
531598 .call_one,
......@@ -538,7 +605,7 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,
538605 .async_call_comma,
539606 => {
540607 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);
542609 },
543610
544611 .array_access => {
......@@ -547,25 +614,25 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,
547614 const rbracket = tree.lastToken(suffix.rhs) + 1;
548615 const one_line = tree.tokensOnSameLine(lbracket, rbracket);
549616 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);
551618 ais.pushIndentNextLine();
552 try renderToken(ais, tree, lbracket, inner_space); // [
553 try renderExpression(gpa, ais, tree, suffix.rhs, inner_space);
619 try renderToken(r, lbracket, inner_space); // [
620 try renderExpression(r, suffix.rhs, inner_space);
554621 ais.popIndent();
555 return renderToken(ais, tree, rbracket, space); // ]
622 return renderToken(r, rbracket, space); // ]
556623 },
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
560627 .deref => {
561 try renderExpression(gpa, ais, tree, datas[node].lhs, .none);
562 return renderToken(ais, tree, main_tokens[node], space);
628 try renderExpression(r, datas[node].lhs, .none);
629 return renderToken(r, main_tokens[node], space);
563630 },
564631
565632 .unwrap_optional => {
566 try renderExpression(gpa, ais, tree, datas[node].lhs, .none);
567 try renderToken(ais, tree, main_tokens[node], .none);
568 return renderToken(ais, tree, datas[node].rhs, space);
633 try renderExpression(r, datas[node].lhs, .none);
634 try renderToken(r, main_tokens[node], .none);
635 return renderToken(r, datas[node].rhs, space);
569636 },
570637
571638 .@"break" => {
......@@ -573,19 +640,19 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,
573640 const label_token = datas[node].lhs;
574641 const target = datas[node].rhs;
575642 if (label_token == 0 and target == 0) {
576 try renderToken(ais, tree, main_token, space); // break keyword
643 try renderToken(r, main_token, space); // break keyword
577644 } else if (label_token == 0 and target != 0) {
578 try renderToken(ais, tree, main_token, .space); // break keyword
579 try renderExpression(gpa, ais, tree, target, space);
645 try renderToken(r, main_token, .space); // break keyword
646 try renderExpression(r, target, space);
580647 } else if (label_token != 0 and target == 0) {
581 try renderToken(ais, tree, main_token, .space); // break keyword
582 try renderToken(ais, tree, label_token - 1, .none); // colon
583 try renderIdentifier(ais, tree, label_token, space, .eagerly_unquote); // identifier
648 try renderToken(r, main_token, .space); // break keyword
649 try renderToken(r, label_token - 1, .none); // colon
650 try renderIdentifier(r, label_token, space, .eagerly_unquote); // identifier
584651 } else if (label_token != 0 and target != 0) {
585 try renderToken(ais, tree, main_token, .space); // break keyword
586 try renderToken(ais, tree, label_token - 1, .none); // colon
587 try renderIdentifier(ais, tree, label_token, .space, .eagerly_unquote); // identifier
588 try renderExpression(gpa, ais, tree, target, space);
652 try renderToken(r, main_token, .space); // break keyword
653 try renderToken(r, label_token - 1, .none); // colon
654 try renderIdentifier(r, label_token, .space, .eagerly_unquote); // identifier
655 try renderExpression(r, target, space);
589656 }
590657 },
591658
......@@ -593,28 +660,28 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,
593660 const main_token = main_tokens[node];
594661 const label = datas[node].lhs;
595662 if (label != 0) {
596 try renderToken(ais, tree, main_token, .space); // continue
597 try renderToken(ais, tree, label - 1, .none); // :
598 return renderIdentifier(ais, tree, label, space, .eagerly_unquote); // label
663 try renderToken(r, main_token, .space); // continue
664 try renderToken(r, label - 1, .none); // :
665 return renderIdentifier(r, label, space, .eagerly_unquote); // label
599666 } else {
600 return renderToken(ais, tree, main_token, space); // continue
667 return renderToken(r, main_token, space); // continue
601668 }
602669 },
603670
604671 .@"return" => {
605672 if (datas[node].lhs != 0) {
606 try renderToken(ais, tree, main_tokens[node], .space);
607 try renderExpression(gpa, ais, tree, datas[node].lhs, space);
673 try renderToken(r, main_tokens[node], .space);
674 try renderExpression(r, datas[node].lhs, space);
608675 } else {
609 try renderToken(ais, tree, main_tokens[node], space);
676 try renderToken(r, main_tokens[node], space);
610677 }
611678 },
612679
613680 .grouped_expression => {
614 try renderToken(ais, tree, main_tokens[node], .none); // lparen
681 try renderToken(r, main_tokens[node], .none); // lparen
615682 ais.pushIndentOneShot();
616 try renderExpression(gpa, ais, tree, datas[node].lhs, .none);
617 return renderToken(ais, tree, datas[node].rhs, space); // rparen
683 try renderExpression(r, datas[node].lhs, .none);
684 return renderToken(r, datas[node].rhs, space); // rparen
618685 },
619686
620687 .container_decl,
......@@ -631,7 +698,7 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,
631698 .tagged_union_two_trailing,
632699 => {
633700 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);
635702 },
636703
637704 .error_set_decl => {
......@@ -639,62 +706,62 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,
639706 const lbrace = error_token + 1;
640707 const rbrace = datas[node].rhs;
641708
642 try renderToken(ais, tree, error_token, .none);
709 try renderToken(r, error_token, .none);
643710
644711 if (lbrace + 1 == rbrace) {
645712 // There is nothing between the braces so render condensed: `error{}`
646 try renderToken(ais, tree, lbrace, .none);
647 return renderToken(ais, tree, rbrace, space);
713 try renderToken(r, lbrace, .none);
714 return renderToken(r, rbrace, space);
648715 } else if (lbrace + 2 == rbrace and token_tags[lbrace + 1] == .identifier) {
649716 // There is exactly one member and no trailing comma or
650717 // comments, so render without surrounding spaces: `error{Foo}`
651 try renderToken(ais, tree, lbrace, .none);
652 try renderIdentifier(ais, tree, lbrace + 1, .none, .eagerly_unquote); // identifier
653 return renderToken(ais, tree, rbrace, space);
718 try renderToken(r, lbrace, .none);
719 try renderIdentifier(r, lbrace + 1, .none, .eagerly_unquote); // identifier
720 return renderToken(r, rbrace, space);
654721 } else if (token_tags[rbrace - 1] == .comma) {
655722 // There is a trailing comma so render each member on a new line.
656723 ais.pushIndentNextLine();
657 try renderToken(ais, tree, lbrace, .newline);
724 try renderToken(r, lbrace, .newline);
658725 var i = lbrace + 1;
659726 while (i < rbrace) : (i += 1) {
660 if (i > lbrace + 1) try renderExtraNewlineToken(ais, tree, i);
727 if (i > lbrace + 1) try renderExtraNewlineToken(r, i);
661728 switch (token_tags[i]) {
662 .doc_comment => try renderToken(ais, tree, i, .newline),
663 .identifier => try renderIdentifier(ais, tree, i, .comma, .eagerly_unquote),
729 .doc_comment => try renderToken(r, i, .newline),
730 .identifier => try renderIdentifier(r, i, .comma, .eagerly_unquote),
664731 .comma => {},
665732 else => unreachable,
666733 }
667734 }
668735 ais.popIndent();
669 return renderToken(ais, tree, rbrace, space);
736 return renderToken(r, rbrace, space);
670737 } else {
671738 // There is no trailing comma so render everything on one line.
672 try renderToken(ais, tree, lbrace, .space);
739 try renderToken(r, lbrace, .space);
673740 var i = lbrace + 1;
674741 while (i < rbrace) : (i += 1) {
675742 switch (token_tags[i]) {
676743 .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),
678745 .comma => {},
679746 else => unreachable,
680747 }
681748 }
682 return renderToken(ais, tree, rbrace, space);
749 return renderToken(r, rbrace, space);
683750 }
684751 },
685752
686753 .builtin_call_two, .builtin_call_two_comma => {
687754 if (datas[node].lhs == 0) {
688 return renderBuiltinCall(gpa, ais, tree, main_tokens[node], &.{}, space);
755 return renderBuiltinCall(r, main_tokens[node], &.{}, space);
689756 } 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);
691758 } 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);
693760 }
694761 },
695762 .builtin_call, .builtin_call_comma => {
696763 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);
698765 },
699766
700767 .fn_proto_simple,
......@@ -703,17 +770,17 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,
703770 .fn_proto,
704771 => {
705772 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);
707774 },
708775
709776 .anyframe_type => {
710777 const main_token = main_tokens[node];
711778 if (datas[node].rhs != 0) {
712 try renderToken(ais, tree, main_token, .none); // anyframe
713 try renderToken(ais, tree, main_token + 1, .none); // ->
714 return renderExpression(gpa, ais, tree, datas[node].rhs, space);
779 try renderToken(r, main_token, .none); // anyframe
780 try renderToken(r, main_token + 1, .none); // ->
781 return renderExpression(r, datas[node].rhs, space);
715782 } else {
716 return renderToken(ais, tree, main_token, space); // anyframe
783 return renderToken(r, main_token, space); // anyframe
717784 }
718785 },
719786
......@@ -726,48 +793,48 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,
726793 const cases = tree.extra_data[extra.start..extra.end];
727794 const rparen = tree.lastToken(condition) + 1;
728795
729 try renderToken(ais, tree, switch_token, .space); // switch keyword
730 try renderToken(ais, tree, switch_token + 1, .none); // lparen
731 try renderExpression(gpa, ais, tree, condition, .none); // condition expression
732 try renderToken(ais, tree, rparen, .space); // rparen
796 try renderToken(r, switch_token, .space); // switch keyword
797 try renderToken(r, switch_token + 1, .none); // lparen
798 try renderExpression(r, condition, .none); // condition expression
799 try renderToken(r, rparen, .space); // rparen
733800
734801 ais.pushIndentNextLine();
735802 if (cases.len == 0) {
736 try renderToken(ais, tree, rparen + 1, .none); // lbrace
803 try renderToken(r, rparen + 1, .none); // lbrace
737804 } else {
738 try renderToken(ais, tree, rparen + 1, .newline); // lbrace
739 try renderExpressions(gpa, ais, tree, cases, .comma);
805 try renderToken(r, rparen + 1, .newline); // lbrace
806 try renderExpressions(r, cases, .comma);
740807 }
741808 ais.popIndent();
742 return renderToken(ais, tree, tree.lastToken(node), space); // rbrace
809 return renderToken(r, tree.lastToken(node), space); // rbrace
743810 },
744811
745812 .switch_case_one,
746813 .switch_case_inline_one,
747814 .switch_case,
748815 .switch_case_inline,
749 => return renderSwitchCase(gpa, ais, tree, tree.fullSwitchCase(node).?, space),
816 => return renderSwitchCase(r, tree.fullSwitchCase(node).?, space),
750817
751818 .while_simple,
752819 .while_cont,
753820 .@"while",
754 => return renderWhile(gpa, ais, tree, tree.fullWhile(node).?, space),
821 => return renderWhile(r, tree.fullWhile(node).?, space),
755822
756823 .for_simple,
757824 .@"for",
758 => return renderFor(gpa, ais, tree, tree.fullFor(node).?, space),
825 => return renderFor(r, tree.fullFor(node).?, space),
759826
760827 .if_simple,
761828 .@"if",
762 => return renderIf(gpa, ais, tree, tree.fullIf(node).?, space),
829 => return renderIf(r, tree.fullIf(node).?, space),
763830
764831 .asm_simple,
765832 .@"asm",
766 => return renderAsm(gpa, ais, tree, tree.fullAsm(node).?, space),
833 => return renderAsm(r, tree.fullAsm(node).?, space),
767834
768835 .enum_literal => {
769 try renderToken(ais, tree, main_tokens[node] - 1, .none); // .
770 return renderIdentifier(ais, tree, main_tokens[node], space, .eagerly_unquote); // name
836 try renderToken(r, main_tokens[node] - 1, .none); // .
837 return renderIdentifier(r, main_tokens[node], space, .eagerly_unquote); // name
771838 },
772839
773840 .fn_decl => unreachable,
......@@ -787,34 +854,29 @@ fn renderExpression(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node.Index,
787854}
788855
789856fn renderArrayType(
790 gpa: Allocator,
791 ais: *Ais,
792 tree: Ast,
857 r: *Render,
793858 array_type: Ast.full.ArrayType,
794859 space: Space,
795860) Error!void {
861 const tree = r.tree;
862 const ais = r.ais;
796863 const rbracket = tree.firstToken(array_type.ast.elem_type) - 1;
797864 const one_line = tree.tokensOnSameLine(array_type.ast.lbracket, rbracket);
798865 const inner_space = if (one_line) Space.none else Space.newline;
799866 ais.pushIndentNextLine();
800 try renderToken(ais, tree, array_type.ast.lbracket, inner_space); // lbracket
801 try renderExpression(gpa, ais, tree, array_type.ast.elem_count, inner_space);
867 try renderToken(r, array_type.ast.lbracket, inner_space); // lbracket
868 try renderExpression(r, array_type.ast.elem_count, inner_space);
802869 if (array_type.ast.sentinel != 0) {
803 try renderToken(ais, tree, tree.firstToken(array_type.ast.sentinel) - 1, inner_space); // colon
804 try renderExpression(gpa, ais, tree, array_type.ast.sentinel, inner_space);
870 try renderToken(r, tree.firstToken(array_type.ast.sentinel) - 1, inner_space); // colon
871 try renderExpression(r, array_type.ast.sentinel, inner_space);
805872 }
806873 ais.popIndent();
807 try renderToken(ais, tree, rbracket, .none); // rbracket
808 return renderExpression(gpa, ais, tree, array_type.ast.elem_type, space);
874 try renderToken(r, rbracket, .none); // rbracket
875 return renderExpression(r, array_type.ast.elem_type, space);
809876}
810877
811fn renderPtrType(
812 gpa: Allocator,
813 ais: *Ais,
814 tree: Ast,
815 ptr_type: Ast.full.PtrType,
816 space: Space,
817) Error!void {
878fn renderPtrType(r: *Render, ptr_type: Ast.full.PtrType, space: Space) Error!void {
879 const tree = r.tree;
818880 switch (ptr_type.size) {
819881 .One => {
820882 // Since ** tokens exist and the same token is shared by two
......@@ -825,90 +887,89 @@ fn renderPtrType(
825887 if (tree.tokens.items(.tag)[ptr_type.ast.main_token] == .asterisk_asterisk and
826888 ptr_type.ast.main_token == tree.nodes.items(.main_token)[ptr_type.ast.child_type])
827889 {
828 return renderExpression(gpa, ais, tree, ptr_type.ast.child_type, space);
890 return renderExpression(r, ptr_type.ast.child_type, space);
829891 }
830 try renderToken(ais, tree, ptr_type.ast.main_token, .none); // asterisk
892 try renderToken(r, ptr_type.ast.main_token, .none); // asterisk
831893 },
832894 .Many => {
833895 if (ptr_type.ast.sentinel == 0) {
834 try renderToken(ais, tree, ptr_type.ast.main_token - 1, .none); // lbracket
835 try renderToken(ais, tree, ptr_type.ast.main_token, .none); // asterisk
836 try renderToken(ais, tree, ptr_type.ast.main_token + 1, .none); // rbracket
896 try renderToken(r, ptr_type.ast.main_token - 1, .none); // lbracket
897 try renderToken(r, ptr_type.ast.main_token, .none); // asterisk
898 try renderToken(r, ptr_type.ast.main_token + 1, .none); // rbracket
837899 } else {
838 try renderToken(ais, tree, ptr_type.ast.main_token - 1, .none); // lbracket
839 try renderToken(ais, tree, ptr_type.ast.main_token, .none); // asterisk
840 try renderToken(ais, tree, ptr_type.ast.main_token + 1, .none); // colon
841 try renderExpression(gpa, ais, tree, ptr_type.ast.sentinel, .none);
842 try renderToken(ais, tree, tree.lastToken(ptr_type.ast.sentinel) + 1, .none); // rbracket
900 try renderToken(r, ptr_type.ast.main_token - 1, .none); // lbracket
901 try renderToken(r, ptr_type.ast.main_token, .none); // asterisk
902 try renderToken(r, ptr_type.ast.main_token + 1, .none); // colon
903 try renderExpression(r, ptr_type.ast.sentinel, .none);
904 try renderToken(r, tree.lastToken(ptr_type.ast.sentinel) + 1, .none); // rbracket
843905 }
844906 },
845907 .C => {
846 try renderToken(ais, tree, ptr_type.ast.main_token - 1, .none); // lbracket
847 try renderToken(ais, tree, ptr_type.ast.main_token, .none); // asterisk
848 try renderToken(ais, tree, ptr_type.ast.main_token + 1, .none); // c
849 try renderToken(ais, tree, ptr_type.ast.main_token + 2, .none); // rbracket
908 try renderToken(r, ptr_type.ast.main_token - 1, .none); // lbracket
909 try renderToken(r, ptr_type.ast.main_token, .none); // asterisk
910 try renderToken(r, ptr_type.ast.main_token + 1, .none); // c
911 try renderToken(r, ptr_type.ast.main_token + 2, .none); // rbracket
850912 },
851913 .Slice => {
852914 if (ptr_type.ast.sentinel == 0) {
853 try renderToken(ais, tree, ptr_type.ast.main_token, .none); // lbracket
854 try renderToken(ais, tree, ptr_type.ast.main_token + 1, .none); // rbracket
915 try renderToken(r, ptr_type.ast.main_token, .none); // lbracket
916 try renderToken(r, ptr_type.ast.main_token + 1, .none); // rbracket
855917 } else {
856 try renderToken(ais, tree, ptr_type.ast.main_token, .none); // lbracket
857 try renderToken(ais, tree, ptr_type.ast.main_token + 1, .none); // colon
858 try renderExpression(gpa, ais, tree, ptr_type.ast.sentinel, .none);
859 try renderToken(ais, tree, tree.lastToken(ptr_type.ast.sentinel) + 1, .none); // rbracket
918 try renderToken(r, ptr_type.ast.main_token, .none); // lbracket
919 try renderToken(r, ptr_type.ast.main_token + 1, .none); // colon
920 try renderExpression(r, ptr_type.ast.sentinel, .none);
921 try renderToken(r, tree.lastToken(ptr_type.ast.sentinel) + 1, .none); // rbracket
860922 }
861923 },
862924 }
863925
864926 if (ptr_type.allowzero_token) |allowzero_token| {
865 try renderToken(ais, tree, allowzero_token, .space);
927 try renderToken(r, allowzero_token, .space);
866928 }
867929
868930 if (ptr_type.ast.align_node != 0) {
869931 const align_first = tree.firstToken(ptr_type.ast.align_node);
870 try renderToken(ais, tree, align_first - 2, .none); // align
871 try renderToken(ais, tree, align_first - 1, .none); // lparen
872 try renderExpression(gpa, ais, tree, ptr_type.ast.align_node, .none);
932 try renderToken(r, align_first - 2, .none); // align
933 try renderToken(r, align_first - 1, .none); // lparen
934 try renderExpression(r, ptr_type.ast.align_node, .none);
873935 if (ptr_type.ast.bit_range_start != 0) {
874936 assert(ptr_type.ast.bit_range_end != 0);
875 try renderToken(ais, tree, tree.firstToken(ptr_type.ast.bit_range_start) - 1, .none); // colon
876 try renderExpression(gpa, ais, tree, ptr_type.ast.bit_range_start, .none);
877 try renderToken(ais, tree, tree.firstToken(ptr_type.ast.bit_range_end) - 1, .none); // colon
878 try renderExpression(gpa, ais, tree, ptr_type.ast.bit_range_end, .none);
879 try renderToken(ais, tree, tree.lastToken(ptr_type.ast.bit_range_end) + 1, .space); // rparen
937 try renderToken(r, tree.firstToken(ptr_type.ast.bit_range_start) - 1, .none); // colon
938 try renderExpression(r, ptr_type.ast.bit_range_start, .none);
939 try renderToken(r, tree.firstToken(ptr_type.ast.bit_range_end) - 1, .none); // colon
940 try renderExpression(r, ptr_type.ast.bit_range_end, .none);
941 try renderToken(r, tree.lastToken(ptr_type.ast.bit_range_end) + 1, .space); // rparen
880942 } else {
881 try renderToken(ais, tree, tree.lastToken(ptr_type.ast.align_node) + 1, .space); // rparen
943 try renderToken(r, tree.lastToken(ptr_type.ast.align_node) + 1, .space); // rparen
882944 }
883945 }
884946
885947 if (ptr_type.ast.addrspace_node != 0) {
886948 const addrspace_first = tree.firstToken(ptr_type.ast.addrspace_node);
887 try renderToken(ais, tree, addrspace_first - 2, .none); // addrspace
888 try renderToken(ais, tree, addrspace_first - 1, .none); // lparen
889 try renderExpression(gpa, ais, tree, ptr_type.ast.addrspace_node, .none);
890 try renderToken(ais, tree, tree.lastToken(ptr_type.ast.addrspace_node) + 1, .space); // rparen
949 try renderToken(r, addrspace_first - 2, .none); // addrspace
950 try renderToken(r, addrspace_first - 1, .none); // lparen
951 try renderExpression(r, ptr_type.ast.addrspace_node, .none);
952 try renderToken(r, tree.lastToken(ptr_type.ast.addrspace_node) + 1, .space); // rparen
891953 }
892954
893955 if (ptr_type.const_token) |const_token| {
894 try renderToken(ais, tree, const_token, .space);
956 try renderToken(r, const_token, .space);
895957 }
896958
897959 if (ptr_type.volatile_token) |volatile_token| {
898 try renderToken(ais, tree, volatile_token, .space);
960 try renderToken(r, volatile_token, .space);
899961 }
900962
901 try renderExpression(gpa, ais, tree, ptr_type.ast.child_type, space);
963 try renderExpression(r, ptr_type.ast.child_type, space);
902964}
903965
904966fn renderSlice(
905 gpa: Allocator,
906 ais: *Ais,
907 tree: Ast,
967 r: *Render,
908968 slice_node: Ast.Node.Index,
909969 slice: Ast.full.Slice,
910970 space: Space,
911971) Error!void {
972 const tree = r.tree;
912973 const node_tags = tree.nodes.items(.tag);
913974 const after_start_space_bool = nodeCausesSliceOpSpace(node_tags[slice.ast.start]) or
914975 if (slice.ast.end != 0) nodeCausesSliceOpSpace(node_tags[slice.ast.end]) else false;
......@@ -917,33 +978,32 @@ fn renderSlice(
917978 after_start_space
918979 else if (slice.ast.sentinel != 0) Space.space else Space.none;
919980
920 try renderExpression(gpa, ais, tree, slice.ast.sliced, .none);
921 try renderToken(ais, tree, slice.ast.lbracket, .none); // lbracket
981 try renderExpression(r, slice.ast.sliced, .none);
982 try renderToken(r, slice.ast.lbracket, .none); // lbracket
922983
923984 const start_last = tree.lastToken(slice.ast.start);
924 try renderExpression(gpa, ais, tree, slice.ast.start, after_start_space);
925 try renderToken(ais, tree, start_last + 1, after_dots_space); // ellipsis2 ("..")
985 try renderExpression(r, slice.ast.start, after_start_space);
986 try renderToken(r, start_last + 1, after_dots_space); // ellipsis2 ("..")
926987
927988 if (slice.ast.end != 0) {
928989 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);
930991 }
931992
932993 if (slice.ast.sentinel != 0) {
933 try renderToken(ais, tree, tree.firstToken(slice.ast.sentinel) - 1, .none); // colon
934 try renderExpression(gpa, ais, tree, slice.ast.sentinel, .none);
994 try renderToken(r, tree.firstToken(slice.ast.sentinel) - 1, .none); // colon
995 try renderExpression(r, slice.ast.sentinel, .none);
935996 }
936997
937 try renderToken(ais, tree, tree.lastToken(slice_node), space); // rbracket
998 try renderToken(r, tree.lastToken(slice_node), space); // rbracket
938999}
9391000
9401001fn renderAsmOutput(
941 gpa: Allocator,
942 ais: *Ais,
943 tree: Ast,
1002 r: *Render,
9441003 asm_output: Ast.Node.Index,
9451004 space: Space,
9461005) Error!void {
1006 const tree = r.tree;
9471007 const token_tags = tree.tokens.items(.tag);
9481008 const node_tags = tree.nodes.items(.tag);
9491009 const main_tokens = tree.nodes.items(.main_token);
......@@ -951,77 +1011,95 @@ fn renderAsmOutput(
9511011 assert(node_tags[asm_output] == .asm_output);
9521012 const symbolic_name = main_tokens[asm_output];
9531013
954 try renderToken(ais, tree, symbolic_name - 1, .none); // lbracket
955 try renderIdentifier(ais, tree, symbolic_name, .none, .eagerly_unquote); // ident
956 try renderToken(ais, tree, symbolic_name + 1, .space); // rbracket
957 try renderToken(ais, tree, symbolic_name + 2, .space); // "constraint"
958 try renderToken(ais, tree, symbolic_name + 3, .none); // lparen
1014 try renderToken(r, symbolic_name - 1, .none); // lbracket
1015 try renderIdentifier(r, symbolic_name, .none, .eagerly_unquote); // ident
1016 try renderToken(r, symbolic_name + 1, .space); // rbracket
1017 try renderToken(r, symbolic_name + 2, .space); // "constraint"
1018 try renderToken(r, symbolic_name + 3, .none); // lparen
9591019
9601020 if (token_tags[symbolic_name + 4] == .arrow) {
961 try renderToken(ais, tree, symbolic_name + 4, .space); // ->
962 try renderExpression(gpa, ais, tree, datas[asm_output].lhs, Space.none);
963 return renderToken(ais, tree, datas[asm_output].rhs, space); // rparen
1021 try renderToken(r, symbolic_name + 4, .space); // ->
1022 try renderExpression(r, datas[asm_output].lhs, Space.none);
1023 return renderToken(r, datas[asm_output].rhs, space); // rparen
9641024 } else {
965 try renderIdentifier(ais, tree, symbolic_name + 4, .none, .eagerly_unquote); // ident
966 return renderToken(ais, tree, symbolic_name + 5, space); // rparen
1025 try renderIdentifier(r, symbolic_name + 4, .none, .eagerly_unquote); // ident
1026 return renderToken(r, symbolic_name + 5, space); // rparen
9671027 }
9681028}
9691029
9701030fn renderAsmInput(
971 gpa: Allocator,
972 ais: *Ais,
973 tree: Ast,
1031 r: *Render,
9741032 asm_input: Ast.Node.Index,
9751033 space: Space,
9761034) Error!void {
1035 const tree = r.tree;
9771036 const node_tags = tree.nodes.items(.tag);
9781037 const main_tokens = tree.nodes.items(.main_token);
9791038 const datas = tree.nodes.items(.data);
9801039 assert(node_tags[asm_input] == .asm_input);
9811040 const symbolic_name = main_tokens[asm_input];
9821041
983 try renderToken(ais, tree, symbolic_name - 1, .none); // lbracket
984 try renderIdentifier(ais, tree, symbolic_name, .none, .eagerly_unquote); // ident
985 try renderToken(ais, tree, symbolic_name + 1, .space); // rbracket
986 try renderToken(ais, tree, symbolic_name + 2, .space); // "constraint"
987 try renderToken(ais, tree, symbolic_name + 3, .none); // lparen
988 try renderExpression(gpa, ais, tree, datas[asm_input].lhs, Space.none);
989 return renderToken(ais, tree, datas[asm_input].rhs, space); // rparen
1042 try renderToken(r, symbolic_name - 1, .none); // lbracket
1043 try renderIdentifier(r, symbolic_name, .none, .eagerly_unquote); // ident
1044 try renderToken(r, symbolic_name + 1, .space); // rbracket
1045 try renderToken(r, symbolic_name + 2, .space); // "constraint"
1046 try renderToken(r, symbolic_name + 3, .none); // lparen
1047 try renderExpression(r, datas[asm_input].lhs, Space.none);
1048 return renderToken(r, datas[asm_input].rhs, space); // rparen
9901049}
9911050
9921051fn renderVarDecl(
993 gpa: Allocator,
994 ais: *Ais,
995 tree: Ast,
1052 r: *Render,
1053 var_decl: Ast.full.VarDecl,
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,
9961071 var_decl: Ast.full.VarDecl,
9971072 /// Destructures intentionally ignore leading `comptime` tokens.
9981073 ignore_comptime_token: bool,
9991074 /// `comma_space` and `space` are used for destructure LHS decls.
10001075 space: Space,
10011076) Error!void {
1077 const tree = r.tree;
1078 const ais = r.ais;
1079
10021080 if (var_decl.visib_token) |visib_token| {
1003 try renderToken(ais, tree, visib_token, Space.space); // pub
1081 try renderToken(r, visib_token, Space.space); // pub
10041082 }
10051083
10061084 if (var_decl.extern_export_token) |extern_export_token| {
1007 try renderToken(ais, tree, extern_export_token, Space.space); // extern
1085 try renderToken(r, extern_export_token, Space.space); // extern
10081086
10091087 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"
10111089 }
10121090 }
10131091
10141092 if (var_decl.threadlocal_token) |thread_local_token| {
1015 try renderToken(ais, tree, thread_local_token, Space.space); // threadlocal
1093 try renderToken(r, thread_local_token, Space.space); // threadlocal
10161094 }
10171095
10181096 if (!ignore_comptime_token) {
10191097 if (var_decl.comptime_token) |comptime_token| {
1020 try renderToken(ais, tree, comptime_token, Space.space); // comptime
1098 try renderToken(r, comptime_token, Space.space); // comptime
10211099 }
10221100 }
10231101
1024 try renderToken(ais, tree, var_decl.ast.mut_token, .space); // var
1102 try renderToken(r, var_decl.ast.mut_token, .space); // var
10251103
10261104 if (var_decl.ast.type_node != 0 or var_decl.ast.align_node != 0 or
10271105 var_decl.ast.addrspace_node != 0 or var_decl.ast.section_node != 0 or
......@@ -1036,19 +1114,19 @@ fn renderVarDecl(
10361114 else
10371115 Space.none;
10381116
1039 try renderIdentifier(ais, tree, var_decl.ast.mut_token + 1, name_space, .preserve_when_shadowing); // name
1117 try renderIdentifier(r, var_decl.ast.mut_token + 1, name_space, .preserve_when_shadowing); // name
10401118 } else {
1041 return renderIdentifier(ais, tree, var_decl.ast.mut_token + 1, space, .preserve_when_shadowing); // name
1119 return renderIdentifier(r, var_decl.ast.mut_token + 1, space, .preserve_when_shadowing); // name
10421120 }
10431121
10441122 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); // :
10461124 if (var_decl.ast.align_node != 0 or var_decl.ast.addrspace_node != 0 or
10471125 var_decl.ast.section_node != 0 or var_decl.ast.init_node != 0)
10481126 {
1049 try renderExpression(gpa, ais, tree, var_decl.ast.type_node, .space);
1127 try renderExpression(r, var_decl.ast.type_node, .space);
10501128 } else {
1051 return renderExpression(gpa, ais, tree, var_decl.ast.type_node, space);
1129 return renderExpression(r, var_decl.ast.type_node, space);
10521130 }
10531131 }
10541132
......@@ -1056,15 +1134,15 @@ fn renderVarDecl(
10561134 const lparen = tree.firstToken(var_decl.ast.align_node) - 1;
10571135 const align_kw = lparen - 1;
10581136 const rparen = tree.lastToken(var_decl.ast.align_node) + 1;
1059 try renderToken(ais, tree, align_kw, Space.none); // align
1060 try renderToken(ais, tree, lparen, Space.none); // (
1061 try renderExpression(gpa, ais, tree, var_decl.ast.align_node, Space.none);
1137 try renderToken(r, align_kw, Space.none); // align
1138 try renderToken(r, lparen, Space.none); // (
1139 try renderExpression(r, var_decl.ast.align_node, Space.none);
10621140 if (var_decl.ast.addrspace_node != 0 or var_decl.ast.section_node != 0 or
10631141 var_decl.ast.init_node != 0)
10641142 {
1065 try renderToken(ais, tree, rparen, .space); // )
1143 try renderToken(r, rparen, .space); // )
10661144 } else {
1067 return renderToken(ais, tree, rparen, space); // )
1145 return renderToken(r, rparen, space); // )
10681146 }
10691147 }
10701148
......@@ -1072,14 +1150,14 @@ fn renderVarDecl(
10721150 const lparen = tree.firstToken(var_decl.ast.addrspace_node) - 1;
10731151 const addrspace_kw = lparen - 1;
10741152 const rparen = tree.lastToken(var_decl.ast.addrspace_node) + 1;
1075 try renderToken(ais, tree, addrspace_kw, Space.none); // addrspace
1076 try renderToken(ais, tree, lparen, Space.none); // (
1077 try renderExpression(gpa, ais, tree, var_decl.ast.addrspace_node, Space.none);
1153 try renderToken(r, addrspace_kw, Space.none); // addrspace
1154 try renderToken(r, lparen, Space.none); // (
1155 try renderExpression(r, var_decl.ast.addrspace_node, Space.none);
10781156 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); // )
10801158 } else {
1081 try renderToken(ais, tree, rparen, .none); // )
1082 return renderToken(ais, tree, rparen + 1, Space.newline); // ;
1159 try renderToken(r, rparen, .none); // )
1160 return renderToken(r, rparen + 1, Space.newline); // ;
10831161 }
10841162 }
10851163
......@@ -1087,13 +1165,13 @@ fn renderVarDecl(
10871165 const lparen = tree.firstToken(var_decl.ast.section_node) - 1;
10881166 const section_kw = lparen - 1;
10891167 const rparen = tree.lastToken(var_decl.ast.section_node) + 1;
1090 try renderToken(ais, tree, section_kw, Space.none); // linksection
1091 try renderToken(ais, tree, lparen, Space.none); // (
1092 try renderExpression(gpa, ais, tree, var_decl.ast.section_node, Space.none);
1168 try renderToken(r, section_kw, Space.none); // linksection
1169 try renderToken(r, lparen, Space.none); // (
1170 try renderExpression(r, var_decl.ast.section_node, Space.none);
10931171 if (var_decl.ast.init_node != 0) {
1094 try renderToken(ais, tree, rparen, .space); // )
1172 try renderToken(r, rparen, .space); // )
10951173 } else {
1096 return renderToken(ais, tree, rparen, space); // )
1174 return renderToken(r, rparen, space); // )
10971175 }
10981176 }
10991177
......@@ -1103,15 +1181,15 @@ fn renderVarDecl(
11031181 const eq_space: Space = if (tree.tokensOnSameLine(eq_token, eq_token + 1)) .space else .newline;
11041182 {
11051183 ais.pushIndent();
1106 try renderToken(ais, tree, eq_token, eq_space); // =
1184 try renderToken(r, eq_token, eq_space); // =
11071185 ais.popIndent();
11081186 }
11091187 ais.pushIndentOneShot();
1110 return renderExpression(gpa, ais, tree, var_decl.ast.init_node, space); // ;
1188 return renderExpression(r, var_decl.ast.init_node, space); // ;
11111189}
11121190
1113fn renderIf(gpa: Allocator, ais: *Ais, tree: Ast, if_node: Ast.full.If, space: Space) Error!void {
1114 return renderWhile(gpa, ais, tree, .{
1191fn renderIf(r: *Render, if_node: Ast.full.If, space: Space) Error!void {
1192 return renderWhile(r, .{
11151193 .ast = .{
11161194 .while_token = if_node.ast.if_token,
11171195 .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
11291207
11301208/// Note that this function is additionally used to render if expressions, with
11311209/// 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;
11331212 const token_tags = tree.tokens.items(.tag);
11341213
11351214 if (while_node.label_token) |label| {
1136 try renderIdentifier(ais, tree, label, .none, .eagerly_unquote); // label
1137 try renderToken(ais, tree, label + 1, .space); // :
1215 try renderIdentifier(r, label, .none, .eagerly_unquote); // label
1216 try renderToken(r, label + 1, .space); // :
11381217 }
11391218
11401219 if (while_node.inline_token) |inline_token| {
1141 try renderToken(ais, tree, inline_token, .space); // inline
1220 try renderToken(r, inline_token, .space); // inline
11421221 }
11431222
1144 try renderToken(ais, tree, while_node.ast.while_token, .space); // if/for/while
1145 try renderToken(ais, tree, while_node.ast.while_token + 1, .none); // lparen
1146 try renderExpression(gpa, ais, tree, while_node.ast.cond_expr, .none); // condition
1223 try renderToken(r, while_node.ast.while_token, .space); // if/for/while
1224 try renderToken(r, while_node.ast.while_token + 1, .none); // lparen
1225 try renderExpression(r, while_node.ast.cond_expr, .none); // condition
11471226
11481227 var last_prefix_token = tree.lastToken(while_node.ast.cond_expr) + 1; // rparen
11491228
11501229 if (while_node.payload_token) |payload_token| {
1151 try renderToken(ais, tree, last_prefix_token, .space);
1152 try renderToken(ais, tree, payload_token - 1, .none); // |
1230 try renderToken(r, last_prefix_token, .space);
1231 try renderToken(r, payload_token - 1, .none); // |
11531232 const ident = blk: {
11541233 if (token_tags[payload_token] == .asterisk) {
1155 try renderToken(ais, tree, payload_token, .none); // *
1234 try renderToken(r, payload_token, .none); // *
11561235 break :blk payload_token + 1;
11571236 } else {
11581237 break :blk payload_token;
11591238 }
11601239 };
1161 try renderIdentifier(ais, tree, ident, .none, .preserve_when_shadowing); // identifier
1240 try renderIdentifier(r, ident, .none, .preserve_when_shadowing); // identifier
11621241 const pipe = blk: {
11631242 if (token_tags[ident + 1] == .comma) {
1164 try renderToken(ais, tree, ident + 1, .space); // ,
1165 try renderIdentifier(ais, tree, ident + 2, .none, .preserve_when_shadowing); // index
1243 try renderToken(r, ident + 1, .space); // ,
1244 try renderIdentifier(r, ident + 2, .none, .preserve_when_shadowing); // index
11661245 break :blk ident + 3;
11671246 } else {
11681247 break :blk ident + 1;
......@@ -1172,18 +1251,16 @@ fn renderWhile(gpa: Allocator, ais: *Ais, tree: Ast, while_node: Ast.full.While,
11721251 }
11731252
11741253 if (while_node.ast.cont_expr != 0) {
1175 try renderToken(ais, tree, last_prefix_token, .space);
1254 try renderToken(r, last_prefix_token, .space);
11761255 const lparen = tree.firstToken(while_node.ast.cont_expr) - 1;
1177 try renderToken(ais, tree, lparen - 1, .space); // :
1178 try renderToken(ais, tree, lparen, .none); // lparen
1179 try renderExpression(gpa, ais, tree, while_node.ast.cont_expr, .none);
1256 try renderToken(r, lparen - 1, .space); // :
1257 try renderToken(r, lparen, .none); // lparen
1258 try renderExpression(r, while_node.ast.cont_expr, .none);
11801259 last_prefix_token = tree.lastToken(while_node.ast.cont_expr) + 1; // rparen
11811260 }
11821261
11831262 try renderThenElse(
1184 gpa,
1185 ais,
1186 tree,
1263 r,
11871264 last_prefix_token,
11881265 while_node.ast.then_expr,
11891266 while_node.else_token,
......@@ -1194,9 +1271,7 @@ fn renderWhile(gpa: Allocator, ais: *Ais, tree: Ast, while_node: Ast.full.While,
11941271}
11951272
11961273fn renderThenElse(
1197 gpa: Allocator,
1198 ais: *Ais,
1199 tree: Ast,
1274 r: *Render,
12001275 last_prefix_token: Ast.TokenIndex,
12011276 then_expr: Ast.Node.Index,
12021277 else_token: Ast.TokenIndex,
......@@ -1204,33 +1279,35 @@ fn renderThenElse(
12041279 else_expr: Ast.Node.Index,
12051280 space: Space,
12061281) Error!void {
1282 const tree = r.tree;
1283 const ais = r.ais;
12071284 const node_tags = tree.nodes.items(.tag);
12081285 const then_expr_is_block = nodeIsBlock(node_tags[then_expr]);
12091286 const indent_then_expr = !then_expr_is_block and
12101287 !tree.tokensOnSameLine(last_prefix_token, tree.firstToken(then_expr));
12111288 if (indent_then_expr or (then_expr_is_block and ais.isLineOverIndented())) {
12121289 ais.pushIndentNextLine();
1213 try renderToken(ais, tree, last_prefix_token, .newline);
1290 try renderToken(r, last_prefix_token, .newline);
12141291 ais.popIndent();
12151292 } else {
1216 try renderToken(ais, tree, last_prefix_token, .space);
1293 try renderToken(r, last_prefix_token, .space);
12171294 }
12181295
12191296 if (else_expr != 0) {
12201297 if (indent_then_expr) {
12211298 ais.pushIndent();
1222 try renderExpression(gpa, ais, tree, then_expr, .newline);
1299 try renderExpression(r, then_expr, .newline);
12231300 ais.popIndent();
12241301 } else {
1225 try renderExpression(gpa, ais, tree, then_expr, .space);
1302 try renderExpression(r, then_expr, .space);
12261303 }
12271304
12281305 var last_else_token = else_token;
12291306
12301307 if (maybe_error_token) |error_token| {
1231 try renderToken(ais, tree, else_token, .space); // else
1232 try renderToken(ais, tree, error_token - 1, .none); // |
1233 try renderIdentifier(ais, tree, error_token, .none, .preserve_when_shadowing); // identifier
1308 try renderToken(r, else_token, .space); // else
1309 try renderToken(r, error_token - 1, .none); // |
1310 try renderIdentifier(r, error_token, .none, .preserve_when_shadowing); // identifier
12341311 last_else_token = error_token + 1; // |
12351312 }
12361313
......@@ -1239,53 +1316,55 @@ fn renderThenElse(
12391316 !nodeIsIfForWhileSwitch(node_tags[else_expr]);
12401317 if (indent_else_expr) {
12411318 ais.pushIndentNextLine();
1242 try renderToken(ais, tree, last_else_token, .newline);
1319 try renderToken(r, last_else_token, .newline);
12431320 ais.popIndent();
1244 try renderExpressionIndented(gpa, ais, tree, else_expr, space);
1321 try renderExpressionIndented(r, else_expr, space);
12451322 } else {
1246 try renderToken(ais, tree, last_else_token, .space);
1247 try renderExpression(gpa, ais, tree, else_expr, space);
1323 try renderToken(r, last_else_token, .space);
1324 try renderExpression(r, else_expr, space);
12481325 }
12491326 } else {
12501327 if (indent_then_expr) {
1251 try renderExpressionIndented(gpa, ais, tree, then_expr, space);
1328 try renderExpressionIndented(r, then_expr, space);
12521329 } else {
1253 try renderExpression(gpa, ais, tree, then_expr, space);
1330 try renderExpression(r, then_expr, space);
12541331 }
12551332 }
12561333}
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;
12591338 const token_tags = tree.tokens.items(.tag);
12601339
12611340 if (for_node.label_token) |label| {
1262 try renderIdentifier(ais, tree, label, .none, .eagerly_unquote); // label
1263 try renderToken(ais, tree, label + 1, .space); // :
1341 try renderIdentifier(r, label, .none, .eagerly_unquote); // label
1342 try renderToken(r, label + 1, .space); // :
12641343 }
12651344
12661345 if (for_node.inline_token) |inline_token| {
1267 try renderToken(ais, tree, inline_token, .space); // inline
1346 try renderToken(r, inline_token, .space); // inline
12681347 }
12691348
1270 try renderToken(ais, tree, for_node.ast.for_token, .space); // if/for/while
1349 try renderToken(r, for_node.ast.for_token, .space); // if/for/while
12711350
12721351 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
12751354 var cur = for_node.payload_token;
12761355 const pipe = std.mem.indexOfScalarPos(std.zig.Token.Tag, token_tags, cur, .pipe).?;
12771356 if (token_tags[pipe - 1] == .comma) {
12781357 ais.pushIndentNextLine();
1279 try renderToken(ais, tree, cur - 1, .newline); // |
1358 try renderToken(r, cur - 1, .newline); // |
12801359 while (true) {
12811360 if (token_tags[cur] == .asterisk) {
1282 try renderToken(ais, tree, cur, .none); // *
1361 try renderToken(r, cur, .none); // *
12831362 cur += 1;
12841363 }
1285 try renderIdentifier(ais, tree, cur, .none, .preserve_when_shadowing); // identifier
1364 try renderIdentifier(r, cur, .none, .preserve_when_shadowing); // identifier
12861365 cur += 1;
12871366 if (token_tags[cur] == .comma) {
1288 try renderToken(ais, tree, cur, .newline); // ,
1367 try renderToken(r, cur, .newline); // ,
12891368 cur += 1;
12901369 }
12911370 if (token_tags[cur] == .pipe) {
......@@ -1294,16 +1373,16 @@ fn renderFor(gpa: Allocator, ais: *Ais, tree: Ast, for_node: Ast.full.For, space
12941373 }
12951374 ais.popIndent();
12961375 } else {
1297 try renderToken(ais, tree, cur - 1, .none); // |
1376 try renderToken(r, cur - 1, .none); // |
12981377 while (true) {
12991378 if (token_tags[cur] == .asterisk) {
1300 try renderToken(ais, tree, cur, .none); // *
1379 try renderToken(r, cur, .none); // *
13011380 cur += 1;
13021381 }
1303 try renderIdentifier(ais, tree, cur, .none, .preserve_when_shadowing); // identifier
1382 try renderIdentifier(r, cur, .none, .preserve_when_shadowing); // identifier
13041383 cur += 1;
13051384 if (token_tags[cur] == .comma) {
1306 try renderToken(ais, tree, cur, .space); // ,
1385 try renderToken(r, cur, .space); // ,
13071386 cur += 1;
13081387 }
13091388 if (token_tags[cur] == .pipe) {
......@@ -1313,9 +1392,7 @@ fn renderFor(gpa: Allocator, ais: *Ais, tree: Ast, for_node: Ast.full.For, space
13131392 }
13141393
13151394 try renderThenElse(
1316 gpa,
1317 ais,
1318 tree,
1395 r,
13191396 cur,
13201397 for_node.ast.then_expr,
13211398 for_node.else_token,
......@@ -1326,13 +1403,13 @@ fn renderFor(gpa: Allocator, ais: *Ais, tree: Ast, for_node: Ast.full.For, space
13261403}
13271404
13281405fn renderContainerField(
1329 gpa: Allocator,
1330 ais: *Ais,
1331 tree: Ast,
1406 r: *Render,
13321407 container: Container,
13331408 field_param: Ast.full.ContainerField,
13341409 space: Space,
13351410) Error!void {
1411 const tree = r.tree;
1412 const ais = r.ais;
13361413 var field = field_param;
13371414 if (container != .tuple) field.convertToNonTupleLike(tree.nodes);
13381415 const quote: QuoteBehavior = switch (container) {
......@@ -1341,102 +1418,102 @@ fn renderContainerField(
13411418 };
13421419
13431420 if (field.comptime_token) |t| {
1344 try renderToken(ais, tree, t, .space); // comptime
1421 try renderToken(r, t, .space); // comptime
13451422 }
13461423 if (field.ast.type_expr == 0 and field.ast.value_expr == 0) {
13471424 if (field.ast.align_expr != 0) {
1348 try renderIdentifier(ais, tree, field.ast.main_token, .space, quote); // name
1425 try renderIdentifier(r, field.ast.main_token, .space, quote); // name
13491426 const lparen_token = tree.firstToken(field.ast.align_expr) - 1;
13501427 const align_kw = lparen_token - 1;
13511428 const rparen_token = tree.lastToken(field.ast.align_expr) + 1;
1352 try renderToken(ais, tree, align_kw, .none); // align
1353 try renderToken(ais, tree, lparen_token, .none); // (
1354 try renderExpression(gpa, ais, tree, field.ast.align_expr, .none); // alignment
1355 return renderToken(ais, tree, rparen_token, .space); // )
1429 try renderToken(r, align_kw, .none); // align
1430 try renderToken(r, lparen_token, .none); // (
1431 try renderExpression(r, field.ast.align_expr, .none); // alignment
1432 return renderToken(r, rparen_token, .space); // )
13561433 }
1357 return renderIdentifierComma(ais, tree, field.ast.main_token, space, quote); // name
1434 return renderIdentifierComma(r, field.ast.main_token, space, quote); // name
13581435 }
13591436 if (field.ast.type_expr != 0 and field.ast.value_expr == 0) {
13601437 if (!field.ast.tuple_like) {
1361 try renderIdentifier(ais, tree, field.ast.main_token, .none, quote); // name
1362 try renderToken(ais, tree, field.ast.main_token + 1, .space); // :
1438 try renderIdentifier(r, field.ast.main_token, .none, quote); // name
1439 try renderToken(r, field.ast.main_token + 1, .space); // :
13631440 }
13641441
13651442 if (field.ast.align_expr != 0) {
1366 try renderExpression(gpa, ais, tree, field.ast.type_expr, .space); // type
1443 try renderExpression(r, field.ast.type_expr, .space); // type
13671444 const align_token = tree.firstToken(field.ast.align_expr) - 2;
1368 try renderToken(ais, tree, align_token, .none); // align
1369 try renderToken(ais, tree, align_token + 1, .none); // (
1370 try renderExpression(gpa, ais, tree, field.ast.align_expr, .none); // alignment
1445 try renderToken(r, align_token, .none); // align
1446 try renderToken(r, align_token + 1, .none); // (
1447 try renderExpression(r, field.ast.align_expr, .none); // alignment
13711448 const rparen = tree.lastToken(field.ast.align_expr) + 1;
1372 return renderTokenComma(ais, tree, rparen, space); // )
1449 return renderTokenComma(r, rparen, space); // )
13731450 } else {
1374 return renderExpressionComma(gpa, ais, tree, field.ast.type_expr, space); // type
1451 return renderExpressionComma(r, field.ast.type_expr, space); // type
13751452 }
13761453 }
13771454 if (field.ast.type_expr == 0 and field.ast.value_expr != 0) {
1378 try renderIdentifier(ais, tree, field.ast.main_token, .space, quote); // name
1455 try renderIdentifier(r, field.ast.main_token, .space, quote); // name
13791456 if (field.ast.align_expr != 0) {
13801457 const lparen_token = tree.firstToken(field.ast.align_expr) - 1;
13811458 const align_kw = lparen_token - 1;
13821459 const rparen_token = tree.lastToken(field.ast.align_expr) + 1;
1383 try renderToken(ais, tree, align_kw, .none); // align
1384 try renderToken(ais, tree, lparen_token, .none); // (
1385 try renderExpression(gpa, ais, tree, field.ast.align_expr, .none); // alignment
1386 try renderToken(ais, tree, rparen_token, .space); // )
1460 try renderToken(r, align_kw, .none); // align
1461 try renderToken(r, lparen_token, .none); // (
1462 try renderExpression(r, field.ast.align_expr, .none); // alignment
1463 try renderToken(r, rparen_token, .space); // )
13871464 }
1388 try renderToken(ais, tree, field.ast.main_token + 1, .space); // =
1389 return renderExpressionComma(gpa, ais, tree, field.ast.value_expr, space); // value
1465 try renderToken(r, field.ast.main_token + 1, .space); // =
1466 return renderExpressionComma(r, field.ast.value_expr, space); // value
13901467 }
13911468 if (!field.ast.tuple_like) {
1392 try renderIdentifier(ais, tree, field.ast.main_token, .none, quote); // name
1393 try renderToken(ais, tree, field.ast.main_token + 1, .space); // :
1469 try renderIdentifier(r, field.ast.main_token, .none, quote); // name
1470 try renderToken(r, field.ast.main_token + 1, .space); // :
13941471 }
1395 try renderExpression(gpa, ais, tree, field.ast.type_expr, .space); // type
1472 try renderExpression(r, field.ast.type_expr, .space); // type
13961473
13971474 if (field.ast.align_expr != 0) {
13981475 const lparen_token = tree.firstToken(field.ast.align_expr) - 1;
13991476 const align_kw = lparen_token - 1;
14001477 const rparen_token = tree.lastToken(field.ast.align_expr) + 1;
1401 try renderToken(ais, tree, align_kw, .none); // align
1402 try renderToken(ais, tree, lparen_token, .none); // (
1403 try renderExpression(gpa, ais, tree, field.ast.align_expr, .none); // alignment
1404 try renderToken(ais, tree, rparen_token, .space); // )
1478 try renderToken(r, align_kw, .none); // align
1479 try renderToken(r, lparen_token, .none); // (
1480 try renderExpression(r, field.ast.align_expr, .none); // alignment
1481 try renderToken(r, rparen_token, .space); // )
14051482 }
14061483 const eq_token = tree.firstToken(field.ast.value_expr) - 1;
14071484 const eq_space: Space = if (tree.tokensOnSameLine(eq_token, eq_token + 1)) .space else .newline;
14081485 {
14091486 ais.pushIndent();
1410 try renderToken(ais, tree, eq_token, eq_space); // =
1487 try renderToken(r, eq_token, eq_space); // =
14111488 ais.popIndent();
14121489 }
14131490
14141491 if (eq_space == .space)
1415 return renderExpressionComma(gpa, ais, tree, field.ast.value_expr, space); // value
1492 return renderExpressionComma(r, field.ast.value_expr, space); // value
14161493
14171494 const token_tags = tree.tokens.items(.tag);
14181495 const maybe_comma = tree.lastToken(field.ast.value_expr) + 1;
14191496
14201497 if (token_tags[maybe_comma] == .comma) {
14211498 ais.pushIndent();
1422 try renderExpression(gpa, ais, tree, field.ast.value_expr, .none); // value
1499 try renderExpression(r, field.ast.value_expr, .none); // value
14231500 ais.popIndent();
1424 try renderToken(ais, tree, maybe_comma, .newline);
1501 try renderToken(r, maybe_comma, .newline);
14251502 } else {
14261503 ais.pushIndent();
1427 try renderExpression(gpa, ais, tree, field.ast.value_expr, space); // value
1504 try renderExpression(r, field.ast.value_expr, space); // value
14281505 ais.popIndent();
14291506 }
14301507}
14311508
14321509fn renderBuiltinCall(
1433 gpa: Allocator,
1434 ais: *Ais,
1435 tree: Ast,
1510 r: *Render,
14361511 builtin_token: Ast.TokenIndex,
14371512 params: []const Ast.Node.Index,
14381513 space: Space,
14391514) Error!void {
1515 const tree = r.tree;
1516 const ais = r.ais;
14401517 const token_tags = tree.tokens.items(.tag);
14411518
14421519 // TODO remove before release of 0.12.0
......@@ -1465,14 +1542,14 @@ fn renderBuiltinCall(
14651542 if (token_tags[after_last_param_token] != .comma) {
14661543 // Render all on one line, no trailing comma.
14671544 try ais.writer().writeAll("@as");
1468 try renderToken(ais, tree, builtin_token + 1, .none); // (
1469 try renderExpression(gpa, ais, tree, params[0], .comma_space);
1545 try renderToken(r, builtin_token + 1, .none); // (
1546 try renderExpression(r, params[0], .comma_space);
14701547 } else {
14711548 // Render one param per line.
14721549 try ais.writer().writeAll("@as");
14731550 ais.pushIndent();
1474 try renderToken(ais, tree, builtin_token + 1, .newline); // (
1475 try renderExpression(gpa, ais, tree, params[0], .comma);
1551 try renderToken(r, builtin_token + 1, .newline); // (
1552 try renderExpression(r, params[0], .comma);
14761553 }
14771554 }
14781555 // Corresponding logic below builtin name rewrite below
......@@ -1507,29 +1584,29 @@ fn renderBuiltinCall(
15071584 } else if (mem.eql(u8, slice, "@errSetCast")) {
15081585 try ais.writer().writeAll("@errorCast");
15091586 } else {
1510 try renderToken(ais, tree, builtin_token, .none); // @name
1587 try renderToken(r, builtin_token, .none); // @name
15111588 }
15121589
15131590 if (rewrite_two_param_cast) {
15141591 // Matches with corresponding logic above builtin name rewrite
15151592 const after_last_param_token = tree.lastToken(params[1]) + 1;
15161593 try ais.writer().writeAll("(");
1517 try renderExpression(gpa, ais, tree, params[1], .none);
1594 try renderExpression(r, params[1], .none);
15181595 try ais.writer().writeAll(")");
15191596 if (token_tags[after_last_param_token] != .comma) {
15201597 // 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); // )
15221599 } else {
15231600 // Render one param per line.
15241601 ais.popIndent();
1525 try renderToken(ais, tree, after_last_param_token, .newline); // ,
1526 return renderToken(ais, tree, after_last_param_token + 1, space); // )
1602 try renderToken(r, after_last_param_token, .newline); // ,
1603 return renderToken(r, after_last_param_token + 1, space); // )
15271604 }
15281605 }
15291606
15301607 if (params.len == 0) {
1531 try renderToken(ais, tree, builtin_token + 1, .none); // (
1532 return renderToken(ais, tree, builtin_token + 2, space); // )
1608 try renderToken(r, builtin_token + 1, .none); // (
1609 return renderToken(r, builtin_token + 2, space); // )
15331610 }
15341611
15351612 const last_param = params[params.len - 1];
......@@ -1537,7 +1614,7 @@ fn renderBuiltinCall(
15371614
15381615 if (token_tags[after_last_param_token] != .comma) {
15391616 // 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
15421619 for (params, 0..) |param_node, i| {
15431620 const first_param_token = tree.firstToken(param_node);
......@@ -1546,39 +1623,41 @@ fn renderBuiltinCall(
15461623 {
15471624 ais.pushIndentOneShot();
15481625 }
1549 try renderExpression(gpa, ais, tree, param_node, .none);
1626 try renderExpression(r, param_node, .none);
15501627
15511628 if (i + 1 < params.len) {
15521629 const comma_token = tree.lastToken(param_node) + 1;
1553 try renderToken(ais, tree, comma_token, .space); // ,
1630 try renderToken(r, comma_token, .space); // ,
15541631 }
15551632 }
1556 return renderToken(ais, tree, after_last_param_token, space); // )
1633 return renderToken(r, after_last_param_token, space); // )
15571634 } else {
15581635 // Render one param per line.
15591636 ais.pushIndent();
1560 try renderToken(ais, tree, builtin_token + 1, Space.newline); // (
1637 try renderToken(r, builtin_token + 1, Space.newline); // (
15611638
15621639 for (params) |param_node| {
1563 try renderExpression(gpa, ais, tree, param_node, .comma);
1640 try renderExpression(r, param_node, .comma);
15641641 }
15651642 ais.popIndent();
15661643
1567 return renderToken(ais, tree, after_last_param_token + 1, space); // )
1644 return renderToken(r, after_last_param_token + 1, space); // )
15681645 }
15691646}
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;
15721651 const token_tags = tree.tokens.items(.tag);
15731652 const token_starts = tree.tokens.items(.start);
15741653
15751654 const after_fn_token = fn_proto.ast.fn_token + 1;
15761655 const lparen = if (token_tags[after_fn_token] == .identifier) blk: {
1577 try renderToken(ais, tree, fn_proto.ast.fn_token, .space); // fn
1578 try renderIdentifier(ais, tree, after_fn_token, .none, .preserve_when_shadowing); // name
1656 try renderToken(r, fn_proto.ast.fn_token, .space); // fn
1657 try renderIdentifier(r, after_fn_token, .none, .preserve_when_shadowing); // name
15791658 break :blk after_fn_token + 1;
15801659 } else blk: {
1581 try renderToken(ais, tree, fn_proto.ast.fn_token, .space); // fn
1660 try renderToken(r, fn_proto.ast.fn_token, .space); // fn
15821661 break :blk fn_proto.ast.fn_token + 1;
15831662 };
15841663 assert(token_tags[lparen] == .l_paren);
......@@ -1630,7 +1709,7 @@ fn renderFnProto(gpa: Allocator, ais: *Ais, tree: Ast, fn_proto: Ast.full.FnProt
16301709 const trailing_comma = token_tags[rparen - 1] == .comma;
16311710 if (!trailing_comma and !hasComment(tree, lparen, rparen)) {
16321711 // Render all on one line, no trailing comma.
1633 try renderToken(ais, tree, lparen, .none); // (
1712 try renderToken(r, lparen, .none); // (
16341713
16351714 var param_i: usize = 0;
16361715 var last_param_token = lparen;
......@@ -1638,25 +1717,25 @@ fn renderFnProto(gpa: Allocator, ais: *Ais, tree: Ast, fn_proto: Ast.full.FnProt
16381717 last_param_token += 1;
16391718 switch (token_tags[last_param_token]) {
16401719 .doc_comment => {
1641 try renderToken(ais, tree, last_param_token, .newline);
1720 try renderToken(r, last_param_token, .newline);
16421721 continue;
16431722 },
16441723 .ellipsis3 => {
1645 try renderToken(ais, tree, last_param_token, .none); // ...
1724 try renderToken(r, last_param_token, .none); // ...
16461725 break;
16471726 },
16481727 .keyword_noalias, .keyword_comptime => {
1649 try renderToken(ais, tree, last_param_token, .space);
1728 try renderToken(r, last_param_token, .space);
16501729 last_param_token += 1;
16511730 },
16521731 .identifier => {},
16531732 .keyword_anytype => {
1654 try renderToken(ais, tree, last_param_token, .none); // anytype
1733 try renderToken(r, last_param_token, .none); // anytype
16551734 continue;
16561735 },
16571736 .r_paren => break,
16581737 .comma => {
1659 try renderToken(ais, tree, last_param_token, .space); // ,
1738 try renderToken(r, last_param_token, .space); // ,
16601739 continue;
16611740 },
16621741 else => {}, // Parameter type without a name.
......@@ -1664,24 +1743,24 @@ fn renderFnProto(gpa: Allocator, ais: *Ais, tree: Ast, fn_proto: Ast.full.FnProt
16641743 if (token_tags[last_param_token] == .identifier and
16651744 token_tags[last_param_token + 1] == .colon)
16661745 {
1667 try renderIdentifier(ais, tree, last_param_token, .none, .preserve_when_shadowing); // name
1746 try renderIdentifier(r, last_param_token, .none, .preserve_when_shadowing); // name
16681747 last_param_token += 1;
1669 try renderToken(ais, tree, last_param_token, .space); // :
1748 try renderToken(r, last_param_token, .space); // :
16701749 last_param_token += 1;
16711750 }
16721751 if (token_tags[last_param_token] == .keyword_anytype) {
1673 try renderToken(ais, tree, last_param_token, .none); // anytype
1752 try renderToken(r, last_param_token, .none); // anytype
16741753 continue;
16751754 }
16761755 const param = fn_proto.ast.params[param_i];
16771756 param_i += 1;
1678 try renderExpression(gpa, ais, tree, param, .none);
1757 try renderExpression(r, param, .none);
16791758 last_param_token = tree.lastToken(param);
16801759 }
16811760 } else {
16821761 // One param per line.
16831762 ais.pushIndent();
1684 try renderToken(ais, tree, lparen, .newline); // (
1763 try renderToken(r, lparen, .newline); // (
16851764
16861765 var param_i: usize = 0;
16871766 var last_param_token = lparen;
......@@ -1689,20 +1768,20 @@ fn renderFnProto(gpa: Allocator, ais: *Ais, tree: Ast, fn_proto: Ast.full.FnProt
16891768 last_param_token += 1;
16901769 switch (token_tags[last_param_token]) {
16911770 .doc_comment => {
1692 try renderToken(ais, tree, last_param_token, .newline);
1771 try renderToken(r, last_param_token, .newline);
16931772 continue;
16941773 },
16951774 .ellipsis3 => {
1696 try renderToken(ais, tree, last_param_token, .comma); // ...
1775 try renderToken(r, last_param_token, .comma); // ...
16971776 break;
16981777 },
16991778 .keyword_noalias, .keyword_comptime => {
1700 try renderToken(ais, tree, last_param_token, .space);
1779 try renderToken(r, last_param_token, .space);
17011780 last_param_token += 1;
17021781 },
17031782 .identifier => {},
17041783 .keyword_anytype => {
1705 try renderToken(ais, tree, last_param_token, .comma); // anytype
1784 try renderToken(r, last_param_token, .comma); // anytype
17061785 if (token_tags[last_param_token + 1] == .comma)
17071786 last_param_token += 1;
17081787 continue;
......@@ -1713,56 +1792,56 @@ fn renderFnProto(gpa: Allocator, ais: *Ais, tree: Ast, fn_proto: Ast.full.FnProt
17131792 if (token_tags[last_param_token] == .identifier and
17141793 token_tags[last_param_token + 1] == .colon)
17151794 {
1716 try renderIdentifier(ais, tree, last_param_token, .none, .preserve_when_shadowing); // name
1795 try renderIdentifier(r, last_param_token, .none, .preserve_when_shadowing); // name
17171796 last_param_token += 1;
1718 try renderToken(ais, tree, last_param_token, .space); // :
1797 try renderToken(r, last_param_token, .space); // :
17191798 last_param_token += 1;
17201799 }
17211800 if (token_tags[last_param_token] == .keyword_anytype) {
1722 try renderToken(ais, tree, last_param_token, .comma); // anytype
1801 try renderToken(r, last_param_token, .comma); // anytype
17231802 if (token_tags[last_param_token + 1] == .comma)
17241803 last_param_token += 1;
17251804 continue;
17261805 }
17271806 const param = fn_proto.ast.params[param_i];
17281807 param_i += 1;
1729 try renderExpression(gpa, ais, tree, param, .comma);
1808 try renderExpression(r, param, .comma);
17301809 last_param_token = tree.lastToken(param);
17311810 if (token_tags[last_param_token + 1] == .comma) last_param_token += 1;
17321811 }
17331812 ais.popIndent();
17341813 }
17351814
1736 try renderToken(ais, tree, rparen, .space); // )
1815 try renderToken(r, rparen, .space); // )
17371816
17381817 if (fn_proto.ast.align_expr != 0) {
17391818 const align_lparen = tree.firstToken(fn_proto.ast.align_expr) - 1;
17401819 const align_rparen = tree.lastToken(fn_proto.ast.align_expr) + 1;
17411820
1742 try renderToken(ais, tree, align_lparen - 1, .none); // align
1743 try renderToken(ais, tree, align_lparen, .none); // (
1744 try renderExpression(gpa, ais, tree, fn_proto.ast.align_expr, .none);
1745 try renderToken(ais, tree, align_rparen, .space); // )
1821 try renderToken(r, align_lparen - 1, .none); // align
1822 try renderToken(r, align_lparen, .none); // (
1823 try renderExpression(r, fn_proto.ast.align_expr, .none);
1824 try renderToken(r, align_rparen, .space); // )
17461825 }
17471826
17481827 if (fn_proto.ast.addrspace_expr != 0) {
17491828 const align_lparen = tree.firstToken(fn_proto.ast.addrspace_expr) - 1;
17501829 const align_rparen = tree.lastToken(fn_proto.ast.addrspace_expr) + 1;
17511830
1752 try renderToken(ais, tree, align_lparen - 1, .none); // addrspace
1753 try renderToken(ais, tree, align_lparen, .none); // (
1754 try renderExpression(gpa, ais, tree, fn_proto.ast.addrspace_expr, .none);
1755 try renderToken(ais, tree, align_rparen, .space); // )
1831 try renderToken(r, align_lparen - 1, .none); // addrspace
1832 try renderToken(r, align_lparen, .none); // (
1833 try renderExpression(r, fn_proto.ast.addrspace_expr, .none);
1834 try renderToken(r, align_rparen, .space); // )
17561835 }
17571836
17581837 if (fn_proto.ast.section_expr != 0) {
17591838 const section_lparen = tree.firstToken(fn_proto.ast.section_expr) - 1;
17601839 const section_rparen = tree.lastToken(fn_proto.ast.section_expr) + 1;
17611840
1762 try renderToken(ais, tree, section_lparen - 1, .none); // section
1763 try renderToken(ais, tree, section_lparen, .none); // (
1764 try renderExpression(gpa, ais, tree, fn_proto.ast.section_expr, .none);
1765 try renderToken(ais, tree, section_rparen, .space); // )
1841 try renderToken(r, section_lparen - 1, .none); // section
1842 try renderToken(r, section_lparen, .none); // (
1843 try renderExpression(r, fn_proto.ast.section_expr, .none);
1844 try renderToken(r, section_rparen, .space); // )
17661845 }
17671846
17681847 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
17711850 const callconv_lparen = tree.firstToken(fn_proto.ast.callconv_expr) - 1;
17721851 const callconv_rparen = tree.lastToken(fn_proto.ast.callconv_expr) + 1;
17731852
1774 try renderToken(ais, tree, callconv_lparen - 1, .none); // callconv
1775 try renderToken(ais, tree, callconv_lparen, .none); // (
1776 try renderExpression(gpa, ais, tree, fn_proto.ast.callconv_expr, .none);
1777 try renderToken(ais, tree, callconv_rparen, .space); // )
1853 try renderToken(r, callconv_lparen - 1, .none); // callconv
1854 try renderToken(r, callconv_lparen, .none); // (
1855 try renderExpression(r, fn_proto.ast.callconv_expr, .none);
1856 try renderToken(r, callconv_rparen, .space); // )
17781857 }
17791858
17801859 if (token_tags[maybe_bang] == .bang) {
1781 try renderToken(ais, tree, maybe_bang, .none); // !
1860 try renderToken(r, maybe_bang, .none); // !
17821861 }
1783 return renderExpression(gpa, ais, tree, fn_proto.ast.return_type, space);
1862 return renderExpression(r, fn_proto.ast.return_type, space);
17841863}
17851864
17861865fn renderSwitchCase(
1787 gpa: Allocator,
1788 ais: *Ais,
1789 tree: Ast,
1866 r: *Render,
17901867 switch_case: Ast.full.SwitchCase,
17911868 space: Space,
17921869) Error!void {
1870 const tree = r.tree;
17931871 const node_tags = tree.nodes.items(.tag);
17941872 const token_tags = tree.tokens.items(.tag);
17951873 const trailing_comma = token_tags[switch_case.ast.arrow_token - 1] == .comma;
......@@ -1800,22 +1878,22 @@ fn renderSwitchCase(
18001878
18011879 // render inline keyword
18021880 if (switch_case.inline_token) |some| {
1803 try renderToken(ais, tree, some, .space);
1881 try renderToken(r, some, .space);
18041882 }
18051883
18061884 // Render everything before the arrow
18071885 if (switch_case.ast.values.len == 0) {
1808 try renderToken(ais, tree, switch_case.ast.arrow_token - 1, .space); // else keyword
1886 try renderToken(r, switch_case.ast.arrow_token - 1, .space); // else keyword
18091887 } else if (switch_case.ast.values.len == 1 and !has_comment_before_arrow) {
18101888 // 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);
18121890 } else if (trailing_comma or has_comment_before_arrow) {
18131891 // 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);
18151893 } else {
18161894 // Render on one line
18171895 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);
18191897 }
18201898 }
18211899
......@@ -1826,35 +1904,35 @@ fn renderSwitchCase(
18261904 else
18271905 Space.space;
18281906 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
18311909 if (switch_case.payload_token) |payload_token| {
1832 try renderToken(ais, tree, payload_token - 1, .none); // pipe
1910 try renderToken(r, payload_token - 1, .none); // pipe
18331911 const ident = payload_token + @intFromBool(token_tags[payload_token] == .asterisk);
18341912 if (token_tags[payload_token] == .asterisk) {
1835 try renderToken(ais, tree, payload_token, .none); // asterisk
1913 try renderToken(r, payload_token, .none); // asterisk
18361914 }
1837 try renderIdentifier(ais, tree, ident, .none, .preserve_when_shadowing); // identifier
1915 try renderIdentifier(r, ident, .none, .preserve_when_shadowing); // identifier
18381916 if (token_tags[ident + 1] == .comma) {
1839 try renderToken(ais, tree, ident + 1, .space); // ,
1840 try renderIdentifier(ais, tree, ident + 2, .none, .preserve_when_shadowing); // identifier
1841 try renderToken(ais, tree, ident + 3, pre_target_space); // pipe
1917 try renderToken(r, ident + 1, .space); // ,
1918 try renderIdentifier(r, ident + 2, .none, .preserve_when_shadowing); // identifier
1919 try renderToken(r, ident + 3, pre_target_space); // pipe
18421920 } else {
1843 try renderToken(ais, tree, ident + 1, pre_target_space); // pipe
1921 try renderToken(r, ident + 1, pre_target_space); // pipe
18441922 }
18451923 }
18461924
1847 try renderExpression(gpa, ais, tree, switch_case.ast.target_expr, space);
1925 try renderExpression(r, switch_case.ast.target_expr, space);
18481926}
18491927
18501928fn renderBlock(
1851 gpa: Allocator,
1852 ais: *Ais,
1853 tree: Ast,
1929 r: *Render,
18541930 block_node: Ast.Node.Index,
18551931 statements: []const Ast.Node.Index,
18561932 space: Space,
18571933) Error!void {
1934 const tree = r.tree;
1935 const ais = r.ais;
18581936 const token_tags = tree.tokens.items(.tag);
18591937 const node_tags = tree.nodes.items(.tag);
18601938 const lbrace = tree.nodes.items(.main_token)[block_node];
......@@ -1862,51 +1940,51 @@ fn renderBlock(
18621940 if (token_tags[lbrace - 1] == .colon and
18631941 token_tags[lbrace - 2] == .identifier)
18641942 {
1865 try renderIdentifier(ais, tree, lbrace - 2, .none, .eagerly_unquote); // identifier
1866 try renderToken(ais, tree, lbrace - 1, .space); // :
1943 try renderIdentifier(r, lbrace - 2, .none, .eagerly_unquote); // identifier
1944 try renderToken(r, lbrace - 1, .space); // :
18671945 }
18681946
18691947 ais.pushIndentNextLine();
18701948 if (statements.len == 0) {
1871 try renderToken(ais, tree, lbrace, .none);
1949 try renderToken(r, lbrace, .none);
18721950 } else {
1873 try renderToken(ais, tree, lbrace, .newline);
1951 try renderToken(r, lbrace, .newline);
18741952 for (statements, 0..) |stmt, i| {
1875 if (i != 0) try renderExtraNewline(ais, tree, stmt);
1953 if (i != 0) try renderExtraNewline(r, stmt);
18761954 switch (node_tags[stmt]) {
18771955 .global_var_decl,
18781956 .local_var_decl,
18791957 .simple_var_decl,
18801958 .aligned_var_decl,
1881 => try renderVarDecl(gpa, ais, tree, tree.fullVarDecl(stmt).?, false, .semicolon),
1882 else => try renderExpression(gpa, ais, tree, stmt, .semicolon),
1959 => try renderVarDecl(r, tree.fullVarDecl(stmt).?, false, .semicolon),
1960 else => try renderExpression(r, stmt, .semicolon),
18831961 }
18841962 }
18851963 }
18861964 ais.popIndent();
18871965
1888 try renderToken(ais, tree, tree.lastToken(block_node), space); // rbrace
1966 try renderToken(r, tree.lastToken(block_node), space); // rbrace
18891967}
18901968
18911969fn renderStructInit(
1892 gpa: Allocator,
1893 ais: *Ais,
1894 tree: Ast,
1970 r: *Render,
18951971 struct_node: Ast.Node.Index,
18961972 struct_init: Ast.full.StructInit,
18971973 space: Space,
18981974) Error!void {
1975 const tree = r.tree;
1976 const ais = r.ais;
18991977 const token_tags = tree.tokens.items(.tag);
19001978 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); // .
19021980 } else {
1903 try renderExpression(gpa, ais, tree, struct_init.ast.type_expr, .none); // T
1981 try renderExpression(r, struct_init.ast.type_expr, .none); // T
19041982 }
19051983 if (struct_init.ast.fields.len == 0) {
19061984 ais.pushIndentNextLine();
1907 try renderToken(ais, tree, struct_init.ast.lbrace, .none); // lbrace
1985 try renderToken(r, struct_init.ast.lbrace, .none); // lbrace
19081986 ais.popIndent();
1909 return renderToken(ais, tree, struct_init.ast.lbrace + 1, space); // rbrace
1987 return renderToken(r, struct_init.ast.lbrace + 1, space); // rbrace
19101988 }
19111989
19121990 const rbrace = tree.lastToken(struct_node);
......@@ -1914,65 +1992,66 @@ fn renderStructInit(
19141992 if (trailing_comma or hasComment(tree, struct_init.ast.lbrace, rbrace)) {
19151993 // Render one field init per line.
19161994 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); // .
1920 try renderIdentifier(ais, tree, struct_init.ast.lbrace + 2, .space, .eagerly_unquote); // name
1997 try renderToken(r, struct_init.ast.lbrace + 1, .none); // .
1998 try renderIdentifier(r, struct_init.ast.lbrace + 2, .space, .eagerly_unquote); // name
19211999 // Don't output a space after the = if expression is a multiline string,
19222000 // since then it will start on the next line.
19232001 const nodes = tree.nodes.items(.tag);
19242002 const expr = nodes[struct_init.ast.fields[0]];
19252003 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); // =
1927 try renderExpression(gpa, ais, tree, struct_init.ast.fields[0], .comma);
2004 try renderToken(r, struct_init.ast.lbrace + 3, space_after_equal); // =
2005 try renderExpression(r, struct_init.ast.fields[0], .comma);
19282006
19292007 for (struct_init.ast.fields[1..]) |field_init| {
19302008 const init_token = tree.firstToken(field_init);
1931 try renderExtraNewlineToken(ais, tree, init_token - 3);
1932 try renderToken(ais, tree, init_token - 3, .none); // .
1933 try renderIdentifier(ais, tree, init_token - 2, .space, .eagerly_unquote); // name
2009 try renderExtraNewlineToken(r, init_token - 3);
2010 try renderToken(r, init_token - 3, .none); // .
2011 try renderIdentifier(r, init_token - 2, .space, .eagerly_unquote); // name
19342012 space_after_equal = if (nodes[field_init] == .multiline_string_literal) .none else .space;
1935 try renderToken(ais, tree, init_token - 1, space_after_equal); // =
1936 try renderExpression(gpa, ais, tree, field_init, .comma);
2013 try renderToken(r, init_token - 1, space_after_equal); // =
2014 try renderExpression(r, field_init, .comma);
19372015 }
19382016
19392017 ais.popIndent();
19402018 } else {
19412019 // 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
19442022 for (struct_init.ast.fields) |field_init| {
19452023 const init_token = tree.firstToken(field_init);
1946 try renderToken(ais, tree, init_token - 3, .none); // .
1947 try renderIdentifier(ais, tree, init_token - 2, .space, .eagerly_unquote); // name
1948 try renderToken(ais, tree, init_token - 1, .space); // =
1949 try renderExpression(gpa, ais, tree, field_init, .comma_space);
2024 try renderToken(r, init_token - 3, .none); // .
2025 try renderIdentifier(r, init_token - 2, .space, .eagerly_unquote); // name
2026 try renderToken(r, init_token - 1, .space); // =
2027 try renderExpression(r, field_init, .comma_space);
19502028 }
19512029 }
19522030
1953 return renderToken(ais, tree, rbrace, space);
2031 return renderToken(r, rbrace, space);
19542032}
19552033
19562034fn renderArrayInit(
1957 gpa: Allocator,
1958 ais: *Ais,
1959 tree: Ast,
2035 r: *Render,
19602036 array_init: Ast.full.ArrayInit,
19612037 space: Space,
19622038) Error!void {
2039 const tree = r.tree;
2040 const ais = r.ais;
2041 const gpa = r.gpa;
19632042 const token_tags = tree.tokens.items(.tag);
19642043
19652044 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); // .
19672046 } else {
1968 try renderExpression(gpa, ais, tree, array_init.ast.type_expr, .none); // T
2047 try renderExpression(r, array_init.ast.type_expr, .none); // T
19692048 }
19702049
19712050 if (array_init.ast.elements.len == 0) {
19722051 ais.pushIndentNextLine();
1973 try renderToken(ais, tree, array_init.ast.lbrace, .none); // lbrace
2052 try renderToken(r, array_init.ast.lbrace, .none); // lbrace
19742053 ais.popIndent();
1975 return renderToken(ais, tree, array_init.ast.lbrace + 1, space); // rbrace
2054 return renderToken(r, array_init.ast.lbrace + 1, space); // rbrace
19762055 }
19772056
19782057 const last_elem = array_init.ast.elements[array_init.ast.elements.len - 1];
......@@ -1987,9 +2066,9 @@ fn renderArrayInit(
19872066 if (token_tags[first_token] != .multiline_string_literal_line and
19882067 !anythingBetween(tree, last_elem_token, rbrace))
19892068 {
1990 try renderToken(ais, tree, array_init.ast.lbrace, .none);
1991 try renderExpression(gpa, ais, tree, only_elem, .none);
1992 return renderToken(ais, tree, rbrace, space);
2069 try renderToken(r, array_init.ast.lbrace, .none);
2070 try renderExpression(r, only_elem, .none);
2071 return renderToken(r, rbrace, space);
19932072 }
19942073 }
19952074
......@@ -2000,19 +2079,19 @@ fn renderArrayInit(
20002079 // Render all on one line, no trailing comma.
20012080 if (array_init.ast.elements.len == 1) {
20022081 // If there is only one element, we don't use spaces
2003 try renderToken(ais, tree, array_init.ast.lbrace, .none);
2004 try renderExpression(gpa, ais, tree, array_init.ast.elements[0], .none);
2082 try renderToken(r, array_init.ast.lbrace, .none);
2083 try renderExpression(r, array_init.ast.elements[0], .none);
20052084 } else {
2006 try renderToken(ais, tree, array_init.ast.lbrace, .space);
2085 try renderToken(r, array_init.ast.lbrace, .space);
20072086 for (array_init.ast.elements) |elem| {
2008 try renderExpression(gpa, ais, tree, elem, .comma_space);
2087 try renderExpression(r, elem, .comma_space);
20092088 }
20102089 }
2011 return renderToken(ais, tree, last_elem_token + 1, space); // rbrace
2090 return renderToken(r, last_elem_token + 1, space); // rbrace
20122091 }
20132092
20142093 ais.pushIndentNextLine();
2015 try renderToken(ais, tree, array_init.ast.lbrace, .newline);
2094 try renderToken(r, array_init.ast.lbrace, .newline);
20162095
20172096 var expr_index: usize = 0;
20182097 while (true) {
......@@ -2068,6 +2147,12 @@ fn renderArrayInit(
20682147 .indent_delta = indent_delta,
20692148 .underlying_writer = sub_expr_buffer.writer(),
20702149 };
2150 var sub_render: Render = .{
2151 .gpa = r.gpa,
2152 .ais = &auto_indenting_stream,
2153 .tree = r.tree,
2154 .fixups = r.fixups,
2155 };
20712156
20722157 // Calculate size of columns in current section
20732158 var column_counter: usize = 0;
......@@ -2078,7 +2163,7 @@ fn renderArrayInit(
20782163 sub_expr_buffer_starts[i] = start;
20792164
20802165 if (i + 1 < section_exprs.len) {
2081 try renderExpression(gpa, &auto_indenting_stream, tree, expr, .none);
2166 try renderExpression(&sub_render, expr, .none);
20822167 const width = sub_expr_buffer.items.len - start;
20832168 const this_contains_newline = mem.indexOfScalar(u8, sub_expr_buffer.items[start..], '\n') != null;
20842169 contains_newline = contains_newline or this_contains_newline;
......@@ -2098,7 +2183,7 @@ fn renderArrayInit(
20982183 column_counter = 0;
20992184 }
21002185 } else {
2101 try renderExpression(gpa, &auto_indenting_stream, tree, expr, .comma);
2186 try renderExpression(&sub_render, expr, .comma);
21022187 const width = sub_expr_buffer.items.len - start - 2;
21032188 const this_contains_newline = mem.indexOfScalar(u8, sub_expr_buffer.items[start .. sub_expr_buffer.items.len - 1], '\n') != null;
21042189 contains_newline = contains_newline or this_contains_newline;
......@@ -2143,7 +2228,7 @@ fn renderArrayInit(
21432228 if (column_counter != row_size - 1) {
21442229 if (!expr_newlines[i] and !expr_newlines[i + 1]) {
21452230 // Neither the current or next expression is multiline
2146 try renderToken(ais, tree, comma, .space); // ,
2231 try renderToken(r, comma, .space); // ,
21472232 assert(column_widths[column_counter % row_size] >= expr_widths[i]);
21482233 const padding = column_widths[column_counter % row_size] - expr_widths[i];
21492234 try ais.writer().writeByteNTimes(' ', padding);
......@@ -2154,13 +2239,13 @@ fn renderArrayInit(
21542239 }
21552240
21562241 if (single_line and row_size != 1) {
2157 try renderToken(ais, tree, comma, .space); // ,
2242 try renderToken(r, comma, .space); // ,
21582243 continue;
21592244 }
21602245
21612246 column_counter = 0;
2162 try renderToken(ais, tree, comma, .newline); // ,
2163 try renderExtraNewline(ais, tree, next_expr);
2247 try renderToken(r, comma, .newline); // ,
2248 try renderExtraNewline(r, next_expr);
21642249 }
21652250 }
21662251
......@@ -2169,21 +2254,21 @@ fn renderArrayInit(
21692254 }
21702255
21712256 ais.popIndent();
2172 return renderToken(ais, tree, rbrace, space); // rbrace
2257 return renderToken(r, rbrace, space); // rbrace
21732258}
21742259
21752260fn renderContainerDecl(
2176 gpa: Allocator,
2177 ais: *Ais,
2178 tree: Ast,
2261 r: *Render,
21792262 container_decl_node: Ast.Node.Index,
21802263 container_decl: Ast.full.ContainerDecl,
21812264 space: Space,
21822265) Error!void {
2266 const tree = r.tree;
2267 const ais = r.ais;
21832268 const token_tags = tree.tokens.items(.tag);
21842269
21852270 if (container_decl.layout_token) |layout_token| {
2186 try renderToken(ais, tree, layout_token, .space);
2271 try renderToken(r, layout_token, .space);
21872272 }
21882273
21892274 const container: Container = switch (token_tags[container_decl.ast.main_token]) {
......@@ -2196,29 +2281,29 @@ fn renderContainerDecl(
21962281
21972282 var lbrace: Ast.TokenIndex = undefined;
21982283 if (container_decl.ast.enum_token) |enum_token| {
2199 try renderToken(ais, tree, container_decl.ast.main_token, .none); // union
2200 try renderToken(ais, tree, enum_token - 1, .none); // lparen
2201 try renderToken(ais, tree, enum_token, .none); // enum
2284 try renderToken(r, container_decl.ast.main_token, .none); // union
2285 try renderToken(r, enum_token - 1, .none); // lparen
2286 try renderToken(r, enum_token, .none); // enum
22022287 if (container_decl.ast.arg != 0) {
2203 try renderToken(ais, tree, enum_token + 1, .none); // lparen
2204 try renderExpression(gpa, ais, tree, container_decl.ast.arg, .none);
2288 try renderToken(r, enum_token + 1, .none); // lparen
2289 try renderExpression(r, container_decl.ast.arg, .none);
22052290 const rparen = tree.lastToken(container_decl.ast.arg) + 1;
2206 try renderToken(ais, tree, rparen, .none); // rparen
2207 try renderToken(ais, tree, rparen + 1, .space); // rparen
2291 try renderToken(r, rparen, .none); // rparen
2292 try renderToken(r, rparen + 1, .space); // rparen
22082293 lbrace = rparen + 2;
22092294 } else {
2210 try renderToken(ais, tree, enum_token + 1, .space); // rparen
2295 try renderToken(r, enum_token + 1, .space); // rparen
22112296 lbrace = enum_token + 2;
22122297 }
22132298 } else if (container_decl.ast.arg != 0) {
2214 try renderToken(ais, tree, container_decl.ast.main_token, .none); // union
2215 try renderToken(ais, tree, container_decl.ast.main_token + 1, .none); // lparen
2216 try renderExpression(gpa, ais, tree, container_decl.ast.arg, .none);
2299 try renderToken(r, container_decl.ast.main_token, .none); // union
2300 try renderToken(r, container_decl.ast.main_token + 1, .none); // lparen
2301 try renderExpression(r, container_decl.ast.arg, .none);
22172302 const rparen = tree.lastToken(container_decl.ast.arg) + 1;
2218 try renderToken(ais, tree, rparen, .space); // rparen
2303 try renderToken(r, rparen, .space); // rparen
22192304 lbrace = rparen + 1;
22202305 } else {
2221 try renderToken(ais, tree, container_decl.ast.main_token, .space); // union
2306 try renderToken(r, container_decl.ast.main_token, .space); // union
22222307 lbrace = container_decl.ast.main_token + 1;
22232308 }
22242309
......@@ -2226,13 +2311,13 @@ fn renderContainerDecl(
22262311 if (container_decl.ast.members.len == 0) {
22272312 ais.pushIndentNextLine();
22282313 if (token_tags[lbrace + 1] == .container_doc_comment) {
2229 try renderToken(ais, tree, lbrace, .newline); // lbrace
2230 try renderContainerDocComments(ais, tree, lbrace + 1);
2314 try renderToken(r, lbrace, .newline); // lbrace
2315 try renderContainerDocComments(r, lbrace + 1);
22312316 } else {
2232 try renderToken(ais, tree, lbrace, .none); // lbrace
2317 try renderToken(r, lbrace, .none); // lbrace
22332318 }
22342319 ais.popIndent();
2235 return renderToken(ais, tree, rbrace, space); // rbrace
2320 return renderToken(r, rbrace, space); // rbrace
22362321 }
22372322
22382323 const src_has_trailing_comma = token_tags[rbrace - 1] == .comma;
......@@ -2258,52 +2343,52 @@ fn renderContainerDecl(
22582343 }
22592344
22602345 // Print all the declarations on the same line.
2261 try renderToken(ais, tree, lbrace, .space); // lbrace
2346 try renderToken(r, lbrace, .space); // lbrace
22622347 for (container_decl.ast.members) |member| {
2263 try renderMember(gpa, ais, tree, container, member, .space);
2348 try renderMember(r, container, member, .space);
22642349 }
2265 return renderToken(ais, tree, rbrace, space); // rbrace
2350 return renderToken(r, rbrace, space); // rbrace
22662351 }
22672352
22682353 // One member per line.
22692354 ais.pushIndentNextLine();
2270 try renderToken(ais, tree, lbrace, .newline); // lbrace
2355 try renderToken(r, lbrace, .newline); // lbrace
22712356 if (token_tags[lbrace + 1] == .container_doc_comment) {
2272 try renderContainerDocComments(ais, tree, lbrace + 1);
2357 try renderContainerDocComments(r, lbrace + 1);
22732358 }
22742359 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);
22762361 switch (tree.nodes.items(.tag)[member]) {
22772362 // For container fields, ensure a trailing comma is added if necessary.
22782363 .container_field_init,
22792364 .container_field_align,
22802365 .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),
22842369 }
22852370 }
22862371 ais.popIndent();
22872372
2288 return renderToken(ais, tree, rbrace, space); // rbrace
2373 return renderToken(r, rbrace, space); // rbrace
22892374}
22902375
22912376fn renderAsm(
2292 gpa: Allocator,
2293 ais: *Ais,
2294 tree: Ast,
2377 r: *Render,
22952378 asm_node: Ast.full.Asm,
22962379 space: Space,
22972380) Error!void {
2381 const tree = r.tree;
2382 const ais = r.ais;
22982383 const token_tags = tree.tokens.items(.tag);
22992384
2300 try renderToken(ais, tree, asm_node.ast.asm_token, .space); // asm
2385 try renderToken(r, asm_node.ast.asm_token, .space); // asm
23012386
23022387 if (asm_node.volatile_token) |volatile_token| {
2303 try renderToken(ais, tree, volatile_token, .space); // volatile
2304 try renderToken(ais, tree, volatile_token + 1, .none); // lparen
2388 try renderToken(r, volatile_token, .space); // volatile
2389 try renderToken(r, volatile_token + 1, .none); // lparen
23052390 } else {
2306 try renderToken(ais, tree, asm_node.ast.asm_token + 1, .none); // lparen
2391 try renderToken(r, asm_node.ast.asm_token + 1, .none); // lparen
23072392 }
23082393
23092394 if (asm_node.ast.items.len == 0) {
......@@ -2311,27 +2396,27 @@ fn renderAsm(
23112396 if (asm_node.first_clobber) |first_clobber| {
23122397 // asm ("foo" ::: "a", "b")
23132398 // asm ("foo" ::: "a", "b",)
2314 try renderExpression(gpa, ais, tree, asm_node.ast.template, .space);
2399 try renderExpression(r, asm_node.ast.template, .space);
23152400 // Render the three colons.
2316 try renderToken(ais, tree, first_clobber - 3, .none);
2317 try renderToken(ais, tree, first_clobber - 2, .none);
2318 try renderToken(ais, tree, first_clobber - 1, .space);
2401 try renderToken(r, first_clobber - 3, .none);
2402 try renderToken(r, first_clobber - 2, .none);
2403 try renderToken(r, first_clobber - 1, .space);
23192404
23202405 var tok_i = first_clobber;
23212406 while (true) : (tok_i += 1) {
2322 try renderToken(ais, tree, tok_i, .none);
2407 try renderToken(r, tok_i, .none);
23232408 tok_i += 1;
23242409 switch (token_tags[tok_i]) {
23252410 .r_paren => {
23262411 ais.popIndent();
2327 return renderToken(ais, tree, tok_i, space);
2412 return renderToken(r, tok_i, space);
23282413 },
23292414 .comma => {
23302415 if (token_tags[tok_i + 1] == .r_paren) {
23312416 ais.popIndent();
2332 return renderToken(ais, tree, tok_i + 1, space);
2417 return renderToken(r, tok_i + 1, space);
23332418 } else {
2334 try renderToken(ais, tree, tok_i, .space);
2419 try renderToken(r, tok_i, .space);
23352420 }
23362421 },
23372422 else => unreachable,
......@@ -2339,40 +2424,40 @@ fn renderAsm(
23392424 }
23402425 } else {
23412426 // asm ("foo")
2342 try renderExpression(gpa, ais, tree, asm_node.ast.template, .none);
2427 try renderExpression(r, asm_node.ast.template, .none);
23432428 ais.popIndent();
2344 return renderToken(ais, tree, asm_node.ast.rparen, space); // rparen
2429 return renderToken(r, asm_node.ast.rparen, space); // rparen
23452430 }
23462431 }
23472432
23482433 ais.pushIndent();
2349 try renderExpression(gpa, ais, tree, asm_node.ast.template, .newline);
2434 try renderExpression(r, asm_node.ast.template, .newline);
23502435 ais.setIndentDelta(asm_indent_delta);
23512436 const colon1 = tree.lastToken(asm_node.ast.template) + 1;
23522437
23532438 const colon2 = if (asm_node.outputs.len == 0) colon2: {
2354 try renderToken(ais, tree, colon1, .newline); // :
2439 try renderToken(r, colon1, .newline); // :
23552440 break :colon2 colon1 + 1;
23562441 } else colon2: {
2357 try renderToken(ais, tree, colon1, .space); // :
2442 try renderToken(r, colon1, .space); // :
23582443
23592444 ais.pushIndent();
23602445 for (asm_node.outputs, 0..) |asm_output, i| {
23612446 if (i + 1 < asm_node.outputs.len) {
23622447 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
23652450 const comma = tree.firstToken(next_asm_output) - 1;
2366 try renderToken(ais, tree, comma, .newline); // ,
2367 try renderExtraNewlineToken(ais, tree, tree.firstToken(next_asm_output));
2451 try renderToken(r, comma, .newline); // ,
2452 try renderExtraNewlineToken(r, tree.firstToken(next_asm_output));
23682453 } 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);
23702455 ais.popIndent();
23712456 ais.setIndentDelta(indent_delta);
23722457 ais.popIndent();
2373 return renderToken(ais, tree, asm_node.ast.rparen, space); // rparen
2458 return renderToken(r, asm_node.ast.rparen, space); // rparen
23742459 } else {
2375 try renderAsmOutput(gpa, ais, tree, asm_output, .comma);
2460 try renderAsmOutput(r, asm_output, .comma);
23762461 const comma_or_colon = tree.lastToken(asm_output) + 1;
23772462 ais.popIndent();
23782463 break :colon2 switch (token_tags[comma_or_colon]) {
......@@ -2384,27 +2469,27 @@ fn renderAsm(
23842469 };
23852470
23862471 const colon3 = if (asm_node.inputs.len == 0) colon3: {
2387 try renderToken(ais, tree, colon2, .newline); // :
2472 try renderToken(r, colon2, .newline); // :
23882473 break :colon3 colon2 + 1;
23892474 } else colon3: {
2390 try renderToken(ais, tree, colon2, .space); // :
2475 try renderToken(r, colon2, .space); // :
23912476 ais.pushIndent();
23922477 for (asm_node.inputs, 0..) |asm_input, i| {
23932478 if (i + 1 < asm_node.inputs.len) {
23942479 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
23972482 const first_token = tree.firstToken(next_asm_input);
2398 try renderToken(ais, tree, first_token - 1, .newline); // ,
2399 try renderExtraNewlineToken(ais, tree, first_token);
2483 try renderToken(r, first_token - 1, .newline); // ,
2484 try renderExtraNewlineToken(r, first_token);
24002485 } else if (asm_node.first_clobber == null) {
2401 try renderAsmInput(gpa, ais, tree, asm_input, .comma);
2486 try renderAsmInput(r, asm_input, .comma);
24022487 ais.popIndent();
24032488 ais.setIndentDelta(indent_delta);
24042489 ais.popIndent();
2405 return renderToken(ais, tree, asm_node.ast.rparen, space); // rparen
2490 return renderToken(r, asm_node.ast.rparen, space); // rparen
24062491 } else {
2407 try renderAsmInput(gpa, ais, tree, asm_input, .comma);
2492 try renderAsmInput(r, asm_input, .comma);
24082493 const comma_or_colon = tree.lastToken(asm_input) + 1;
24092494 ais.popIndent();
24102495 break :colon3 switch (token_tags[comma_or_colon]) {
......@@ -2416,7 +2501,7 @@ fn renderAsm(
24162501 unreachable;
24172502 };
24182503
2419 try renderToken(ais, tree, colon3, .space); // :
2504 try renderToken(r, colon3, .space); // :
24202505 const first_clobber = asm_node.first_clobber.?;
24212506 var tok_i = first_clobber;
24222507 while (true) {
......@@ -2424,20 +2509,20 @@ fn renderAsm(
24242509 .r_paren => {
24252510 ais.setIndentDelta(indent_delta);
24262511 ais.popIndent();
2427 try renderToken(ais, tree, tok_i, .newline);
2428 return renderToken(ais, tree, tok_i + 1, space);
2512 try renderToken(r, tok_i, .newline);
2513 return renderToken(r, tok_i + 1, space);
24292514 },
24302515 .comma => {
24312516 switch (token_tags[tok_i + 2]) {
24322517 .r_paren => {
24332518 ais.setIndentDelta(indent_delta);
24342519 ais.popIndent();
2435 try renderToken(ais, tree, tok_i, .newline);
2436 return renderToken(ais, tree, tok_i + 2, space);
2520 try renderToken(r, tok_i, .newline);
2521 return renderToken(r, tok_i + 2, space);
24372522 },
24382523 else => {
2439 try renderToken(ais, tree, tok_i, .none);
2440 try renderToken(ais, tree, tok_i + 1, .space);
2524 try renderToken(r, tok_i, .none);
2525 try renderToken(r, tok_i + 1, .space);
24412526 tok_i += 2;
24422527 },
24432528 }
......@@ -2448,44 +2533,42 @@ fn renderAsm(
24482533}
24492534
24502535fn renderCall(
2451 gpa: Allocator,
2452 ais: *Ais,
2453 tree: Ast,
2536 r: *Render,
24542537 call: Ast.full.Call,
24552538 space: Space,
24562539) Error!void {
24572540 if (call.async_token) |async_token| {
2458 try renderToken(ais, tree, async_token, .space);
2541 try renderToken(r, async_token, .space);
24592542 }
2460 try renderExpression(gpa, ais, tree, call.ast.fn_expr, .none);
2461 try renderParamList(gpa, ais, tree, call.ast.lparen, call.ast.params, space);
2543 try renderExpression(r, call.ast.fn_expr, .none);
2544 try renderParamList(r, call.ast.lparen, call.ast.params, space);
24622545}
24632546
24642547fn renderParamList(
2465 gpa: Allocator,
2466 ais: *Ais,
2467 tree: Ast,
2548 r: *Render,
24682549 lparen: Ast.TokenIndex,
24692550 params: []const Ast.Node.Index,
24702551 space: Space,
24712552) Error!void {
2553 const tree = r.tree;
2554 const ais = r.ais;
24722555 const token_tags = tree.tokens.items(.tag);
24732556
24742557 if (params.len == 0) {
24752558 ais.pushIndentNextLine();
2476 try renderToken(ais, tree, lparen, .none);
2559 try renderToken(r, lparen, .none);
24772560 ais.popIndent();
2478 return renderToken(ais, tree, lparen + 1, space); // )
2561 return renderToken(r, lparen + 1, space); // )
24792562 }
24802563
24812564 const last_param = params[params.len - 1];
24822565 const after_last_param_tok = tree.lastToken(last_param) + 1;
24832566 if (token_tags[after_last_param_tok] == .comma) {
24842567 ais.pushIndentNextLine();
2485 try renderToken(ais, tree, lparen, .newline); // (
2568 try renderToken(r, lparen, .newline); // (
24862569 for (params, 0..) |param_node, i| {
24872570 if (i + 1 < params.len) {
2488 try renderExpression(gpa, ais, tree, param_node, .none);
2571 try renderExpression(r, param_node, .none);
24892572
24902573 // Unindent the comma for multiline string literals.
24912574 const is_multiline_string =
......@@ -2493,20 +2576,20 @@ fn renderParamList(
24932576 if (is_multiline_string) ais.popIndent();
24942577
24952578 const comma = tree.lastToken(param_node) + 1;
2496 try renderToken(ais, tree, comma, .newline); // ,
2579 try renderToken(r, comma, .newline); // ,
24972580
24982581 if (is_multiline_string) ais.pushIndent();
24992582
2500 try renderExtraNewline(ais, tree, params[i + 1]);
2583 try renderExtraNewline(r, params[i + 1]);
25012584 } else {
2502 try renderExpression(gpa, ais, tree, param_node, .comma);
2585 try renderExpression(r, param_node, .comma);
25032586 }
25042587 }
25052588 ais.popIndent();
2506 return renderToken(ais, tree, after_last_param_tok + 1, space); // )
2589 return renderToken(r, after_last_param_tok + 1, space); // )
25072590 }
25082591
2509 try renderToken(ais, tree, lparen, .none); // (
2592 try renderToken(r, lparen, .none); // (
25102593
25112594 for (params, 0..) |param_node, i| {
25122595 const first_param_token = tree.firstToken(param_node);
......@@ -2515,23 +2598,25 @@ fn renderParamList(
25152598 {
25162599 ais.pushIndentOneShot();
25172600 }
2518 try renderExpression(gpa, ais, tree, param_node, .none);
2601 try renderExpression(r, param_node, .none);
25192602
25202603 if (i + 1 < params.len) {
25212604 const comma = tree.lastToken(param_node) + 1;
25222605 const next_multiline_string =
25232606 token_tags[tree.firstToken(params[i + 1])] == .multiline_string_literal_line;
25242607 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);
25262609 }
25272610 }
25282611
2529 return renderToken(ais, tree, after_last_param_tok, space); // )
2612 return renderToken(r, after_last_param_tok, space); // )
25302613}
25312614
25322615/// Renders the given expression indented, popping the indent before rendering
25332616/// 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;
25352620 const token_starts = tree.tokens.items(.start);
25362621 const token_tags = tree.tokens.items(.tag);
25372622
......@@ -2545,24 +2630,24 @@ fn renderExpressionIndented(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node
25452630 .semicolon => token_tags[last_token + 1] == .semicolon,
25462631 };
25472632
2548 try renderExpression(gpa, ais, tree, node, if (punctuation) .none else .skip);
2633 try renderExpression(r, node, if (punctuation) .none else .skip);
25492634
25502635 switch (space) {
25512636 .none, .space, .newline, .skip => {},
25522637 .comma => {
25532638 if (token_tags[last_token + 1] == .comma) {
2554 try renderToken(ais, tree, last_token + 1, .skip);
2639 try renderToken(r, last_token + 1, .skip);
25552640 last_token += 1;
25562641 } else {
25572642 try ais.writer().writeByte(',');
25582643 }
25592644 },
25602645 .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);
25622647 last_token += 1;
25632648 },
25642649 .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);
25662651 last_token += 1;
25672652 },
25682653 }
......@@ -2572,7 +2657,7 @@ fn renderExpressionIndented(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node
25722657 if (space == .skip) return;
25732658
25742659 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
25772662 if (!comment) switch (space) {
25782663 .none => {},
......@@ -2589,40 +2674,43 @@ fn renderExpressionIndented(gpa: Allocator, ais: *Ais, tree: Ast, node: Ast.Node
25892674
25902675/// Render an expression, and the comma that follows it, if it is present in the source.
25912676/// 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;
25932679 const token_tags = tree.tokens.items(.tag);
25942680 const maybe_comma = tree.lastToken(node) + 1;
25952681 if (token_tags[maybe_comma] == .comma and space != .comma) {
2596 try renderExpression(gpa, ais, tree, node, .none);
2597 return renderToken(ais, tree, maybe_comma, space);
2682 try renderExpression(r, node, .none);
2683 return renderToken(r, maybe_comma, space);
25982684 } else {
2599 return renderExpression(gpa, ais, tree, node, space);
2685 return renderExpression(r, node, space);
26002686 }
26012687}
26022688
26032689/// Render a token, and the comma that follows it, if it is present in the source.
26042690/// 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;
26062693 const token_tags = tree.tokens.items(.tag);
26072694 const maybe_comma = token + 1;
26082695 if (token_tags[maybe_comma] == .comma and space != .comma) {
2609 try renderToken(ais, tree, token, .none);
2610 return renderToken(ais, tree, maybe_comma, space);
2696 try renderToken(r, token, .none);
2697 return renderToken(r, maybe_comma, space);
26112698 } else {
2612 return renderToken(ais, tree, token, space);
2699 return renderToken(r, token, space);
26132700 }
26142701}
26152702
26162703/// Render an identifier, and the comma that follows it, if it is present in the source.
26172704/// 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;
26192707 const token_tags = tree.tokens.items(.tag);
26202708 const maybe_comma = token + 1;
26212709 if (token_tags[maybe_comma] == .comma and space != .comma) {
2622 try renderIdentifier(ais, tree, token, .none, quote);
2623 return renderToken(ais, tree, maybe_comma, space);
2710 try renderIdentifier(r, token, .none, quote);
2711 return renderToken(r, maybe_comma, space);
26242712 } else {
2625 return renderIdentifier(ais, tree, token, space, quote);
2713 return renderIdentifier(r, token, space, quote);
26262714 }
26272715}
26282716
......@@ -2647,13 +2735,17 @@ const Space = enum {
26472735 skip,
26482736};
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;
26512741 const lexeme = tokenSliceForRender(tree, token_index);
26522742 try ais.writer().writeAll(lexeme);
2653 try renderSpace(ais, tree, token_index, lexeme.len, space);
2743 try renderSpace(r, token_index, lexeme.len, space);
26542744}
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;
26572749 const token_tags = tree.tokens.items(.tag);
26582750 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
26652757 try ais.writer().writeByte(',');
26662758 }
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]);
26692761 switch (space) {
26702762 .none => {},
26712763 .space => if (!comment) try ais.writer().writeByte(' '),
26722764 .newline => if (!comment) try ais.insertNewline(),
26732765
26742766 .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);
26762768 } else if (!comment) {
26772769 try ais.insertNewline();
26782770 },
26792771
26802772 .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);
26822774 } else if (!comment) {
26832775 try ais.writer().writeByte(' ');
26842776 },
26852777
26862778 .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);
26882780 } else if (!comment) {
26892781 try ais.insertNewline();
26902782 },
......@@ -2693,18 +2785,32 @@ fn renderSpace(ais: *Ais, tree: Ast, token_index: Ast.TokenIndex, lexeme_len: us
26932785 }
26942786}
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
26962801const QuoteBehavior = enum {
26972802 preserve_when_shadowing,
26982803 eagerly_unquote,
26992804 eagerly_unquote_except_underscore,
27002805};
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;
27032809 const token_tags = tree.tokens.items(.tag);
27042810 assert(token_tags[token_index] == .identifier);
27052811 const lexeme = tokenSliceForRender(tree, token_index);
27062812 if (lexeme[0] != '@') {
2707 return renderToken(ais, tree, token_index, space);
2813 return renderToken(r, token_index, space);
27082814 }
27092815
27102816 assert(lexeme.len >= 3);
......@@ -2715,15 +2821,15 @@ fn renderIdentifier(ais: *Ais, tree: Ast, token_index: Ast.TokenIndex, space: Sp
27152821
27162822 // Empty name can't be unquoted.
27172823 if (contents.len == 0) {
2718 return renderQuotedIdentifier(ais, tree, token_index, space, false);
2824 return renderQuotedIdentifier(r, token_index, space, false);
27192825 }
27202826
27212827 // Special case for _ which would incorrectly be rejected by isValidId below.
27222828 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),
27242830 .eagerly_unquote_except_underscore,
27252831 .preserve_when_shadowing,
2726 => return renderQuotedIdentifier(ais, tree, token_index, space, false),
2832 => return renderQuotedIdentifier(r, token_index, space, false),
27272833 };
27282834
27292835 // 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
27312837 var contents_i: usize = 0;
27322838 while (contents_i < contents.len) {
27332839 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),
27352841 'A'...'Z', 'a'...'z', '_' => {},
27362842 '\\' => {
27372843 var esc_offset = contents_i;
27382844 const res = std.zig.string_literal.parseEscapeSequence(contents, &esc_offset);
27392845 switch (res) {
27402846 .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),
27422848 'A'...'Z', 'a'...'z', '_' => {},
2743 else => return renderQuotedIdentifier(ais, tree, token_index, space, false),
2849 else => return renderQuotedIdentifier(r, token_index, space, false),
27442850 },
2745 .failure => return renderQuotedIdentifier(ais, tree, token_index, space, false),
2851 .failure => return renderQuotedIdentifier(r, token_index, space, false),
27462852 }
27472853 contents_i += esc_offset;
27482854 continue;
27492855 },
2750 else => return renderQuotedIdentifier(ais, tree, token_index, space, false),
2856 else => return renderQuotedIdentifier(r, token_index, space, false),
27512857 }
27522858 contents_i += 1;
27532859 }
......@@ -2784,23 +2890,25 @@ fn renderIdentifier(ais: *Ais, tree: Ast, token_index: Ast.TokenIndex, space: Sp
27842890 // We read the whole thing, so it could be a keyword or primitive.
27852891 if (contents_i == contents.len) {
27862892 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);
27882894 }
27892895 if (primitives.isPrimitive(buf[0..buf_i])) switch (quote) {
27902896 .eagerly_unquote,
27912897 .eagerly_unquote_except_underscore,
2792 => return renderQuotedIdentifier(ais, tree, token_index, space, true),
2793 .preserve_when_shadowing => return renderQuotedIdentifier(ais, tree, token_index, space, false),
2898 => return renderQuotedIdentifier(r, token_index, space, true),
2899 .preserve_when_shadowing => return renderQuotedIdentifier(r, token_index, space, false),
27942900 };
27952901 }
27962902
2797 try renderQuotedIdentifier(ais, tree, token_index, space, true);
2903 try renderQuotedIdentifier(r, token_index, space, true);
27982904}
27992905
28002906// Renders a @"" quoted identifier, normalizing escapes.
28012907// Unnecessary escapes are un-escaped, and \u escapes are normalized to \x when they fit.
28022908// 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;
28042912 const token_tags = tree.tokens.items(.tag);
28052913 assert(token_tags[token_index] == .identifier);
28062914 const lexeme = tokenSliceForRender(tree, token_index);
......@@ -2811,7 +2919,7 @@ fn renderQuotedIdentifier(ais: *Ais, tree: Ast, token_index: Ast.TokenIndex, spa
28112919 try renderIdentifierContents(ais.writer(), contents);
28122920 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);
28152923}
28162924
28172925fn renderIdentifierContents(writer: anytype, bytes: []const u8) !void {
......@@ -2884,7 +2992,10 @@ fn hasMultilineString(tree: Ast, start_token: Ast.TokenIndex, end_token: Ast.Tok
28842992
28852993/// Assumes that start is the first byte past the previous token and
28862994/// 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
28882999 var index: usize = start;
28893000 while (mem.indexOf(u8, tree.source[index..end], "//")) |offset| {
28903001 const comment_start = index + offset;
......@@ -2944,12 +3055,14 @@ fn renderComments(ais: *Ais, tree: Ast, start: usize, end: usize) Error!bool {
29443055 return index != start;
29453056}
29463057
2947fn renderExtraNewline(ais: *Ais, tree: Ast, node: Ast.Node.Index) Error!void {
2948 return renderExtraNewlineToken(ais, tree, tree.firstToken(node));
3058fn renderExtraNewline(r: *Render, node: Ast.Node.Index) Error!void {
3059 return renderExtraNewlineToken(r, r.tree.firstToken(node));
29493060}
29503061
29513062/// 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;
29533066 const token_starts = tree.tokens.items(.start);
29543067 const token_start = token_starts[token_index];
29553068 if (token_start == 0) return;
......@@ -2974,7 +3087,8 @@ fn renderExtraNewlineToken(ais: *Ais, tree: Ast, token_index: Ast.TokenIndex) Er
29743087
29753088/// end_token is the token one past the last doc comment token. This function
29763089/// 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;
29783092 // Search backwards for the first doc comment.
29793093 const token_tags = tree.tokens.items(.tag);
29803094 if (end_token == 0) return;
......@@ -2995,27 +3109,45 @@ fn renderDocComments(ais: *Ais, tree: Ast, end_token: Ast.TokenIndex) Error!void
29953109 assert(prev_token_tag != .l_paren);
29963110
29973111 if (prev_token_tag != .l_brace) {
2998 try renderExtraNewlineToken(ais, tree, first_tok);
3112 try renderExtraNewlineToken(r, first_tok);
29993113 }
30003114 }
30013115
30023116 while (token_tags[tok] == .doc_comment) : (tok += 1) {
3003 try renderToken(ais, tree, tok, .newline);
3117 try renderToken(r, tok, .newline);
30043118 }
30053119}
30063120
30073121/// 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;
30093124 const token_tags = tree.tokens.items(.tag);
30103125 var tok = start_token;
30113126 while (token_tags[tok] == .container_doc_comment) : (tok += 1) {
3012 try renderToken(ais, tree, tok, .newline);
3127 try renderToken(r, tok, .newline);
30133128 }
30143129 // Render extra newline if there is one between final container doc comment and
30153130 // the next token. If the next token is a doc comment, that code path
30163131 // will have its own logic to insert a newline.
30173132 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");
30193151 }
30203152}
30213153
src/main.zig+11-1
......@@ -212,6 +212,14 @@ pub fn main() anyerror!void {
212212 }
213213 }
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
215223 return mainArgs(gpa, arena, args);
216224}
217225
......@@ -328,6 +336,8 @@ pub fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi
328336 } else if (mem.eql(u8, cmd, "env")) {
329337 verifyLibcxxCorrectlyLinked();
330338 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);
331341 } else if (mem.eql(u8, cmd, "zen")) {
332342 return io.getStdOut().writeAll(info_zen);
333343 } 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(
57665776 fmt.out_buffer.shrinkRetainingCapacity(0);
57675777 try fmt.out_buffer.ensureTotalCapacity(source_code.len);
57685778
5769 try tree.renderToArrayList(&fmt.out_buffer);
5779 try tree.renderToArrayList(&fmt.out_buffer, .{});
57705780 if (mem.eql(u8, fmt.out_buffer.items, source_code))
57715781 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;
1414pub const only_c = false;
1515pub const force_gpa = false;
1616pub const only_core_functionality = true;
17pub const only_reduce = false;