Skip to main content

luaur_ast/
visit.rs

1//! AST visitor dispatch — the faithful Rust form of Luau's virtual
2//! `AstNode::visit(AstVisitor*)` traversal (`Ast/src/Ast.cpp`).
3//!
4//! In C++ each concrete node overrides `visit(AstVisitor*)`: it calls the typed
5//! `visitor->visit(this)` (compile-time overload on `this`'s static type) and,
6//! if that returns `true`, recurses into its children with `child->visit(v)` —
7//! a *virtual* call dispatched on the child's dynamic type.
8//!
9//! Rust has no vtable here (nodes are thin `*mut AstExpr` etc. in the arena), so
10//! the per-node override becomes `impl AstVisitable for X`, and the virtual
11//! recursion becomes a `class_index` match in the `*_visit` dispatch functions
12//! below — the central analog of the C++ vtable. A node's `visit` body calls
13//! `crate::visit::ast_expr_visit(self.child, v)` for each child pointer (and
14//! loops over `AstArray` children), never `child.visit(v)` directly, because the
15//! static type of `self.child` is only the base.
16
17use crate::records::ast_visitor::AstVisitor;
18
19// `block->visit(visitor)` where the static type is already `AstStatBlock` —
20// C++ calls the override directly (no virtual dispatch needed), so route to
21// the node's own `visit` impl rather than the class-index dispatcher.
22pub use crate::methods::ast_stat_block_visit::ast_stat_block_visit;
23
24/// C++ `AstX::visit(AstVisitor*)` override. Implemented once per concrete node
25/// by that node's `visit` method item. `&self` is enough — `visit` never mutates
26/// the node (it only feeds `self` to the visitor and recurses).
27pub trait AstVisitable {
28    fn visit(&self, visitor: &mut dyn AstVisitor);
29}
30
31/// `expr->visit(visitor)` where `expr` is a base `*mut AstExpr` — dispatch to the
32/// concrete override by RTTI class index.
33///
34/// # Safety
35/// `expr` must be null or point to a live `AstExpr`-prefixed node.
36pub unsafe fn ast_expr_visit(
37    expr: *mut crate::records::ast_expr::AstExpr,
38    visitor: &mut dyn AstVisitor,
39) {
40    dispatch_node((expr as *mut crate::records::ast_node::AstNode), visitor);
41}
42
43/// `stat->visit(visitor)` for a base `*mut AstStat`.
44///
45/// # Safety
46/// `stat` must be null or point to a live `AstStat`-prefixed node.
47pub unsafe fn ast_stat_visit(
48    stat: *mut crate::records::ast_stat::AstStat,
49    visitor: &mut dyn AstVisitor,
50) {
51    dispatch_node((stat as *mut crate::records::ast_node::AstNode), visitor);
52}
53
54/// `ty->visit(visitor)` for a base `*mut AstType`.
55///
56/// # Safety
57/// `ty` must be null or point to a live `AstType`-prefixed node.
58pub unsafe fn ast_type_visit(
59    ty: *mut crate::records::ast_type::AstType,
60    visitor: &mut dyn AstVisitor,
61) {
62    dispatch_node((ty as *mut crate::records::ast_node::AstNode), visitor);
63}
64
65/// `pack->visit(visitor)` for a base `*mut AstTypePack`.
66///
67/// # Safety
68/// `pack` must be null or point to a live `AstTypePack`-prefixed node.
69pub unsafe fn ast_type_pack_visit(
70    pack: *mut crate::records::ast_type_pack::AstTypePack,
71    visitor: &mut dyn AstVisitor,
72) {
73    dispatch_node((pack as *mut crate::records::ast_node::AstNode), visitor);
74}
75
76/// `node->visit(visitor)` for any base `*mut AstNode`.
77///
78/// # Safety
79/// `node` must be null or point to a live AST node.
80pub unsafe fn ast_node_visit(
81    node: *mut crate::records::ast_node::AstNode,
82    visitor: &mut dyn AstVisitor,
83) {
84    dispatch_node(node, visitor);
85}
86
87// The central class-index dispatcher — the analog of the C++ vtable. One arm
88// per concrete node type; each downcast is sound for the same reason as
89// `ast_node_as` (standard-layout, base at offset 0).
90//
91// # Safety
92// `node` must be null or point to a live AST node.
93pub unsafe fn dispatch_node(
94    node: *mut crate::records::ast_node::AstNode,
95    visitor: &mut dyn AstVisitor,
96) {
97    use crate::rtti::AstNodeClass;
98    use crate::visit::AstVisitable;
99    if node.is_null() {
100        return;
101    }
102    match (*node).class_index {
103        x if x == crate::records::ast_attr::AstAttr::CLASS_INDEX => {
104            (&*(node as *const crate::records::ast_attr::AstAttr)).visit(visitor)
105        }
106        x if x == crate::records::ast_expr_binary::AstExprBinary::CLASS_INDEX => {
107            (&*(node as *const crate::records::ast_expr_binary::AstExprBinary)).visit(visitor)
108        }
109        x if x == crate::records::ast_expr_call::AstExprCall::CLASS_INDEX => {
110            (&*(node as *const crate::records::ast_expr_call::AstExprCall)).visit(visitor)
111        }
112        x if x == crate::records::ast_expr_constant_bool::AstExprConstantBool::CLASS_INDEX => {
113            (&*(node as *const crate::records::ast_expr_constant_bool::AstExprConstantBool)).visit(visitor)
114        }
115        x if x == crate::records::ast_expr_constant_integer::AstExprConstantInteger::CLASS_INDEX => {
116            (&*(node as *const crate::records::ast_expr_constant_integer::AstExprConstantInteger)).visit(visitor)
117        }
118        x if x == crate::records::ast_expr_constant_nil::AstExprConstantNil::CLASS_INDEX => {
119            (&*(node as *const crate::records::ast_expr_constant_nil::AstExprConstantNil)).visit(visitor)
120        }
121        x if x == crate::records::ast_expr_constant_number::AstExprConstantNumber::CLASS_INDEX => {
122            (&*(node as *const crate::records::ast_expr_constant_number::AstExprConstantNumber)).visit(visitor)
123        }
124        x if x == crate::records::ast_expr_constant_string::AstExprConstantString::CLASS_INDEX => {
125            (&*(node as *const crate::records::ast_expr_constant_string::AstExprConstantString)).visit(visitor)
126        }
127        x if x == crate::records::ast_expr_error::AstExprError::CLASS_INDEX => {
128            (&*(node as *const crate::records::ast_expr_error::AstExprError)).visit(visitor)
129        }
130        x if x == crate::records::ast_expr_function::AstExprFunction::CLASS_INDEX => {
131            (&*(node as *const crate::records::ast_expr_function::AstExprFunction)).visit(visitor)
132        }
133        x if x == crate::records::ast_expr_global::AstExprGlobal::CLASS_INDEX => {
134            (&*(node as *const crate::records::ast_expr_global::AstExprGlobal)).visit(visitor)
135        }
136        x if x == crate::records::ast_expr_group::AstExprGroup::CLASS_INDEX => {
137            (&*(node as *const crate::records::ast_expr_group::AstExprGroup)).visit(visitor)
138        }
139        x if x == crate::records::ast_expr_if_else::AstExprIfElse::CLASS_INDEX => {
140            (&*(node as *const crate::records::ast_expr_if_else::AstExprIfElse)).visit(visitor)
141        }
142        x if x == crate::records::ast_expr_index_expr::AstExprIndexExpr::CLASS_INDEX => {
143            (&*(node as *const crate::records::ast_expr_index_expr::AstExprIndexExpr)).visit(visitor)
144        }
145        x if x == crate::records::ast_expr_index_name::AstExprIndexName::CLASS_INDEX => {
146            (&*(node as *const crate::records::ast_expr_index_name::AstExprIndexName)).visit(visitor)
147        }
148        x if x == crate::records::ast_expr_instantiate::AstExprInstantiate::CLASS_INDEX => {
149            (&*(node as *const crate::records::ast_expr_instantiate::AstExprInstantiate)).visit(visitor)
150        }
151        x if x == crate::records::ast_expr_interp_string::AstExprInterpString::CLASS_INDEX => {
152            (&*(node as *const crate::records::ast_expr_interp_string::AstExprInterpString)).visit(visitor)
153        }
154        x if x == crate::records::ast_expr_local::AstExprLocal::CLASS_INDEX => {
155            (&*(node as *const crate::records::ast_expr_local::AstExprLocal)).visit(visitor)
156        }
157        x if x == crate::records::ast_expr_table::AstExprTable::CLASS_INDEX => {
158            (&*(node as *const crate::records::ast_expr_table::AstExprTable)).visit(visitor)
159        }
160        x if x == crate::records::ast_expr_type_assertion::AstExprTypeAssertion::CLASS_INDEX => {
161            (&*(node as *const crate::records::ast_expr_type_assertion::AstExprTypeAssertion)).visit(visitor)
162        }
163        x if x == crate::records::ast_expr_unary::AstExprUnary::CLASS_INDEX => {
164            (&*(node as *const crate::records::ast_expr_unary::AstExprUnary)).visit(visitor)
165        }
166        x if x == crate::records::ast_expr_varargs::AstExprVarargs::CLASS_INDEX => {
167            (&*(node as *const crate::records::ast_expr_varargs::AstExprVarargs)).visit(visitor)
168        }
169        x if x == crate::records::ast_generic_type::AstGenericType::CLASS_INDEX => {
170            (&*(node as *const crate::records::ast_generic_type::AstGenericType)).visit(visitor)
171        }
172        x if x == crate::records::ast_generic_type_pack::AstGenericTypePack::CLASS_INDEX => {
173            (&*(node as *const crate::records::ast_generic_type_pack::AstGenericTypePack)).visit(visitor)
174        }
175        x if x == crate::records::ast_stat_assign::AstStatAssign::CLASS_INDEX => {
176            (&*(node as *const crate::records::ast_stat_assign::AstStatAssign)).visit(visitor)
177        }
178        x if x == crate::records::ast_stat_block::AstStatBlock::CLASS_INDEX => {
179            (&*(node as *const crate::records::ast_stat_block::AstStatBlock)).visit(visitor)
180        }
181        x if x == crate::records::ast_stat_break::AstStatBreak::CLASS_INDEX => {
182            (&*(node as *const crate::records::ast_stat_break::AstStatBreak)).visit(visitor)
183        }
184        x if x == crate::records::ast_stat_class::AstStatClass::CLASS_INDEX => {
185            (&*(node as *const crate::records::ast_stat_class::AstStatClass)).visit(visitor)
186        }
187        x if x == crate::records::ast_stat_compound_assign::AstStatCompoundAssign::CLASS_INDEX => {
188            (&*(node as *const crate::records::ast_stat_compound_assign::AstStatCompoundAssign)).visit(visitor)
189        }
190        x if x == crate::records::ast_stat_continue::AstStatContinue::CLASS_INDEX => {
191            (&*(node as *const crate::records::ast_stat_continue::AstStatContinue)).visit(visitor)
192        }
193        x if x == crate::records::ast_stat_declare_extern_type::AstStatDeclareExternType::CLASS_INDEX => {
194            (&*(node as *const crate::records::ast_stat_declare_extern_type::AstStatDeclareExternType)).visit(visitor)
195        }
196        x if x == crate::records::ast_stat_declare_function::AstStatDeclareFunction::CLASS_INDEX => {
197            (&*(node as *const crate::records::ast_stat_declare_function::AstStatDeclareFunction)).visit(visitor)
198        }
199        x if x == crate::records::ast_stat_declare_global::AstStatDeclareGlobal::CLASS_INDEX => {
200            (&*(node as *const crate::records::ast_stat_declare_global::AstStatDeclareGlobal)).visit(visitor)
201        }
202        x if x == crate::records::ast_stat_error::AstStatError::CLASS_INDEX => {
203            (&*(node as *const crate::records::ast_stat_error::AstStatError)).visit(visitor)
204        }
205        x if x == crate::records::ast_stat_expr::AstStatExpr::CLASS_INDEX => {
206            (&*(node as *const crate::records::ast_stat_expr::AstStatExpr)).visit(visitor)
207        }
208        x if x == crate::records::ast_stat_for::AstStatFor::CLASS_INDEX => {
209            (&*(node as *const crate::records::ast_stat_for::AstStatFor)).visit(visitor)
210        }
211        x if x == crate::records::ast_stat_for_in::AstStatForIn::CLASS_INDEX => {
212            (&*(node as *const crate::records::ast_stat_for_in::AstStatForIn)).visit(visitor)
213        }
214        x if x == crate::records::ast_stat_function::AstStatFunction::CLASS_INDEX => {
215            (&*(node as *const crate::records::ast_stat_function::AstStatFunction)).visit(visitor)
216        }
217        x if x == crate::records::ast_stat_if::AstStatIf::CLASS_INDEX => {
218            (&*(node as *const crate::records::ast_stat_if::AstStatIf)).visit(visitor)
219        }
220        x if x == crate::records::ast_stat_local::AstStatLocal::CLASS_INDEX => {
221            (&*(node as *const crate::records::ast_stat_local::AstStatLocal)).visit(visitor)
222        }
223        x if x == crate::records::ast_stat_local_function::AstStatLocalFunction::CLASS_INDEX => {
224            (&*(node as *const crate::records::ast_stat_local_function::AstStatLocalFunction)).visit(visitor)
225        }
226        x if x == crate::records::ast_stat_repeat::AstStatRepeat::CLASS_INDEX => {
227            (&*(node as *const crate::records::ast_stat_repeat::AstStatRepeat)).visit(visitor)
228        }
229        x if x == crate::records::ast_stat_return::AstStatReturn::CLASS_INDEX => {
230            (&*(node as *const crate::records::ast_stat_return::AstStatReturn)).visit(visitor)
231        }
232        x if x == crate::records::ast_stat_type_alias::AstStatTypeAlias::CLASS_INDEX => {
233            (&*(node as *const crate::records::ast_stat_type_alias::AstStatTypeAlias)).visit(visitor)
234        }
235        x if x == crate::records::ast_stat_type_function::AstStatTypeFunction::CLASS_INDEX => {
236            (&*(node as *const crate::records::ast_stat_type_function::AstStatTypeFunction)).visit(visitor)
237        }
238        x if x == crate::records::ast_stat_while::AstStatWhile::CLASS_INDEX => {
239            (&*(node as *const crate::records::ast_stat_while::AstStatWhile)).visit(visitor)
240        }
241        x if x == crate::records::ast_type_error::AstTypeError::CLASS_INDEX => {
242            (&*(node as *const crate::records::ast_type_error::AstTypeError)).visit(visitor)
243        }
244        x if x == crate::records::ast_type_function::AstTypeFunction::CLASS_INDEX => {
245            (&*(node as *const crate::records::ast_type_function::AstTypeFunction)).visit(visitor)
246        }
247        x if x == crate::records::ast_type_group::AstTypeGroup::CLASS_INDEX => {
248            (&*(node as *const crate::records::ast_type_group::AstTypeGroup)).visit(visitor)
249        }
250        x if x == crate::records::ast_type_intersection::AstTypeIntersection::CLASS_INDEX => {
251            (&*(node as *const crate::records::ast_type_intersection::AstTypeIntersection)).visit(visitor)
252        }
253        x if x == crate::records::ast_type_optional::AstTypeOptional::CLASS_INDEX => {
254            (&*(node as *const crate::records::ast_type_optional::AstTypeOptional)).visit(visitor)
255        }
256        x if x == crate::records::ast_type_pack_explicit::AstTypePackExplicit::CLASS_INDEX => {
257            (&*(node as *const crate::records::ast_type_pack_explicit::AstTypePackExplicit)).visit(visitor)
258        }
259        x if x == crate::records::ast_type_pack_generic::AstTypePackGeneric::CLASS_INDEX => {
260            (&*(node as *const crate::records::ast_type_pack_generic::AstTypePackGeneric)).visit(visitor)
261        }
262        x if x == crate::records::ast_type_pack_variadic::AstTypePackVariadic::CLASS_INDEX => {
263            (&*(node as *const crate::records::ast_type_pack_variadic::AstTypePackVariadic)).visit(visitor)
264        }
265        x if x == crate::records::ast_type_reference::AstTypeReference::CLASS_INDEX => {
266            (&*(node as *const crate::records::ast_type_reference::AstTypeReference)).visit(visitor)
267        }
268        x if x == crate::records::ast_type_singleton_bool::AstTypeSingletonBool::CLASS_INDEX => {
269            (&*(node as *const crate::records::ast_type_singleton_bool::AstTypeSingletonBool)).visit(visitor)
270        }
271        x if x == crate::records::ast_type_singleton_string::AstTypeSingletonString::CLASS_INDEX => {
272            (&*(node as *const crate::records::ast_type_singleton_string::AstTypeSingletonString)).visit(visitor)
273        }
274        x if x == crate::records::ast_type_table::AstTypeTable::CLASS_INDEX => {
275            (&*(node as *const crate::records::ast_type_table::AstTypeTable)).visit(visitor)
276        }
277        x if x == crate::records::ast_type_typeof::AstTypeTypeof::CLASS_INDEX => {
278            (&*(node as *const crate::records::ast_type_typeof::AstTypeTypeof)).visit(visitor)
279        }
280        x if x == crate::records::ast_type_union::AstTypeUnion::CLASS_INDEX => {
281            (&*(node as *const crate::records::ast_type_union::AstTypeUnion)).visit(visitor)
282        }
283        _ => {
284            // C++ cannot reach here: every concrete AstNode subclass overrides
285            // visit. An unknown class index means arena corruption.
286            panic!("dispatch_node: unknown AST class index {}", (*node).class_index);
287        }
288    }
289}