Skip to main content

luaur_ast/methods/
printer_visualize_pretty_printer_alt_c.rs

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}