roblox_rs_core/ast/
visitor.rs

1//! AST visitor module.
2//!
3//! This module provides a visitor pattern implementation for traversing the AST.
4
5use syn::{visit::{self, Visit}, File, ItemFn, ItemStruct, ItemEnum, ItemImpl, ImplItem};
6
7/// A visitor that collects information about the AST.
8pub struct AstCollector {
9    /// Functions found in the AST
10    pub functions: Vec<String>,
11    /// Structs found in the AST
12    pub structs: Vec<String>,
13    /// Enums found in the AST
14    pub enums: Vec<String>,
15    /// Import paths found in the AST
16    pub imports: Vec<String>,
17}
18
19impl AstCollector {
20    /// Create a new collector
21    pub fn new() -> Self {
22        Self {
23            functions: Vec::new(),
24            structs: Vec::new(),
25            enums: Vec::new(),
26            imports: Vec::new(),
27        }
28    }
29    
30    /// Analyze a Rust file and collect information
31    pub fn analyze(&mut self, file: &File) {
32        self.visit_file(file);
33    }
34}
35
36impl<'ast> Visit<'ast> for AstCollector {
37    fn visit_item_fn(&mut self, node: &'ast ItemFn) {
38        // Collect function names
39        self.functions.push(node.sig.ident.to_string());
40        
41        // Continue visiting the function body
42        visit::visit_item_fn(self, node);
43    }
44    
45    fn visit_item_struct(&mut self, node: &'ast ItemStruct) {
46        // Collect struct names
47        self.structs.push(node.ident.to_string());
48        
49        // Continue visiting the struct fields
50        visit::visit_item_struct(self, node);
51    }
52    
53    fn visit_item_enum(&mut self, node: &'ast ItemEnum) {
54        // Collect enum names
55        self.enums.push(node.ident.to_string());
56        
57        // Continue visiting the enum variants
58        visit::visit_item_enum(self, node);
59    }
60    
61    fn visit_item_use(&mut self, node: &'ast syn::ItemUse) {
62        // Collect import paths (simplified)
63        let path = format!("{:?}", node.tree);
64        self.imports.push(path);
65        
66        // Continue visiting
67        visit::visit_item_use(self, node);
68    }
69}
70
71/// A dependency analyzer for finding external dependencies.
72pub struct DependencyAnalyzer {
73    /// External crates used in the code
74    pub external_crates: Vec<String>,
75    /// Standard library modules used
76    pub std_modules: Vec<String>,
77}
78
79impl DependencyAnalyzer {
80    /// Create a new dependency analyzer
81    pub fn new() -> Self {
82        Self {
83            external_crates: Vec::new(),
84            std_modules: Vec::new(),
85        }
86    }
87    
88    /// Analyze a Rust file and collect dependency information
89    pub fn analyze(&mut self, file: &File) {
90        self.visit_file(file);
91    }
92}
93
94impl<'ast> Visit<'ast> for DependencyAnalyzer {
95    fn visit_use_path(&mut self, path: &'ast syn::UsePath) {
96        // Check for std modules
97        let name = path.ident.to_string();
98        
99        if name == "std" {
100            // Access the tree to look for the second segment
101            if let syn::UseTree::Path(subtree) = &*path.tree {
102                let module = subtree.ident.to_string();
103                if !self.std_modules.contains(&module) {
104                    self.std_modules.push(module);
105                }
106            }
107        } else if !name.starts_with("self") && !name.starts_with("crate") {
108            // External crate
109            if !self.external_crates.contains(&name) {
110                self.external_crates.push(name);
111            }
112        }
113        
114        // Continue visiting
115        visit::visit_use_path(self, path);
116    }
117}
118
119/// A visitor that looks for potential compatibility issues when compiling to Luau.
120pub struct CompatibilityChecker {
121    /// List of compatibility issues found
122    pub issues: Vec<String>,
123}
124
125impl CompatibilityChecker {
126    /// Create a new compatibility checker
127    pub fn new() -> Self {
128        Self {
129            issues: Vec::new(),
130        }
131    }
132    
133    /// Check a Rust file for compatibility issues
134    pub fn check(&mut self, file: &File) {
135        self.visit_file(file);
136    }
137}
138
139impl<'ast> Visit<'ast> for CompatibilityChecker {
140    fn visit_item_fn(&mut self, node: &'ast ItemFn) {
141        // Check for async functions
142        if node.sig.asyncness.is_some() {
143            self.issues.push(format!(
144                "Async function '{}' may need special handling for Luau",
145                node.sig.ident
146            ));
147        }
148        
149        // Check for unsafe blocks
150        if node.sig.unsafety.is_some() {
151            self.issues.push(format!(
152                "Unsafe function '{}' will need manual safety checks in Luau",
153                node.sig.ident
154            ));
155        }
156        
157        // Continue visiting
158        visit::visit_item_fn(self, node);
159    }
160    
161    fn visit_expr_method_call(&mut self, node: &'ast syn::ExprMethodCall) {
162        // Check for methods that might not be compatible
163        let method_name = node.method.to_string();
164        
165        // Examples of methods that might need special handling
166        if ["thread_local", "spawn", "catch_unwind"].contains(&method_name.as_str()) {
167            self.issues.push(format!(
168                "Method call '{}' might not be directly translatable to Luau",
169                method_name
170            ));
171        }
172        
173        // Continue visiting
174        visit::visit_expr_method_call(self, node);
175    }
176}
177
178#[cfg(test)]
179mod tests {
180    use super::*;
181    use syn::parse_quote;
182    
183    #[test]
184    fn test_ast_collector() {
185        let code = parse_quote! {
186            fn hello() {
187                println!("Hello");
188            }
189            
190            struct Point {
191                x: f32,
192                y: f32,
193            }
194            
195            enum Direction {
196                North,
197                South,
198                East,
199                West,
200            }
201        };
202        
203        let mut collector = AstCollector::new();
204        collector.analyze(&code);
205        
206        assert_eq!(collector.functions.len(), 1);
207        assert_eq!(collector.functions[0], "hello");
208        
209        assert_eq!(collector.structs.len(), 1);
210        assert_eq!(collector.structs[0], "Point");
211        
212        assert_eq!(collector.enums.len(), 1);
213        assert_eq!(collector.enums[0], "Direction");
214    }
215}