Skip to main content

ownsight_core/
analysis.rs

1use crate::model::*;
2use crate::graph::*;
3use anyhow::Result;
4
5pub struct Analyzer {
6    pub analysis: ProgramAnalysis,
7}
8
9impl Analyzer {
10    pub fn new(mode: AnalysisMode) -> Self {
11        Self {
12            analysis: ProgramAnalysis::new(mode),
13        }
14    }
15    
16    pub fn analyze_snippet(&mut self, code: &str, filename: &str) -> Result<()> {
17        let source_file = SourceFile::new(filename.to_string(), code.to_string());
18        self.analysis.files.push(source_file);
19        
20        Ok(())
21    }
22    
23    pub fn build_ownership_graph(&mut self) {
24        for event in &self.analysis.events {
25            let var_node = GraphNode::Variable(event.variable_id);
26            self.analysis.ownership_graph.add_node(var_node.clone());
27            
28            match event.kind {
29                EventKind::MoveOut => {
30                    if let Some(related_id) = event.related_variable_id {
31                        let target_node = GraphNode::Variable(related_id);
32                        self.analysis.ownership_graph.add_node(target_node.clone());
33                        self.analysis.ownership_graph.add_edge(
34                            GraphEdge::new(var_node, target_node, EdgeKind::MovesTo)
35                        );
36                    }
37                }
38                EventKind::BorrowShared => {
39                    if let Some(related_id) = event.related_variable_id {
40                        let ref_node = GraphNode::Reference(related_id);
41                        self.analysis.ownership_graph.add_node(ref_node.clone());
42                        self.analysis.ownership_graph.add_edge(
43                            GraphEdge::new(ref_node, var_node, EdgeKind::Borrows)
44                        );
45                    }
46                }
47                EventKind::BorrowMut => {
48                    if let Some(related_id) = event.related_variable_id {
49                        let ref_node = GraphNode::Reference(related_id);
50                        self.analysis.ownership_graph.add_node(ref_node.clone());
51                        self.analysis.ownership_graph.add_edge(
52                            GraphEdge::new(ref_node, var_node, EdgeKind::MutablyBorrows)
53                        );
54                    }
55                }
56                _ => {}
57            }
58        }
59    }
60    
61    pub fn query_why_cant_use(&self, var_id: VariableId, line: usize) -> Option<String> {
62        let state = self.analysis.get_ownership_state_at_line(line);
63        
64        if state.is_moved(var_id) {
65            let move_events: Vec<_> = self.analysis.events.iter()
66                .filter(|e| e.variable_id == var_id && e.kind == EventKind::MoveOut && e.line_number < line)
67                .collect();
68            
69            if let Some(move_event) = move_events.last() {
70                let var = self.analysis.get_variable(var_id)?;
71                return Some(format!(
72                    "Cannot use `{}` because it was moved at line {}. {}",
73                    var.name, move_event.line_number, move_event.explanation
74                ));
75            }
76        }
77        
78        None
79    }
80    
81    pub fn query_where_moved(&self, var_id: VariableId) -> Vec<usize> {
82        self.analysis.events.iter()
83            .filter(|e| e.variable_id == var_id && e.kind == EventKind::MoveOut)
84            .map(|e| e.line_number)
85            .collect()
86    }
87    
88    pub fn query_what_borrows(&self, var_id: VariableId, line: usize) -> Vec<String> {
89        let state = self.analysis.get_ownership_state_at_line(line);
90        let mut results = Vec::new();
91        
92        for borrow in &state.active_borrows {
93            if borrow.borrowed_var == var_id {
94                if let Some(borrow_var_id) = borrow.borrow_var {
95                    if let Some(var) = self.analysis.get_variable(borrow_var_id) {
96                        let borrow_type = if borrow.is_mutable { "mutably" } else { "immutably" };
97                        results.push(format!("`{}` is borrowed {} by `{}`", 
98                            self.analysis.get_variable(var_id).map(|v| v.name.as_str()).unwrap_or("?"),
99                            borrow_type,
100                            var.name
101                        ));
102                    }
103                }
104            }
105        }
106        
107        results
108    }
109    
110    pub fn finalize(mut self) -> ProgramAnalysis {
111        self.build_ownership_graph();
112        self.analysis
113    }
114}