1use serde::{Deserialize, Serialize};
2use std::collections::HashMap;
3
4#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
5pub struct VariableId(pub usize);
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
8pub struct EventId(pub usize);
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
11pub struct ScopeId(pub usize);
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
14pub struct FunctionId(pub usize);
15
16#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
17pub struct Span {
18 pub file: String,
19 pub start_line: usize,
20 pub start_col: usize,
21 pub end_line: usize,
22 pub end_col: usize,
23}
24
25impl Span {
26 pub fn new(file: String, start_line: usize, start_col: usize, end_line: usize, end_col: usize) -> Self {
27 Self { file, start_line, start_col, end_line, end_col }
28 }
29
30 pub fn single_line(file: String, line: usize, start_col: usize, end_col: usize) -> Self {
31 Self::new(file, line, start_col, line, end_col)
32 }
33}
34
35#[derive(Debug, Clone, Serialize, Deserialize)]
36pub struct Variable {
37 pub id: VariableId,
38 pub name: String,
39 pub ty: String,
40 pub scope_id: ScopeId,
41 pub span: Span,
42 pub is_mutable: bool,
43}
44
45#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
46pub enum EventKind {
47 Create,
48 MoveOut,
49 MoveIn,
50 BorrowShared,
51 BorrowMut,
52 Reborrow,
53 Use,
54 Drop,
55 StorageLive,
56 StorageDead,
57 Reinit,
58 Conflict,
59}
60
61#[derive(Debug, Clone, Serialize, Deserialize)]
62pub struct Event {
63 pub id: EventId,
64 pub kind: EventKind,
65 pub variable_id: VariableId,
66 pub related_variable_id: Option<VariableId>,
67 pub span: Span,
68 pub explanation: String,
69 pub line_number: usize,
70}
71
72impl Event {
73 pub fn new(
74 id: EventId,
75 kind: EventKind,
76 variable_id: VariableId,
77 span: Span,
78 line_number: usize,
79 explanation: String,
80 ) -> Self {
81 Self {
82 id,
83 kind,
84 variable_id,
85 related_variable_id: None,
86 span,
87 explanation,
88 line_number,
89 }
90 }
91
92 pub fn with_related(mut self, related: VariableId) -> Self {
93 self.related_variable_id = Some(related);
94 self
95 }
96}
97
98#[derive(Debug, Clone, Serialize, Deserialize)]
99pub struct Scope {
100 pub id: ScopeId,
101 pub parent: Option<ScopeId>,
102 pub start_line: usize,
103 pub end_line: usize,
104 pub kind: ScopeKind,
105}
106
107#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
108pub enum ScopeKind {
109 Function,
110 Block,
111 Loop,
112 If,
113 Match,
114}
115
116#[derive(Debug, Clone, Serialize, Deserialize)]
117pub struct FunctionInfo {
118 pub id: FunctionId,
119 pub name: String,
120 pub span: Span,
121 pub parameters: Vec<Parameter>,
122 pub return_type: String,
123 pub summary: Option<FunctionSummary>,
124}
125
126#[derive(Debug, Clone, Serialize, Deserialize)]
127pub struct Parameter {
128 pub name: String,
129 pub ty: String,
130 pub ownership_behavior: OwnershipBehavior,
131}
132
133#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
134pub enum OwnershipBehavior {
135 Consumes,
136 SharedBorrow,
137 MutableBorrow,
138}
139
140#[derive(Debug, Clone, Serialize, Deserialize)]
141pub struct FunctionSummary {
142 pub consumes: Vec<String>,
143 pub borrows_shared: Vec<String>,
144 pub borrows_mut: Vec<String>,
145 pub returns_ownership: bool,
146 pub references_escape: bool,
147}
148
149#[derive(Debug, Clone, Serialize, Deserialize)]
150pub struct SourceFile {
151 pub path: String,
152 pub content: String,
153 pub lines: Vec<String>,
154}
155
156impl SourceFile {
157 pub fn new(path: String, content: String) -> Self {
158 let lines = content.lines().map(|s| s.to_string()).collect();
159 Self { path, content, lines }
160 }
161}
162
163#[derive(Debug, Clone, Serialize, Deserialize)]
164pub struct Diagnostic {
165 pub level: DiagnosticLevel,
166 pub message: String,
167 pub span: Span,
168 pub code: Option<String>,
169 pub suggestion: Option<String>,
170}
171
172#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
173pub enum DiagnosticLevel {
174 Error,
175 Warning,
176 Note,
177 Help,
178}
179
180#[derive(Debug, Clone, Serialize, Deserialize)]
181pub struct ProgramAnalysis {
182 pub files: Vec<SourceFile>,
183 pub functions: Vec<FunctionInfo>,
184 pub variables: Vec<Variable>,
185 pub scopes: Vec<Scope>,
186 pub events: Vec<Event>,
187 pub ownership_graph: crate::graph::OwnershipGraph,
188 pub diagnostics: Vec<Diagnostic>,
189 pub metadata: AnalysisMetadata,
190}
191
192#[derive(Debug, Clone, Serialize, Deserialize)]
193pub struct AnalysisMetadata {
194 pub mode: AnalysisMode,
195 pub timestamp: String,
196 pub version: String,
197}
198
199#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
200pub enum AnalysisMode {
201 Teaching,
202 Debug,
203}
204
205impl ProgramAnalysis {
206 pub fn new(mode: AnalysisMode) -> Self {
207 Self {
208 files: Vec::new(),
209 functions: Vec::new(),
210 variables: Vec::new(),
211 scopes: Vec::new(),
212 events: Vec::new(),
213 ownership_graph: crate::graph::OwnershipGraph::new(),
214 diagnostics: Vec::new(),
215 metadata: AnalysisMetadata {
216 mode,
217 timestamp: chrono::Utc::now().to_rfc3339(),
218 version: env!("CARGO_PKG_VERSION").to_string(),
219 },
220 }
221 }
222
223 pub fn get_variable(&self, id: VariableId) -> Option<&Variable> {
224 self.variables.iter().find(|v| v.id == id)
225 }
226
227 pub fn get_events_for_variable(&self, id: VariableId) -> Vec<&Event> {
228 self.events.iter()
229 .filter(|e| e.variable_id == id || e.related_variable_id == Some(id))
230 .collect()
231 }
232
233 pub fn get_events_at_line(&self, line: usize) -> Vec<&Event> {
234 self.events.iter()
235 .filter(|e| e.line_number == line)
236 .collect()
237 }
238
239 pub fn get_ownership_state_at_line(&self, line: usize) -> OwnershipState {
240 let mut state = OwnershipState::new();
241
242 for event in self.events.iter().filter(|e| e.line_number <= line) {
243 state.apply_event(event);
244 }
245
246 state
247 }
248}
249
250#[derive(Debug, Clone, Serialize, Deserialize)]
251pub struct OwnershipState {
252 pub valid_variables: HashMap<VariableId, VariableState>,
253 pub active_borrows: Vec<BorrowInfo>,
254}
255
256impl OwnershipState {
257 pub fn new() -> Self {
258 Self {
259 valid_variables: HashMap::new(),
260 active_borrows: Vec::new(),
261 }
262 }
263
264 pub fn apply_event(&mut self, event: &Event) {
265 match event.kind {
266 EventKind::Create | EventKind::StorageLive => {
267 self.valid_variables.insert(event.variable_id, VariableState::Valid);
268 }
269 EventKind::MoveOut => {
270 self.valid_variables.insert(event.variable_id, VariableState::MovedOut);
271 }
272 EventKind::MoveIn => {
273 self.valid_variables.insert(event.variable_id, VariableState::Valid);
274 }
275 EventKind::BorrowShared => {
276 self.active_borrows.push(BorrowInfo {
277 borrowed_var: event.variable_id,
278 borrow_var: event.related_variable_id,
279 is_mutable: false,
280 });
281 }
282 EventKind::BorrowMut => {
283 self.active_borrows.push(BorrowInfo {
284 borrowed_var: event.variable_id,
285 borrow_var: event.related_variable_id,
286 is_mutable: true,
287 });
288 }
289 EventKind::Drop | EventKind::StorageDead => {
290 self.valid_variables.remove(&event.variable_id);
291 self.active_borrows.retain(|b| b.borrowed_var != event.variable_id);
292 }
293 EventKind::Reinit => {
294 self.valid_variables.insert(event.variable_id, VariableState::Valid);
295 }
296 _ => {}
297 }
298 }
299
300 pub fn is_valid(&self, var_id: VariableId) -> bool {
301 matches!(self.valid_variables.get(&var_id), Some(VariableState::Valid))
302 }
303
304 pub fn is_moved(&self, var_id: VariableId) -> bool {
305 matches!(self.valid_variables.get(&var_id), Some(VariableState::MovedOut))
306 }
307}
308
309#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
310pub enum VariableState {
311 Valid,
312 MovedOut,
313 PartiallyMoved,
314}
315
316#[derive(Debug, Clone, Serialize, Deserialize)]
317pub struct BorrowInfo {
318 pub borrowed_var: VariableId,
319 pub borrow_var: Option<VariableId>,
320 pub is_mutable: bool,
321}