Skip to main content

luaur_analysis/records/
find_expr_or_local.rs

1use luaur_ast::records::ast_expr::AstExpr;
2use luaur_ast::records::ast_expr_function::AstExprFunction;
3use luaur_ast::records::ast_local::AstLocal;
4use luaur_ast::records::ast_node::AstNode;
5use luaur_ast::records::ast_stat_block::AstStatBlock;
6use luaur_ast::records::ast_stat_for::AstStatFor;
7use luaur_ast::records::ast_stat_for_in::AstStatForIn;
8use luaur_ast::records::ast_stat_local::AstStatLocal;
9use luaur_ast::records::ast_stat_local_function::AstStatLocalFunction;
10use luaur_ast::records::ast_visitor::AstVisitor;
11use luaur_ast::records::location::Location;
12use luaur_ast::records::position::Position;
13use luaur_ast::visit;
14
15use crate::records::expr_or_local::ExprOrLocal;
16
17#[derive(Debug, Clone)]
18pub struct FindExprOrLocal {
19    pub(crate) pos: Position,
20    pub(crate) result: ExprOrLocal,
21}
22
23impl FindExprOrLocal {
24    pub fn new(pos: Position) -> Self {
25        Self {
26            pos,
27            result: ExprOrLocal {
28                expr: core::ptr::null_mut(),
29                local: core::ptr::null_mut(),
30            },
31        }
32    }
33
34    pub(crate) fn is_closer_match(&self, new_location: Location) -> bool {
35        let current = self.result.get_location();
36        new_location.contains(self.pos)
37            && (current.is_none() || current.map_or(false, |c| c.encloses(&new_location)))
38    }
39
40    fn visit_local(&mut self, local: *mut AstLocal) -> bool {
41        let location = unsafe { (*local).location };
42        if self.is_closer_match(location) {
43            self.result.set_local(local);
44            true
45        } else {
46            false
47        }
48    }
49}
50
51impl AstVisitor for FindExprOrLocal {
52    fn visit_stat_block(&mut self, node: *mut core::ffi::c_void) -> bool {
53        let block = node as *mut AstStatBlock;
54        unsafe {
55            for stat in (*block).body.iter() {
56                let stat = *stat;
57                let stat_node = stat as *mut AstNode;
58                if (*stat_node).location.end <= self.pos {
59                    continue;
60                }
61                if (*stat_node).location.begin > self.pos {
62                    break;
63                }
64                visit::ast_stat_visit(stat, self);
65            }
66        }
67        false
68    }
69
70    fn visit_expr(&mut self, node: *mut core::ffi::c_void) -> bool {
71        let expr = node as *mut AstExpr;
72        let location = unsafe { (*expr).base.location };
73        if self.is_closer_match(location) {
74            self.result.set_expr(expr);
75            true
76        } else {
77            false
78        }
79    }
80
81    fn visit_stat_local_function(&mut self, node: *mut core::ffi::c_void) -> bool {
82        let func = node as *mut AstStatLocalFunction;
83        unsafe {
84            self.visit_local((*func).name);
85        }
86        true
87    }
88
89    fn visit_stat_local(&mut self, node: *mut core::ffi::c_void) -> bool {
90        let stat = node as *mut AstStatLocal;
91        unsafe {
92            for i in 0..(*stat).vars.size {
93                let local = unsafe { *((*stat).vars.data.add(i as usize)) };
94                self.visit_local(local);
95            }
96        }
97        true
98    }
99
100    fn visit_expr_function(&mut self, node: *mut core::ffi::c_void) -> bool {
101        let func = node as *mut AstExprFunction;
102        unsafe {
103            for i in 0..(*func).args.size {
104                let local = unsafe { *((*func).args.data.add(i as usize)) };
105                self.visit_local(local);
106            }
107        }
108        self.visit_expr(node)
109    }
110
111    fn visit_stat_for(&mut self, node: *mut core::ffi::c_void) -> bool {
112        let stat = node as *mut AstStatFor;
113        unsafe {
114            self.visit_local((*stat).var);
115        }
116        true
117    }
118
119    fn visit_stat_for_in(&mut self, node: *mut core::ffi::c_void) -> bool {
120        let stat = node as *mut AstStatForIn;
121        unsafe {
122            for i in 0..(*stat).vars.size {
123                let local = unsafe { *((*stat).vars.data.add(i as usize)) };
124                self.visit_local(local);
125            }
126        }
127        true
128    }
129}