Skip to main content

ownsight_driver/
simple_analyzer.rs

1use ownsight_core::*;
2use anyhow::Result;
3use std::collections::HashMap;
4
5pub struct SimpleAnalyzer {
6    mode: AnalysisMode,
7}
8
9impl SimpleAnalyzer {
10    pub fn new(mode: AnalysisMode) -> Self {
11        Self { mode }
12    }
13    
14    pub fn analyze(&mut self, source: &str, filename: &str) -> Result<ProgramAnalysis> {
15        let mut analyzer = Analyzer::new(self.mode.clone());
16        
17        analyzer.analyze_snippet(source, filename)?;
18        
19        let parsed = self.parse_simple_rust(source, filename)?;
20        
21        analyzer.analysis.variables = parsed.variables;
22        analyzer.analysis.events = parsed.events;
23        analyzer.analysis.scopes = parsed.scopes;
24        analyzer.analysis.functions = parsed.functions;
25        
26        Ok(analyzer.finalize())
27    }
28    
29    fn parse_simple_rust(&self, source: &str, filename: &str) -> Result<ParsedProgram> {
30        let mut parsed = ParsedProgram::default();
31        let lines: Vec<&str> = source.lines().collect();
32        
33        let mut var_counter = 0;
34        let _event_counter = 0;
35        let mut var_map: HashMap<String, VariableId> = HashMap::new();
36        let mut event_builder = EventBuilder::new();
37        
38        let root_scope = Scope {
39            id: ScopeId(0),
40            parent: None,
41            start_line: 1,
42            end_line: lines.len(),
43            kind: ScopeKind::Function,
44        };
45        parsed.scopes.push(root_scope);
46        
47        for (line_idx, line) in lines.iter().enumerate() {
48            let line_num = line_idx + 1;
49            let trimmed = line.trim();
50            
51            if trimmed.starts_with("let ") {
52                if let Some(var_info) = self.parse_let_statement(trimmed, line_num, filename) {
53                    let var_id = VariableId(var_counter);
54                    var_counter += 1;
55                    
56                    let variable = Variable {
57                        id: var_id,
58                        name: var_info.name.clone(),
59                        ty: var_info.ty.clone(),
60                        scope_id: ScopeId(0),
61                        span: Span::single_line(filename.to_string(), line_num, 0, line.len()),
62                        is_mutable: var_info.is_mutable,
63                    };
64                    
65                    var_map.insert(var_info.name.clone(), var_id);
66                    parsed.variables.push(variable);
67                    
68                    let create_event = event_builder.create_detailed_event(
69                        EventKind::Create,
70                        var_id,
71                        Span::single_line(filename.to_string(), line_num, 0, line.len()),
72                        line_num,
73                        &var_info.name,
74                        None,
75                    );
76                    parsed.events.push(create_event);
77                    
78                    if trimmed.contains("&mut ") {
79                        if let Some(borrowed_name) = self.extract_borrowed_var(trimmed) {
80                            if let Some(&borrowed_id) = var_map.get(&borrowed_name) {
81                                let mut borrow_event = event_builder.create_detailed_event(
82                                    EventKind::BorrowMut,
83                                    borrowed_id,
84                                    Span::single_line(filename.to_string(), line_num, 0, line.len()),
85                                    line_num,
86                                    &borrowed_name,
87                                    None,
88                                );
89                                borrow_event.related_variable_id = Some(var_id);
90                                parsed.events.push(borrow_event);
91                            }
92                        }
93                    } else if trimmed.contains("&") && !trimmed.contains("&mut") {
94                        if let Some(borrowed_name) = self.extract_borrowed_var(trimmed) {
95                            if let Some(&borrowed_id) = var_map.get(&borrowed_name) {
96                                let mut borrow_event = event_builder.create_detailed_event(
97                                    EventKind::BorrowShared,
98                                    borrowed_id,
99                                    Span::single_line(filename.to_string(), line_num, 0, line.len()),
100                                    line_num,
101                                    &borrowed_name,
102                                    None,
103                                );
104                                borrow_event.related_variable_id = Some(var_id);
105                                parsed.events.push(borrow_event);
106                            }
107                        }
108                    }
109                }
110            }
111            
112            if let Some(func_call) = self.parse_function_call(trimmed) {
113                for arg in &func_call.args {
114                    if let Some(&var_id) = var_map.get(arg) {
115                        if !trimmed.contains(&format!("&{}", arg)) {
116                            let move_event = event_builder.create_detailed_event(
117                                EventKind::MoveOut,
118                                var_id,
119                                Span::single_line(filename.to_string(), line_num, 0, line.len()),
120                                line_num,
121                                arg,
122                                Some(format!(
123                                    "`{}` was moved into function `{}`. It cannot be used after this point.",
124                                    arg, func_call.name
125                                )),
126                            );
127                            parsed.events.push(move_event);
128                        }
129                    }
130                }
131            }
132            
133            if trimmed == "}" {
134                for (var_name, &var_id) in &var_map {
135                    let last_event = parsed.events.iter()
136                        .filter(|e| e.variable_id == var_id)
137                        .last();
138                    
139                    if let Some(last) = last_event {
140                        if last.kind != EventKind::MoveOut && last.kind != EventKind::Drop {
141                            let drop_event = event_builder.create_detailed_event(
142                                EventKind::Drop,
143                                var_id,
144                                Span::single_line(filename.to_string(), line_num, 0, line.len()),
145                                line_num,
146                                var_name,
147                                None,
148                            );
149                            parsed.events.push(drop_event);
150                        }
151                    }
152                }
153            }
154        }
155        
156        Ok(parsed)
157    }
158    
159    fn parse_let_statement(&self, line: &str, _line_num: usize, _filename: &str) -> Option<VarInfo> {
160        let is_mutable = line.contains("let mut ");
161        let after_let = if is_mutable {
162            line.strip_prefix("let mut ")?.trim()
163        } else {
164            line.strip_prefix("let ")?.trim()
165        };
166        
167        let parts: Vec<&str> = after_let.split('=').collect();
168        if parts.is_empty() {
169            return None;
170        }
171        
172        let var_part = parts[0].trim();
173        let name_and_type: Vec<&str> = var_part.split(':').collect();
174        
175        let name = name_and_type[0].trim().to_string();
176        let ty = if name_and_type.len() > 1 {
177            name_and_type[1].trim().to_string()
178        } else {
179            "inferred".to_string()
180        };
181        
182        Some(VarInfo { name, ty, is_mutable })
183    }
184    
185    fn extract_borrowed_var(&self, line: &str) -> Option<String> {
186        if let Some(pos) = line.find("&mut ") {
187            let after = &line[pos + 5..];
188            let var_name = after.split(|c: char| !c.is_alphanumeric() && c != '_')
189                .next()?
190                .trim();
191            return Some(var_name.to_string());
192        } else if let Some(pos) = line.find("&") {
193            let after = &line[pos + 1..];
194            let var_name = after.split(|c: char| !c.is_alphanumeric() && c != '_')
195                .next()?
196                .trim();
197            if !var_name.is_empty() && var_name != "mut" {
198                return Some(var_name.to_string());
199            }
200        }
201        None
202    }
203    
204    fn parse_function_call(&self, line: &str) -> Option<FunctionCall> {
205        if !line.contains('(') || !line.contains(')') {
206            return None;
207        }
208        
209        let parts: Vec<&str> = line.split('(').collect();
210        if parts.len() < 2 {
211            return None;
212        }
213        
214        let func_name = parts[0].trim().split_whitespace().last()?.to_string();
215        
216        let args_part = parts[1].split(')').next()?;
217        let args: Vec<String> = args_part
218            .split(',')
219            .map(|s| s.trim())
220            .filter(|s| !s.is_empty())
221            .map(|s| {
222                s.trim_start_matches('&')
223                    .trim_start_matches("mut ")
224                    .trim()
225                    .to_string()
226            })
227            .collect();
228        
229        Some(FunctionCall {
230            name: func_name,
231            args,
232        })
233    }
234}
235
236#[derive(Default)]
237struct ParsedProgram {
238    variables: Vec<Variable>,
239    events: Vec<Event>,
240    scopes: Vec<Scope>,
241    functions: Vec<FunctionInfo>,
242}
243
244struct VarInfo {
245    name: String,
246    ty: String,
247    is_mutable: bool,
248}
249
250struct FunctionCall {
251    name: String,
252    args: Vec<String>,
253}