1use crate::records::ast_array::AstArray;
2use crate::records::ast_attr::AstAttr;
3use crate::records::ast_class_method::AstClassMethod;
4use crate::records::ast_class_property::AstClassProperty;
5use crate::records::ast_expr::AstExpr;
6use crate::records::ast_expr_binary::AstExprBinary;
7use crate::records::ast_expr_binary::AstExprBinary_Op;
8use crate::records::ast_local::AstLocal;
9use crate::records::ast_stat::AstStat;
10use crate::records::ast_stat_assign::AstStatAssign;
11use crate::records::ast_stat_block::AstStatBlock;
12use crate::records::ast_stat_break::AstStatBreak;
13use crate::records::ast_stat_class::AstStatClass;
14use crate::records::ast_stat_compound_assign::AstStatCompoundAssign;
15use crate::records::ast_stat_continue::AstStatContinue;
16use crate::records::ast_stat_declare_global::AstStatDeclareGlobal;
17use crate::records::ast_stat_error::AstStatError;
18use crate::records::ast_stat_expr::AstStatExpr;
19use crate::records::ast_stat_for::AstStatFor;
20use crate::records::ast_stat_for_in::AstStatForIn;
21use crate::records::ast_stat_function::AstStatFunction;
22use crate::records::ast_stat_if::AstStatIf;
23use crate::records::ast_stat_local::AstStatLocal;
24use crate::records::ast_stat_local_function::AstStatLocalFunction;
25use crate::records::ast_stat_repeat::AstStatRepeat;
26use crate::records::ast_stat_return::AstStatReturn;
27use crate::records::ast_stat_type_alias::AstStatTypeAlias;
28use crate::records::ast_stat_type_function::AstStatTypeFunction;
29use crate::records::ast_stat_while::AstStatWhile;
30use crate::records::comma_separator_inserter::CommaSeparatorInserter;
31use crate::records::cst_generic_type::CstGenericType;
32use crate::records::cst_generic_type_pack::CstGenericTypePack;
33use crate::records::cst_stat_assign::CstStatAssign;
34use crate::records::cst_stat_compound_assign::CstStatCompoundAssign;
35use crate::records::cst_stat_do::CstStatDo;
36use crate::records::cst_stat_for::CstStatFor;
37use crate::records::cst_stat_for_in::CstStatForIn;
38use crate::records::cst_stat_function::CstStatFunction;
39use crate::records::cst_stat_local::CstStatLocal;
40use crate::records::cst_stat_local_function::CstStatLocalFunction;
41use crate::records::cst_stat_repeat::CstStatRepeat;
42use crate::records::cst_stat_return::CstStatReturn;
43use crate::records::cst_stat_type_alias::CstStatTypeAlias;
44use crate::records::cst_stat_type_function::CstStatTypeFunction;
45use crate::records::position::Position;
46use crate::records::printer::Printer;
47use crate::records::string_writer::StringWriter;
48use crate::records::writer::Writer;
49use crate::rtti::CstNodeClass;
50use crate::visit::ast_expr_visit;
51use crate::visit::ast_stat_visit;
52use luaur_common::functions::visit_variant::visit;
53use luaur_common::records::overloaded;
54use luaur_common::records::variant::Variant2;
55use luaur_common::FFlag;
56use luaur_common::LUAU_ASSERT;
57
58pub trait IntoAstStatPrinter {
59 unsafe fn into_ast_stat_mut(self) -> *mut AstStat;
60}
61
62impl IntoAstStatPrinter for &mut AstStat {
63 unsafe fn into_ast_stat_mut(self) -> *mut AstStat {
64 self
65 }
66}
67
68impl IntoAstStatPrinter for *mut AstStat {
69 unsafe fn into_ast_stat_mut(self) -> *mut AstStat {
70 self
71 }
72}
73
74impl IntoAstStatPrinter for &*mut AstStat {
75 unsafe fn into_ast_stat_mut(self) -> *mut AstStat {
76 *self
77 }
78}
79
80impl<'a> Printer<'a> {
81 #[allow(non_snake_case)]
82 pub fn visualize_ast_stat<S: IntoAstStatPrinter>(&mut self, program: S) {
83 let program = unsafe { &mut *program.into_ast_stat_mut() };
84 self.advance(&program.base.location.begin);
85
86 if let Some(block) = unsafe {
87 crate::rtti::ast_node_as::<AstStatBlock>(
88 program as *mut AstStat as *mut crate::records::ast_node::AstNode,
89 )
90 .as_mut()
91 } {
92 let cst_node = self.lookup_cst_node::<CstStatDo>(
93 program as *mut AstStat as *mut crate::records::ast_node::AstNode,
94 );
95 if !cst_node.is_null() {
96 self.writer.keyword("do");
97 self.advance(&unsafe { (*cst_node).stats_start_position });
98 for s in unsafe { crate::records::ast_array::AstArray::iter(&block.body) } {
99 self.visualize_ast_stat(s);
100 }
101 self.maybe_advance_and_write(unsafe { &(*cst_node).end_position }, "end", false);
102 } else {
103 for s in unsafe { crate::records::ast_array::AstArray::iter(&block.body) } {
104 self.visualize_ast_stat(s);
105 }
106 self.advance(&block.base.base.location.end);
107 self.write_end(&program.base.location);
108 }
109 } else if let Some(a) = unsafe {
110 crate::rtti::ast_node_as::<AstStatIf>(
111 program as *mut AstStat as *mut crate::records::ast_node::AstNode,
112 )
113 .as_mut()
114 } {
115 self.writer.keyword("if");
116 self.visualize_else_if(a);
117 } else if let Some(a) = unsafe {
118 crate::rtti::ast_node_as::<AstStatWhile>(
119 program as *mut AstStat as *mut crate::records::ast_node::AstNode,
120 )
121 .as_mut()
122 } {
123 self.writer.keyword("while");
124 self.visualize_ast_expr(a.condition);
125 self.advance(&a.do_location.begin);
126 self.writer.keyword("do");
127 self.visualize_block_ast_stat_block(a.body);
128 self.advance(&unsafe { (*a.body).base.base.location.end });
129 self.writer.keyword("end");
130 } else if let Some(a) = unsafe {
131 crate::rtti::ast_node_as::<AstStatRepeat>(
132 program as *mut AstStat as *mut crate::records::ast_node::AstNode,
133 )
134 .as_mut()
135 } {
136 self.writer.keyword("repeat");
137 self.visualize_block_ast_stat_block(a.body);
138 let cst_node = self.lookup_cst_node::<CstStatRepeat>(
139 program as *mut AstStat as *mut crate::records::ast_node::AstNode,
140 );
141 if !cst_node.is_null() {
142 self.maybe_advance_and_write(
143 unsafe { &(*cst_node).until_position },
144 "until",
145 false,
146 );
147 } else {
148 self.advance_before(unsafe { (*a.condition).base.location.begin }, 6);
149 self.writer.keyword("until");
150 }
151 self.visualize_ast_expr(a.condition);
152 } else if unsafe {
153 crate::rtti::ast_node_is::<AstStatBreak>(
154 program as *mut AstStat as *mut crate::records::ast_node::AstNode,
155 )
156 } {
157 self.writer.keyword("break");
158 } else if unsafe {
159 crate::rtti::ast_node_is::<AstStatContinue>(
160 program as *mut AstStat as *mut crate::records::ast_node::AstNode,
161 )
162 } {
163 self.writer.keyword("continue");
164 } else if let Some(a) = unsafe {
165 crate::rtti::ast_node_as::<AstStatReturn>(
166 program as *mut AstStat as *mut crate::records::ast_node::AstNode,
167 )
168 .as_mut()
169 } {
170 let cst_node = self.lookup_cst_node::<CstStatReturn>(
171 program as *mut AstStat as *mut crate::records::ast_node::AstNode,
172 );
173 self.writer.keyword("return");
174 let mut comma = CommaSeparatorInserter::new(
175 self.writer,
176 if !cst_node.is_null() {
177 unsafe { (*cst_node).comma_positions.data }
178 } else {
179 core::ptr::null()
180 },
181 );
182 for expr in unsafe { crate::records::ast_array::AstArray::iter(&a.list) } {
183 comma.operator_call(self.writer);
184 self.visualize_ast_expr(expr);
185 }
186 } else if let Some(a) = unsafe {
187 crate::rtti::ast_node_as::<AstStatExpr>(
188 program as *mut AstStat as *mut crate::records::ast_node::AstNode,
189 )
190 .as_mut()
191 } {
192 self.visualize_ast_expr(a.expr);
193 } else if let Some(a) = unsafe {
194 crate::rtti::ast_node_as::<AstStatLocal>(
195 program as *mut AstStat as *mut crate::records::ast_node::AstNode,
196 )
197 .as_mut()
198 } {
199 let cst_node = self.lookup_cst_node::<CstStatLocal>(
200 program as *mut AstStat as *mut crate::records::ast_node::AstNode,
201 );
202 if FFlag::LuauExportValueSyntax.get() && FFlag::LuauConst2.get() && a.is_exported {
203 self.writer.keyword("export");
204 if !cst_node.is_null() {
205 self.advance(unsafe { (*cst_node).declaration_keyword_position });
206 }
207 self.writer
208 .keyword(if a.is_const { "const" } else { "local" });
209 } else if FFlag::LuauConst2.get() && a.is_const {
210 self.writer.keyword("const");
211 } else {
212 self.writer.keyword("local");
213 }
214 let mut var_comma = CommaSeparatorInserter::new(
215 self.writer,
216 if !cst_node.is_null() {
217 unsafe { (*cst_node).vars_comma_positions.data }
218 } else {
219 core::ptr::null()
220 },
221 );
222 for i in 0..a.vars.size {
223 var_comma.operator_call(self.writer);
224 if !cst_node.is_null() {
225 LUAU_ASSERT!(unsafe { (*cst_node).vars_annotation_colon_positions.size > i });
226 self.visualize_ast_local_position(
227 unsafe { &*(*a.vars.data.add(i as usize)) },
228 unsafe {
229 *(*cst_node)
230 .vars_annotation_colon_positions
231 .data
232 .add(i as usize)
233 },
234 );
235 } else {
236 self.visualize_ast_local_position(
237 unsafe { &*(*a.vars.data.add(i as usize)) },
238 Position::missing(),
239 );
240 }
241 }
242 if let Some(loc) = a.equals_sign_location {
243 self.advance(&loc.begin);
244 self.writer.symbol("=");
245 }
246 let mut value_comma = CommaSeparatorInserter::new(
247 self.writer,
248 if !cst_node.is_null() {
249 unsafe { (*cst_node).values_comma_positions.data }
250 } else {
251 core::ptr::null()
252 },
253 );
254 for value in unsafe { crate::records::ast_array::AstArray::iter(&a.values) } {
255 value_comma.operator_call(self.writer);
256 self.visualize_ast_expr(value);
257 }
258 } else if let Some(a) = unsafe {
259 crate::rtti::ast_node_as::<AstStatFor>(
260 program as *mut AstStat as *mut crate::records::ast_node::AstNode,
261 )
262 .as_mut()
263 } {
264 let cst_node = self.lookup_cst_node::<CstStatFor>(
265 program as *mut AstStat as *mut crate::records::ast_node::AstNode,
266 );
267 self.writer.keyword("for");
268 self.visualize_ast_local_position(
269 unsafe { &*a.var },
270 if !cst_node.is_null() {
271 unsafe { (*cst_node).annotation_colon_position }
272 } else {
273 Position::missing()
274 },
275 );
276 if !cst_node.is_null() {
277 self.advance(&unsafe { (*cst_node).equals_position });
278 }
279 self.writer.symbol("=");
280 self.visualize_ast_expr(a.from);
281 if !cst_node.is_null() {
282 self.maybe_advance_and_write(
283 &unsafe { (*cst_node).end_comma_position },
284 ",",
285 false,
286 );
287 } else {
288 self.writer.symbol(",");
289 }
290 self.visualize_ast_expr(a.to);
291 if !a.step.is_null() {
292 if !cst_node.is_null() {
293 self.advance(unsafe { (*cst_node).step_comma_position });
294 }
295 self.writer.symbol(",");
296 self.visualize_ast_expr(a.step);
297 }
298 self.advance(&a.do_location.begin);
299 self.writer.keyword("do");
300 self.visualize_block_ast_stat_block(a.body);
301 self.advance(&unsafe { (*a.body).base.base.location.end });
302 self.writer.keyword("end");
303 } else if let Some(a) = unsafe {
304 crate::rtti::ast_node_as::<AstStatForIn>(
305 program as *mut AstStat as *mut crate::records::ast_node::AstNode,
306 )
307 .as_mut()
308 } {
309 let cst_node = self.lookup_cst_node::<CstStatForIn>(
310 program as *mut AstStat as *mut crate::records::ast_node::AstNode,
311 );
312 self.writer.keyword("for");
313 let mut var_comma = CommaSeparatorInserter::new(
314 self.writer,
315 if !cst_node.is_null() {
316 unsafe { (*cst_node).vars_comma_positions.data }
317 } else {
318 core::ptr::null()
319 },
320 );
321 for i in 0..a.vars.size {
322 var_comma.operator_call(self.writer);
323 if !cst_node.is_null() {
324 LUAU_ASSERT!(unsafe { (*cst_node).vars_annotation_colon_positions.size > i });
325 self.visualize_ast_local_position(
326 unsafe { &*(*a.vars.data.add(i as usize)) },
327 unsafe {
328 *(*cst_node)
329 .vars_annotation_colon_positions
330 .data
331 .add(i as usize)
332 },
333 );
334 } else {
335 self.visualize_ast_local_position(
336 unsafe { &*(*a.vars.data.add(i as usize)) },
337 Position::missing(),
338 );
339 }
340 }
341 self.advance(&a.in_location.begin);
342 self.writer.keyword("in");
343 let mut val_comma = CommaSeparatorInserter::new(
344 self.writer,
345 if !cst_node.is_null() {
346 unsafe { (*cst_node).values_comma_positions.data }
347 } else {
348 core::ptr::null()
349 },
350 );
351 for val in unsafe { crate::records::ast_array::AstArray::iter(&a.values) } {
352 val_comma.operator_call(self.writer);
353 self.visualize_ast_expr(val);
354 }
355 self.advance(&a.do_location.begin);
356 self.writer.keyword("do");
357 self.visualize_block_ast_stat_block(a.body);
358 self.advance(&unsafe { (*a.body).base.base.location.end });
359 self.writer.keyword("end");
360 } else if let Some(a) = unsafe {
361 crate::rtti::ast_node_as::<AstStatAssign>(
362 program as *mut AstStat as *mut crate::records::ast_node::AstNode,
363 )
364 .as_mut()
365 } {
366 let cst_node = self.lookup_cst_node::<CstStatAssign>(
367 program as *mut AstStat as *mut crate::records::ast_node::AstNode,
368 );
369 let mut var_comma = CommaSeparatorInserter::new(
370 self.writer,
371 if !cst_node.is_null() {
372 unsafe { (*cst_node).vars_comma_positions.data }
373 } else {
374 core::ptr::null()
375 },
376 );
377 for var in unsafe { crate::records::ast_array::AstArray::iter(&a.vars) } {
378 var_comma.operator_call(self.writer);
379 self.visualize_ast_expr(var);
380 }
381 if !cst_node.is_null() {
382 self.maybe_advance_and_write(&unsafe { (*cst_node).equals_position }, "=", false);
383 } else {
384 self.writer.space();
385 self.writer.symbol("=");
386 }
387 let mut value_comma = CommaSeparatorInserter::new(
388 self.writer,
389 if !cst_node.is_null() {
390 unsafe { (*cst_node).values_comma_positions.data }
391 } else {
392 core::ptr::null()
393 },
394 );
395 for value in unsafe { crate::records::ast_array::AstArray::iter(&a.values) } {
396 value_comma.operator_call(self.writer);
397 self.visualize_ast_expr(value);
398 }
399 } else if let Some(a) = unsafe {
400 crate::rtti::ast_node_as::<AstStatCompoundAssign>(
401 program as *mut AstStat as *mut crate::records::ast_node::AstNode,
402 )
403 .as_mut()
404 } {
405 let cst_node = self.lookup_cst_node::<CstStatCompoundAssign>(
406 program as *mut AstStat as *mut crate::records::ast_node::AstNode,
407 );
408 self.visualize_ast_expr(a.var);
409 if !cst_node.is_null() {
410 self.advance(unsafe { (*cst_node).op_position });
411 }
412 match a.op {
413 AstExprBinary_Op::Add => {
414 if cst_node.is_null() {
415 self.writer
416 .maybe_space(&unsafe { (*a.value).base.location.begin }, 2);
417 }
418 self.writer.symbol("+=");
419 }
420 AstExprBinary_Op::Sub => {
421 if cst_node.is_null() {
422 self.writer
423 .maybe_space(&unsafe { (*a.value).base.location.begin }, 2);
424 }
425 self.writer.symbol("-=");
426 }
427 AstExprBinary_Op::Mul => {
428 if cst_node.is_null() {
429 self.writer
430 .maybe_space(&unsafe { (*a.value).base.location.begin }, 2);
431 }
432 self.writer.symbol("*=");
433 }
434 AstExprBinary_Op::Div => {
435 if cst_node.is_null() {
436 self.writer
437 .maybe_space(&unsafe { (*a.value).base.location.begin }, 2);
438 }
439 self.writer.symbol("/=");
440 }
441 AstExprBinary_Op::FloorDiv => {
442 if cst_node.is_null() {
443 self.writer
444 .maybe_space(&unsafe { (*a.value).base.location.begin }, 3);
445 }
446 self.writer.symbol("//=");
447 }
448 AstExprBinary_Op::Mod => {
449 if cst_node.is_null() {
450 self.writer
451 .maybe_space(&unsafe { (*a.value).base.location.begin }, 2);
452 }
453 self.writer.symbol("%=");
454 }
455 AstExprBinary_Op::Pow => {
456 if cst_node.is_null() {
457 self.writer
458 .maybe_space(&unsafe { (*a.value).base.location.begin }, 2);
459 }
460 self.writer.symbol("^=");
461 }
462 AstExprBinary_Op::Concat => {
463 if cst_node.is_null() {
464 self.writer
465 .maybe_space(&unsafe { (*a.value).base.location.begin }, 3);
466 }
467 self.writer.symbol("..=");
468 }
469 _ => {
470 LUAU_ASSERT!(false);
471 }
472 }
473 self.visualize_ast_expr(a.value);
474 } else if let Some(a) = unsafe {
475 crate::rtti::ast_node_as::<AstStatFunction>(
476 program as *mut AstStat as *mut crate::records::ast_node::AstNode,
477 )
478 .as_mut()
479 } {
480 for attr in unsafe { crate::records::ast_array::AstArray::iter(&(*a.func).attributes) }
481 {
482 self.visualize_attribute(unsafe { &mut **attr });
483 }
484 let cst_node = self.lookup_cst_node::<CstStatFunction>(
485 program as *mut AstStat as *mut crate::records::ast_node::AstNode,
486 );
487 if !cst_node.is_null() {
488 self.advance(unsafe { (*cst_node).function_keyword_position });
489 }
490 self.writer.keyword("function");
491 self.visualize_ast_expr(a.name);
492 self.visualize_function_body(a.func);
493 } else if let Some(a) = unsafe {
494 crate::rtti::ast_node_as::<AstStatLocalFunction>(
495 program as *mut AstStat as *mut crate::records::ast_node::AstNode,
496 )
497 .as_mut()
498 } {
499 for attr in unsafe { crate::records::ast_array::AstArray::iter(&(*a.func).attributes) }
500 {
501 self.visualize_attribute(unsafe { &mut **attr });
502 }
503 let cst_node = self.lookup_cst_node::<CstStatLocalFunction>(
504 program as *mut AstStat as *mut crate::records::ast_node::AstNode,
505 );
506 if !cst_node.is_null() {
507 self.advance(unsafe { (*cst_node).local_keyword_position });
508 }
509 if FFlag::LuauExportValueSyntax.get()
510 && FFlag::LuauConst2.get()
511 && unsafe { (*a.name).is_exported }
512 {
513 self.writer.keyword("export");
514 } else if FFlag::LuauConst2.get() && unsafe { (*a.name).is_const } {
515 self.writer.keyword("const");
516 } else {
517 self.writer.keyword("local");
518 }
519 if !cst_node.is_null() {
520 self.advance(unsafe { (*cst_node).function_keyword_position });
521 } else {
522 self.writer.space();
523 }
524 self.writer.keyword("function");
525 self.advance(unsafe { (*a.name).location.begin });
526 let name = unsafe { core::ffi::CStr::from_ptr((*a.name).name.value).to_string_lossy() };
527 self.writer.identifier(&name);
528 self.visualize_function_body(a.func);
529 } else if let Some(a) = unsafe {
530 crate::rtti::ast_node_as::<AstStatTypeAlias>(
531 program as *mut AstStat as *mut crate::records::ast_node::AstNode,
532 )
533 .as_mut()
534 } {
535 if self.write_types {
536 let cst_node = self.lookup_cst_node::<CstStatTypeAlias>(
537 program as *mut AstStat as *mut crate::records::ast_node::AstNode,
538 );
539 if a.exported {
540 self.writer.keyword("export");
541 }
542 if !cst_node.is_null() {
543 self.advance(unsafe { (*cst_node).type_keyword_position });
544 }
545 self.writer.keyword("type");
546 self.advance(a.name_location.begin);
547 let name = unsafe { core::ffi::CStr::from_ptr(a.name.value).to_string_lossy() };
548 self.writer.identifier(&name);
549 if a.generics.size > 0 || a.generic_packs.size > 0 {
550 if !cst_node.is_null() {
551 self.advance(unsafe { (*cst_node).generics_open_position });
552 }
553 self.writer.symbol("<");
554 let mut comma = CommaSeparatorInserter::new(
555 self.writer,
556 if !cst_node.is_null() {
557 unsafe { (*cst_node).generics_comma_positions.data }
558 } else {
559 core::ptr::null()
560 },
561 );
562 for o in unsafe { crate::records::ast_array::AstArray::iter(&a.generics) } {
563 let o = *o;
564 comma.operator_call(self.writer);
565 self.writer.advance(unsafe { &(*o).base.location.begin });
566 let name =
567 unsafe { core::ffi::CStr::from_ptr((*o).name.value).to_string_lossy() };
568 self.writer.identifier(&name);
569 if !unsafe { (*o).default_value.is_null() } {
570 let generic_type_cst_node = self.lookup_cst_node::<CstGenericType>(
571 o as *mut crate::records::ast_node::AstNode,
572 );
573 if !generic_type_cst_node.is_null() {
574 self.advance(unsafe {
575 (*generic_type_cst_node).default_equals_position
576 });
577 } else {
578 self.writer.maybe_space(
579 unsafe { &(*(*o).default_value).base.location.begin },
580 2,
581 );
582 }
583 self.writer.symbol("=");
584 self.visualize_type_annotation(unsafe { (*o).default_value });
585 }
586 }
587 for o in unsafe { crate::records::ast_array::AstArray::iter(&a.generic_packs) }
588 {
589 let o = *o;
590 comma.operator_call(self.writer);
591 let generic_type_pack_cst_node = self
592 .lookup_cst_node::<CstGenericTypePack>(
593 o as *mut crate::records::ast_node::AstNode,
594 );
595 self.writer.advance(unsafe { &(*o).base.location.begin });
596 let name =
597 unsafe { core::ffi::CStr::from_ptr((*o).name.value).to_string_lossy() };
598 self.writer.identifier(&name);
599 if !generic_type_pack_cst_node.is_null() {
600 self.maybe_advance_and_write(
601 &unsafe { (*generic_type_pack_cst_node).ellipsis_position },
602 "...",
603 false,
604 );
605 } else {
606 self.writer.symbol("...");
607 }
608 if !unsafe { (*o).default_value.is_null() } {
609 if !cst_node.is_null() {
610 self.advance(unsafe {
611 (*generic_type_pack_cst_node).default_equals_position
612 });
613 } else {
614 self.writer.maybe_space(
615 unsafe { &(*(*o).default_value).base.location.begin },
616 2,
617 );
618 }
619 self.writer.symbol("=");
620 self.visualize_type_pack_annotation(
621 unsafe { &mut *(*o).default_value },
622 false,
623 false,
624 false,
625 );
626 }
627 }
628 if !cst_node.is_null() {
629 self.maybe_advance_and_write(
630 &unsafe { (*cst_node).generics_close_position },
631 ">",
632 false,
633 );
634 } else {
635 self.writer.symbol(">");
636 }
637 }
638 if !cst_node.is_null() {
639 self.maybe_advance_and_write(
640 &unsafe { (*cst_node).equals_position },
641 "=",
642 false,
643 );
644 } else {
645 self.writer
646 .maybe_space(unsafe { &(*a.type_ptr).base.location.begin }, 2);
647 self.writer.symbol("=");
648 }
649 self.visualize_type_annotation(a.type_ptr);
650 }
651 } else if let Some(t) = unsafe {
652 crate::rtti::ast_node_as::<AstStatTypeFunction>(
653 program as *mut AstStat as *mut crate::records::ast_node::AstNode,
654 )
655 .as_mut()
656 } {
657 if self.write_types {
658 let cst_node = self.lookup_cst_node::<CstStatTypeFunction>(
659 program as *mut AstStat as *mut crate::records::ast_node::AstNode,
660 );
661 if t.exported {
662 self.writer.keyword("export");
663 }
664 if !cst_node.is_null() {
665 self.advance(unsafe { (*cst_node).type_keyword_position });
666 } else {
667 self.writer.space();
668 }
669 self.writer.keyword("type");
670 if !cst_node.is_null() {
671 self.advance(unsafe { (*cst_node).function_keyword_position });
672 } else {
673 self.writer.space();
674 }
675 self.writer.keyword("function");
676 self.advance(t.name_location.begin);
677 let name = unsafe { core::ffi::CStr::from_ptr(t.name.value).to_string_lossy() };
678 self.writer.identifier(&name);
679 self.visualize_function_body(t.body);
680 }
681 } else if let Some(a) = unsafe {
682 crate::rtti::ast_node_as::<AstStatError>(
683 program as *mut AstStat as *mut crate::records::ast_node::AstNode,
684 )
685 .as_mut()
686 } {
687 self.writer.symbol("(error-stat");
688 for i in 0..a.expressions.size {
689 self.writer.symbol(if i == 0 && a.statements.size == 0 {
690 ": "
691 } else {
692 ", "
693 });
694 self.visualize_ast_expr(unsafe { *a.expressions.data.add(i as usize) });
695 }
696 for i in 0..a.statements.size {
697 self.writer.symbol(if i == 0 && a.expressions.size == 0 {
698 ": "
699 } else {
700 ", "
701 });
702 self.visualize_ast_stat(unsafe { *a.statements.data.add(i as usize) });
703 }
704 self.writer.symbol(")");
705 } else if let Some(a) = unsafe {
706 crate::rtti::ast_node_as::<AstStatDeclareGlobal>(
707 program as *mut AstStat as *mut crate::records::ast_node::AstNode,
708 )
709 .as_mut()
710 } {
711 self.writer.keyword("declare");
712 self.advance(&a.name_location.begin);
713 let name = unsafe { core::ffi::CStr::from_ptr(a.name.value).to_string_lossy() };
714 self.writer.identifier(&name);
715 self.writer.symbol(":");
716 self.visualize_type_annotation(a.type_);
717 } else if let Some(c) = unsafe {
718 crate::rtti::ast_node_as::<AstStatClass>(
719 program as *mut AstStat as *mut crate::records::ast_node::AstNode,
720 )
721 .as_mut()
722 } {
723 if FFlag::DebugLuauUserDefinedClasses.get() {
724 self.writer.keyword("class");
725 self.advance(unsafe { (*c.name).location.begin });
726 let name =
727 unsafe { core::ffi::CStr::from_ptr((*c.name).name.value).to_string_lossy() };
728 self.writer.identifier(&name);
729 for member in unsafe { crate::records::ast_array::AstArray::iter(&c.members) } {
730 match member {
731 Variant2::V0(prop) => {
732 let prop: &AstClassProperty = prop;
733 self.advance(prop.qualifier_location.begin);
734 self.writer.keyword("public");
735 self.advance(prop.name_location.begin);
736 let name = unsafe {
737 core::ffi::CStr::from_ptr(prop.name.value).to_string_lossy()
738 };
739 self.writer.identifier(&name);
740 if self.write_types && !prop.ty.is_null() {
741 LUAU_ASSERT!(prop.type_colon_location.is_some());
742 self.advance(prop.type_colon_location.unwrap().begin);
743 self.writer.symbol(":");
744 self.visualize_type_annotation(prop.ty);
745 }
746 }
747 Variant2::V1(method) => {
748 let method: &AstClassMethod = method;
749 if let Some(qualifier_location) = method.qualifier_location {
750 self.advance(&qualifier_location.begin);
751 self.writer.keyword("public");
752 }
753 self.advance(method.keyword_location.begin);
754 self.writer.keyword("function");
755 self.advance(method.name_location.begin);
756 let name = unsafe {
757 core::ffi::CStr::from_ptr(method.function_name.value)
758 .to_string_lossy()
759 };
760 self.writer.identifier(&name);
761 self.visualize_function_body(method.function);
762 }
763 }
764 }
765 self.writer.newline();
766 self.writer.keyword("end");
767 self.writer.newline();
768 }
769 } else {
770 LUAU_ASSERT!(false);
771 }
772
773 if program.has_semicolon {
774 self.advance_before(program.base.location.end, 1);
775 self.writer.symbol(";");
776 }
777 }
778}