Skip to main content

luaur_analysis/methods/
state_dot_visit_children_to_dot.rs

1use crate::functions::get_type_alt_j::get_type_id;
2use crate::functions::to_string_to_string_alt_c::to_string_type_id;
3use crate::records::any_type::AnyType;
4use crate::records::blocked_type::BlockedType;
5use crate::records::boolean_singleton::BooleanSingleton;
6use crate::records::error_type::ErrorType;
7use crate::records::extern_type::ExternType;
8use crate::records::free_type::FreeType;
9use crate::records::function_type::FunctionType;
10use crate::records::generic_type::GenericType;
11use crate::records::intersection_type::IntersectionType;
12use crate::records::lazy_type::LazyType;
13use crate::records::metatable_type::MetatableType;
14use crate::records::negation_type::NegationType;
15use crate::records::never_type::NeverType;
16use crate::records::no_refine_type::NoRefineType;
17use crate::records::pending_expansion_type::PendingExpansionType;
18use crate::records::primitive_type::PrimitiveType;
19use crate::records::singleton_type::SingletonType;
20use crate::records::state_dot::StateDot;
21use crate::records::string_singleton::StringSingleton;
22use crate::records::table_type::TableType;
23use crate::records::type_function_instance_type::TypeFunctionInstanceType;
24use crate::records::union_type::UnionType;
25use crate::records::unknown_type::UnknownType;
26use crate::type_aliases::bound_type::BoundType;
27use crate::type_aliases::singleton_variant::SingletonVariantMember;
28use crate::type_aliases::type_id::TypeId;
29use luaur_common::functions::escape::escape;
30use luaur_common::functions::format_append::formatAppend;
31use luaur_common::macros::luau_assert::LUAU_ASSERT;
32
33impl StateDot {
34    pub fn visit_children_type_id_i32(&mut self, ty: TypeId, index: i32) {
35        if self.seen_ty.contains(&ty) {
36            return;
37        }
38        self.seen_ty.insert(ty);
39
40        self.start_node(index);
41        self.start_node_label();
42
43        unsafe {
44            let t = get_type_id::<BoundType>(ty);
45            if !t.is_null() {
46                let t = &*t;
47                formatAppend(&mut self.result, format_args!("BoundType {}", index));
48                self.finish_node_label_type_id(ty);
49                self.finish_node();
50
51                self.visit_child_type_id_i32_c_char(t.boundTo, index, core::ptr::null());
52                return;
53            }
54
55            let t = get_type_id::<BlockedType>(ty);
56            if !t.is_null() {
57                formatAppend(&mut self.result, format_args!("BlockedType {}", index));
58                self.finish_node_label_type_id(ty);
59                self.finish_node();
60                return;
61            }
62
63            let t = get_type_id::<FunctionType>(ty);
64            if !t.is_null() {
65                let t = &*t;
66                formatAppend(&mut self.result, format_args!("FunctionType {}", index));
67                self.finish_node_label_type_id(ty);
68                self.finish_node();
69
70                self.visit_child_type_pack_id_i32_c_char(t.arg_types, index, c"arg".as_ptr());
71                self.visit_child_type_pack_id_i32_c_char(t.ret_types, index, c"ret".as_ptr());
72                return;
73            }
74
75            let t = get_type_id::<TableType>(ty);
76            if !t.is_null() {
77                let t = &*t;
78                if let Some(name) = &t.name {
79                    formatAppend(&mut self.result, format_args!("TableType {}", name));
80                } else if let Some(synthetic_name) = &t.synthetic_name {
81                    formatAppend(
82                        &mut self.result,
83                        format_args!("TableType {}", synthetic_name),
84                    );
85                } else {
86                    formatAppend(&mut self.result, format_args!("TableType {}", index));
87                }
88                self.finish_node_label_type_id(ty);
89                self.finish_node();
90
91                if let Some(bound_to) = t.bound_to {
92                    self.visit_child_type_id_i32_c_char(bound_to, index, c"boundTo".as_ptr());
93                    return;
94                }
95
96                for (name, prop) in t.props.iter() {
97                    if prop.is_shared() {
98                        let c_name = alloc::ffi::CString::new(name.as_bytes()).unwrap();
99                        self.visit_child_type_id_i32_c_char(
100                            prop.read_ty.unwrap(),
101                            index,
102                            c_name.as_ptr(),
103                        );
104                    } else {
105                        if let Some(read_ty) = prop.read_ty {
106                            let read_name = alloc::format!("read {}", name);
107                            let c_name = alloc::ffi::CString::new(read_name.as_bytes()).unwrap();
108                            self.visit_child_type_id_i32_c_char(read_ty, index, c_name.as_ptr());
109                        }
110
111                        if let Some(write_ty) = prop.write_ty {
112                            let write_name = alloc::format!("write {}", name);
113                            let c_name = alloc::ffi::CString::new(write_name.as_bytes()).unwrap();
114                            self.visit_child_type_id_i32_c_char(write_ty, index, c_name.as_ptr());
115                        }
116                    }
117                }
118                if let Some(indexer) = &t.indexer {
119                    self.visit_child_type_id_i32_c_char(
120                        indexer.index_type,
121                        index,
122                        c"[index]".as_ptr(),
123                    );
124                    self.visit_child_type_id_i32_c_char(
125                        indexer.index_result_type,
126                        index,
127                        c"[value]".as_ptr(),
128                    );
129                }
130                for itp in &t.instantiated_type_params {
131                    self.visit_child_type_id_i32_c_char(*itp, index, c"typeParam".as_ptr());
132                }
133
134                for itp in &t.instantiated_type_pack_params {
135                    self.visit_child_type_pack_id_i32_c_char(
136                        *itp,
137                        index,
138                        c"typePackParam".as_ptr(),
139                    );
140                }
141                return;
142            }
143
144            let t = get_type_id::<MetatableType>(ty);
145            if !t.is_null() {
146                let t = &*t;
147                formatAppend(&mut self.result, format_args!("MetatableType {}", index));
148                self.finish_node_label_type_id(ty);
149                self.finish_node();
150
151                self.visit_child_type_id_i32_c_char(t.table, index, c"table".as_ptr());
152                self.visit_child_type_id_i32_c_char(t.metatable, index, c"metatable".as_ptr());
153                return;
154            }
155
156            let t = get_type_id::<UnionType>(ty);
157            if !t.is_null() {
158                let t = &*t;
159                formatAppend(&mut self.result, format_args!("UnionType {}", index));
160                self.finish_node_label_type_id(ty);
161                self.finish_node();
162
163                for opt in &t.options {
164                    self.visit_child_type_id_i32_c_char(*opt, index, core::ptr::null());
165                }
166                return;
167            }
168
169            let t = get_type_id::<IntersectionType>(ty);
170            if !t.is_null() {
171                let t = &*t;
172                formatAppend(&mut self.result, format_args!("IntersectionType {}", index));
173                self.finish_node_label_type_id(ty);
174                self.finish_node();
175
176                for part in &t.parts {
177                    self.visit_child_type_id_i32_c_char(*part, index, core::ptr::null());
178                }
179                return;
180            }
181
182            let t = get_type_id::<LazyType>(ty);
183            if !t.is_null() {
184                formatAppend(&mut self.result, format_args!("LazyType {}", index));
185                self.finish_node_label_type_id(ty);
186                self.finish_node();
187                return;
188            }
189
190            let t = get_type_id::<PendingExpansionType>(ty);
191            if !t.is_null() {
192                formatAppend(
193                    &mut self.result,
194                    format_args!("PendingExpansionType {}", index),
195                );
196                self.finish_node_label_type_id(ty);
197                self.finish_node();
198                return;
199            }
200
201            let t = get_type_id::<GenericType>(ty);
202            if !t.is_null() {
203                let t = &*t;
204                if t.explicit_name {
205                    formatAppend(&mut self.result, format_args!("GenericType {}", t.name));
206                } else {
207                    formatAppend(&mut self.result, format_args!("GenericType {}", index));
208                }
209                self.finish_node_label_type_id(ty);
210                self.finish_node();
211                return;
212            }
213
214            let t = get_type_id::<FreeType>(ty);
215            if !t.is_null() {
216                let t = &*t;
217                formatAppend(&mut self.result, format_args!("FreeType {}", index));
218                self.finish_node_label_type_id(ty);
219                self.finish_node();
220
221                if !t.lower_bound.is_null() && get_type_id::<NeverType>(t.lower_bound).is_null() {
222                    self.visit_child_type_id_i32_c_char(
223                        t.lower_bound,
224                        index,
225                        c"[lowerBound]".as_ptr(),
226                    );
227                }
228
229                if !t.upper_bound.is_null() && get_type_id::<UnknownType>(t.upper_bound).is_null() {
230                    self.visit_child_type_id_i32_c_char(
231                        t.upper_bound,
232                        index,
233                        c"[upperBound]".as_ptr(),
234                    );
235                }
236                return;
237            }
238
239            let t = get_type_id::<AnyType>(ty);
240            if !t.is_null() {
241                formatAppend(&mut self.result, format_args!("AnyType {}", index));
242                self.finish_node_label_type_id(ty);
243                self.finish_node();
244                return;
245            }
246
247            let t = get_type_id::<NoRefineType>(ty);
248            if !t.is_null() {
249                formatAppend(&mut self.result, format_args!("NoRefineType {}", index));
250                self.finish_node_label_type_id(ty);
251                self.finish_node();
252                return;
253            }
254
255            let t = get_type_id::<UnknownType>(ty);
256            if !t.is_null() {
257                formatAppend(&mut self.result, format_args!("UnknownType {}", index));
258                self.finish_node_label_type_id(ty);
259                self.finish_node();
260                return;
261            }
262
263            let t = get_type_id::<NeverType>(ty);
264            if !t.is_null() {
265                formatAppend(&mut self.result, format_args!("NeverType {}", index));
266                self.finish_node_label_type_id(ty);
267                self.finish_node();
268                return;
269            }
270
271            let t = get_type_id::<PrimitiveType>(ty);
272            if !t.is_null() {
273                let s = to_string_type_id(ty);
274                formatAppend(&mut self.result, format_args!("PrimitiveType {}", s));
275                self.finish_node_label_type_id(ty);
276                self.finish_node();
277                return;
278            }
279
280            let t = get_type_id::<ErrorType>(ty);
281            if !t.is_null() {
282                formatAppend(&mut self.result, format_args!("ErrorType {}", index));
283                self.finish_node_label_type_id(ty);
284                self.finish_node();
285                return;
286            }
287
288            let t = get_type_id::<ExternType>(ty);
289            if !t.is_null() {
290                let t = &*t;
291                formatAppend(&mut self.result, format_args!("ExternType {}", t.name));
292                self.finish_node_label_type_id(ty);
293                self.finish_node();
294
295                for (name, prop) in t.props.iter() {
296                    if prop.is_shared() {
297                        let c_name = alloc::ffi::CString::new(name.as_bytes()).unwrap();
298                        self.visit_child_type_id_i32_c_char(
299                            prop.read_ty.unwrap(),
300                            index,
301                            c_name.as_ptr(),
302                        );
303                    } else {
304                        if let Some(read_ty) = prop.read_ty {
305                            let read_name = alloc::format!("read {}", name);
306                            let c_name = alloc::ffi::CString::new(read_name.as_bytes()).unwrap();
307                            self.visit_child_type_id_i32_c_char(read_ty, index, c_name.as_ptr());
308                        }
309
310                        if let Some(write_ty) = prop.write_ty {
311                            let write_name = alloc::format!("write {}", name);
312                            let c_name = alloc::ffi::CString::new(write_name.as_bytes()).unwrap();
313                            self.visit_child_type_id_i32_c_char(write_ty, index, c_name.as_ptr());
314                        }
315                    }
316                }
317
318                if let Some(parent) = t.parent {
319                    self.visit_child_type_id_i32_c_char(parent, index, c"[parent]".as_ptr());
320                }
321
322                if let Some(metatable) = t.metatable {
323                    self.visit_child_type_id_i32_c_char(metatable, index, c"[metatable]".as_ptr());
324                }
325
326                if let Some(indexer) = &t.indexer {
327                    self.visit_child_type_id_i32_c_char(
328                        indexer.index_type,
329                        index,
330                        c"[index]".as_ptr(),
331                    );
332                    self.visit_child_type_id_i32_c_char(
333                        indexer.index_result_type,
334                        index,
335                        c"[value]".as_ptr(),
336                    );
337                }
338                return;
339            }
340
341            let t = get_type_id::<SingletonType>(ty);
342            if !t.is_null() {
343                let res: alloc::string::String;
344
345                let ss = StringSingleton::get_if(&(*t).variant);
346                if let Some(ss) = ss {
347                    // Don't put in quotes anywhere. If it's outside of the call to escape,
348                    // then it's invalid syntax. If it's inside, then escaping is super noisy.
349                    res = alloc::format!("string: {}", escape(&ss.value, false));
350                } else if let Some(bs) = BooleanSingleton::get_if(&(*t).variant) {
351                    res = alloc::format!("boolean: {}", if bs.value { "true" } else { "false" });
352                } else {
353                    LUAU_ASSERT!(false);
354                    res = alloc::string::String::new();
355                }
356
357                formatAppend(&mut self.result, format_args!("SingletonType {}", res));
358                self.finish_node_label_type_id(ty);
359                self.finish_node();
360                return;
361            }
362
363            let t = get_type_id::<NegationType>(ty);
364            if !t.is_null() {
365                let t = &*t;
366                formatAppend(&mut self.result, format_args!("NegationType {}", index));
367                self.finish_node_label_type_id(ty);
368                self.finish_node();
369
370                self.visit_child_type_id_i32_c_char(t.ty, index, c"[negated]".as_ptr());
371                return;
372            }
373
374            let t = get_type_id::<TypeFunctionInstanceType>(ty);
375            if !t.is_null() {
376                let t = &*t;
377                formatAppend(
378                    &mut self.result,
379                    format_args!(
380                        "TypeFunctionInstanceType {} {}",
381                        t.function.as_ref().name,
382                        index
383                    ),
384                );
385                self.finish_node_label_type_id(ty);
386                self.finish_node();
387
388                for ty_param in &t.type_arguments {
389                    self.visit_child_type_id_i32_c_char(*ty_param, index, core::ptr::null());
390                }
391
392                for tp_param in &t.pack_arguments {
393                    self.visit_child_type_pack_id_i32_c_char(*tp_param, index, core::ptr::null());
394                }
395                return;
396            }
397
398            // unknown type kind
399            LUAU_ASSERT!(false);
400        }
401    }
402}