1use crate::core::types::AllocationInfo;
10use serde::{Deserialize, Serialize};
11use std::collections::HashMap;
12use std::sync::{Arc, Mutex, OnceLock};
13
14static GLOBAL_CLOSURE_ANALYZER: OnceLock<Arc<ClosureAnalyzer>> = OnceLock::new();
16
17pub fn get_global_closure_analyzer() -> Arc<ClosureAnalyzer> {
19 GLOBAL_CLOSURE_ANALYZER
20 .get_or_init(|| Arc::new(ClosureAnalyzer::new()))
21 .clone()
22}
23
24pub struct ClosureAnalyzer {
26 closures: Mutex<HashMap<usize, ClosureInfo>>,
28 capture_events: Mutex<Vec<CaptureEvent>>,
30 lifetime_graph: Mutex<LifetimeGraph>,
32}
33
34impl ClosureAnalyzer {
35 pub fn new() -> Self {
37 Self {
38 closures: Mutex::new(HashMap::new()),
39 capture_events: Mutex::new(Vec::new()),
40 lifetime_graph: Mutex::new(LifetimeGraph::new()),
41 }
42 }
43
44 pub fn register_closure(&self, closure_ptr: usize, captures: Vec<CaptureInfo>) {
46 let closure_info = ClosureInfo {
47 ptr: closure_ptr,
48 captures: captures.clone(),
49 creation_timestamp: current_timestamp(),
50 thread_id: format!("{:?}", std::thread::current().id()),
51 call_site: capture_call_site(),
52 memory_footprint: self.calculate_closure_footprint(&captures),
53 optimization_potential: self.analyze_optimization_potential(&captures),
54 };
55
56 for capture in &captures {
58 let event = CaptureEvent {
59 closure_ptr,
60 captured_var: capture.clone(),
61 event_type: CaptureEventType::Captured,
62 timestamp: current_timestamp(),
63 };
64
65 if let Ok(mut events) = self.capture_events.lock() {
66 events.push(event);
67 }
68 }
69
70 if let Ok(mut graph) = self.lifetime_graph.lock() {
72 graph.add_closure_relationships(closure_ptr, &captures);
73 }
74
75 if let Ok(mut closures) = self.closures.lock() {
77 closures.insert(closure_ptr, closure_info);
78 }
79 }
80
81 pub fn track_closure_drop(&self, closure_ptr: usize) {
83 if let Ok(mut closures) = self.closures.lock() {
84 if let Some(closure_info) = closures.get_mut(&closure_ptr) {
85 for capture in &closure_info.captures {
87 let event = CaptureEvent {
88 closure_ptr,
89 captured_var: capture.clone(),
90 event_type: CaptureEventType::Released,
91 timestamp: current_timestamp(),
92 };
93
94 if let Ok(mut events) = self.capture_events.lock() {
95 events.push(event);
96 }
97 }
98 }
99 closures.remove(&closure_ptr);
100 }
101
102 if let Ok(mut graph) = self.lifetime_graph.lock() {
104 graph.remove_closure(closure_ptr);
105 }
106 }
107
108 pub fn analyze_closure_patterns(
110 &self,
111 allocations: &[AllocationInfo],
112 ) -> ClosureAnalysisReport {
113 let mut detected_closures = Vec::new();
114 let mut capture_statistics = CaptureStatistics::default();
115
116 for allocation in allocations {
117 if let Some(type_name) = &allocation.type_name {
118 if self.is_closure_type(type_name) {
119 if let Some(analysis) = self.analyze_closure_allocation(allocation) {
120 detected_closures.push(analysis);
121 }
122 }
123 }
124 }
125
126 if let Ok(closures) = self.closures.lock() {
128 capture_statistics = self.calculate_capture_statistics(&closures);
129 }
130
131 let optimization_suggestions = self.generate_optimization_suggestions(&detected_closures);
132 let lifetime_analysis = self.analyze_capture_lifetimes();
133
134 ClosureAnalysisReport {
135 detected_closures,
136 capture_statistics,
137 optimization_suggestions,
138 lifetime_analysis,
139 analysis_timestamp: current_timestamp(),
140 }
141 }
142
143 fn is_closure_type(&self, type_name: &str) -> bool {
145 type_name.contains("closure")
150 || type_name.contains("{{closure}}")
151 || type_name.starts_with("fn(")
152 || type_name.contains("dyn Fn")
153 || type_name.contains("impl Fn")
154 }
155
156 fn analyze_closure_allocation(&self, allocation: &AllocationInfo) -> Option<DetectedClosure> {
158 let type_name = allocation.type_name.as_ref()?;
159
160 Some(DetectedClosure {
161 ptr: allocation.ptr,
162 type_name: type_name.clone(),
163 size: allocation.size,
164 estimated_captures: self.estimate_captures_from_size(allocation.size),
165 closure_type: self.classify_closure_type(type_name),
166 creation_context: CreationContext {
167 scope_name: allocation.scope_name.clone(),
168 thread_id: allocation.thread_id.clone(),
169 timestamp: allocation.timestamp_alloc,
170 },
171 memory_impact: self.assess_memory_impact(allocation.size),
172 })
173 }
174
175 fn estimate_captures_from_size(&self, size: usize) -> usize {
177 if size <= 8 {
180 0 } else if size <= 32 {
182 2 } else if size <= 128 {
184 8 } else {
186 size / 16 }
188 }
189
190 fn classify_closure_type(&self, type_name: &str) -> ClosureType {
192 if type_name.contains("FnOnce") {
193 ClosureType::FnOnce
194 } else if type_name.contains("FnMut") {
195 ClosureType::FnMut
196 } else if type_name.contains("Fn") {
197 ClosureType::Fn
198 } else {
199 ClosureType::Unknown
200 }
201 }
202
203 fn assess_memory_impact(&self, size: usize) -> MemoryImpact {
205 match size {
206 0..=16 => MemoryImpact::Minimal,
207 17..=64 => MemoryImpact::Low,
208 65..=256 => MemoryImpact::Medium,
209 257..=1024 => MemoryImpact::High,
210 _ => MemoryImpact::VeryHigh,
211 }
212 }
213
214 fn calculate_closure_footprint(&self, captures: &[CaptureInfo]) -> ClosureFootprint {
216 let total_size = captures.iter().map(|c| c.size).sum();
217 let by_value_count = captures
218 .iter()
219 .filter(|c| c.mode == CaptureMode::ByValue)
220 .count();
221 let by_ref_count = captures
222 .iter()
223 .filter(|c| c.mode == CaptureMode::ByReference)
224 .count();
225 let by_mut_ref_count = captures
226 .iter()
227 .filter(|c| c.mode == CaptureMode::ByMutableReference)
228 .count();
229
230 ClosureFootprint {
231 total_size,
232 capture_count: captures.len(),
233 by_value_count,
234 by_ref_count,
235 by_mut_ref_count,
236 estimated_heap_usage: self.estimate_heap_usage(captures),
237 }
238 }
239
240 fn estimate_heap_usage(&self, captures: &[CaptureInfo]) -> usize {
242 captures
243 .iter()
244 .filter(|c| c.mode == CaptureMode::ByValue)
245 .filter(|c| self.is_heap_allocated_type(&c.var_type))
246 .map(|c| c.size)
247 .sum()
248 }
249
250 fn is_heap_allocated_type(&self, type_name: &str) -> bool {
252 type_name.contains("Vec")
253 || type_name.contains("String")
254 || type_name.contains("HashMap")
255 || type_name.contains("Box")
256 || type_name.contains("Arc")
257 || type_name.contains("Rc")
258 }
259
260 fn analyze_optimization_potential(&self, captures: &[CaptureInfo]) -> OptimizationPotential {
262 let mut suggestions = Vec::new();
263 let mut potential_savings = 0;
264
265 for capture in captures {
267 if capture.mode == CaptureMode::ByValue && capture.size > 64 {
268 suggestions.push(format!(
269 "Consider capturing '{}' by reference instead of by value to save {} bytes",
270 capture.var_name, capture.size
271 ));
272 potential_savings += capture.size;
273 }
274 }
275
276 let mut_captures = captures
278 .iter()
279 .filter(|c| c.mode == CaptureMode::ByMutableReference)
280 .count();
281 if mut_captures > captures.len() / 2 {
282 suggestions.push("Consider if all mutable captures are necessary".to_string());
283 }
284
285 let heap_captures = captures
287 .iter()
288 .filter(|c| c.mode == CaptureMode::ByValue && self.is_heap_allocated_type(&c.var_type))
289 .count();
290
291 if heap_captures > 0 {
292 suggestions
293 .push("Consider using move semantics for heap-allocated captures".to_string());
294 }
295
296 OptimizationPotential {
297 level: if potential_savings > 256 {
298 OptimizationLevel::High
299 } else if potential_savings > 64 {
300 OptimizationLevel::Medium
301 } else if !suggestions.is_empty() {
302 OptimizationLevel::Low
303 } else {
304 OptimizationLevel::None
305 },
306 potential_savings,
307 suggestions,
308 }
309 }
310
311 fn calculate_capture_statistics(
313 &self,
314 closures: &HashMap<usize, ClosureInfo>,
315 ) -> CaptureStatistics {
316 let total_closures = closures.len();
317 let total_captures = closures.values().map(|c| c.captures.len()).sum();
318
319 let mut by_mode = HashMap::new();
320 let mut by_type = HashMap::new();
321 let mut total_memory = 0;
322
323 for closure in closures.values() {
324 total_memory += closure.memory_footprint.total_size;
325
326 for capture in &closure.captures {
327 *by_mode.entry(capture.mode.clone()).or_insert(0) += 1;
328 *by_type.entry(capture.var_type.clone()).or_insert(0) += 1;
329 }
330 }
331
332 let avg_captures_per_closure = if total_closures > 0 {
333 total_captures as f64 / total_closures as f64
334 } else {
335 0.0
336 };
337
338 CaptureStatistics {
339 total_closures,
340 total_captures,
341 avg_captures_per_closure,
342 total_memory_usage: total_memory,
343 captures_by_mode: by_mode,
344 captures_by_type: by_type,
345 }
346 }
347
348 fn generate_optimization_suggestions(
350 &self,
351 closures: &[DetectedClosure],
352 ) -> Vec<OptimizationSuggestion> {
353 let mut suggestions = Vec::new();
354
355 let high_memory_closures = closures
357 .iter()
358 .filter(|c| matches!(c.memory_impact, MemoryImpact::High | MemoryImpact::VeryHigh))
359 .count();
360
361 if high_memory_closures > 0 {
362 suggestions.push(OptimizationSuggestion {
363 category: OptimizationCategory::Memory,
364 priority: SuggestionPriority::High,
365 description: format!(
366 "Found {high_memory_closures} closures with high memory usage",
367 ),
368 recommendation: "Consider reducing capture size or using references".to_string(),
369 estimated_impact: "20-50% memory reduction".to_string(),
370 });
371 }
372
373 let fnonce_count = closures
375 .iter()
376 .filter(|c| c.closure_type == ClosureType::FnOnce)
377 .count();
378
379 if fnonce_count > closures.len() / 2 {
380 suggestions.push(OptimizationSuggestion {
381 category: OptimizationCategory::Performance,
382 priority: SuggestionPriority::Medium,
383 description: "Many FnOnce closures detected".to_string(),
384 recommendation: "Consider if Fn or FnMut traits would be more appropriate"
385 .to_string(),
386 estimated_impact: "Improved reusability".to_string(),
387 });
388 }
389
390 suggestions
391 }
392
393 fn analyze_capture_lifetimes(&self) -> LifetimeAnalysis {
395 if let Ok(graph) = self.lifetime_graph.lock() {
396 graph.analyze_lifetimes()
397 } else {
398 LifetimeAnalysis::default()
399 }
400 }
401}
402
403#[derive(Debug, Clone, Serialize, Deserialize)]
405pub struct ClosureInfo {
406 pub ptr: usize,
408 pub captures: Vec<CaptureInfo>,
410 pub creation_timestamp: u64,
412 pub thread_id: String,
414 pub call_site: String,
416 pub memory_footprint: ClosureFootprint,
418 pub optimization_potential: OptimizationPotential,
420}
421
422#[derive(Debug, Clone, Serialize, Deserialize)]
424pub struct CaptureInfo {
425 pub var_name: String,
427 pub var_ptr: usize,
429 pub mode: CaptureMode,
431 pub var_type: String,
433 pub size: usize,
435 pub lifetime_bound: Option<String>,
437}
438
439#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
441pub enum CaptureMode {
442 ByValue,
444 ByReference,
446 ByMutableReference,
448}
449
450#[derive(Debug, Clone, Serialize, Deserialize)]
452pub struct ClosureFootprint {
453 pub total_size: usize,
455 pub capture_count: usize,
457 pub by_value_count: usize,
459 pub by_ref_count: usize,
461 pub by_mut_ref_count: usize,
463 pub estimated_heap_usage: usize,
465}
466
467#[derive(Debug, Clone, Serialize, Deserialize)]
469pub struct OptimizationPotential {
470 pub level: OptimizationLevel,
472 pub potential_savings: usize,
474 pub suggestions: Vec<String>,
476}
477
478#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
480pub enum OptimizationLevel {
481 None,
483 Low,
485 Medium,
487 High,
489}
490
491#[derive(Debug, Clone, Serialize, Deserialize)]
493pub struct CaptureEvent {
494 pub closure_ptr: usize,
496 pub captured_var: CaptureInfo,
498 pub event_type: CaptureEventType,
500 pub timestamp: u64,
502}
503
504#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
506pub enum CaptureEventType {
507 Captured,
509 Released,
511}
512
513#[derive(Debug, Clone, Serialize, Deserialize)]
515pub struct DetectedClosure {
516 pub ptr: usize,
518 pub type_name: String,
520 pub size: usize,
522 pub estimated_captures: usize,
524 pub closure_type: ClosureType,
526 pub creation_context: CreationContext,
528 pub memory_impact: MemoryImpact,
530}
531
532#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
534pub enum ClosureType {
535 Fn,
537 FnMut,
539 FnOnce,
541 Unknown,
543}
544
545#[derive(Debug, Clone, Serialize, Deserialize)]
547pub struct CreationContext {
548 pub scope_name: Option<String>,
550 pub thread_id: String,
552 pub timestamp: u64,
554}
555
556#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
558pub enum MemoryImpact {
559 Minimal,
561 Low,
563 Medium,
565 High,
567 VeryHigh,
569}
570
571#[derive(Debug, Clone, Default, Serialize, Deserialize)]
573pub struct CaptureStatistics {
574 pub total_closures: usize,
576 pub total_captures: usize,
578 pub avg_captures_per_closure: f64,
580 pub total_memory_usage: usize,
582 pub captures_by_mode: HashMap<CaptureMode, usize>,
584 pub captures_by_type: HashMap<String, usize>,
586}
587
588#[derive(Debug, Clone, Serialize, Deserialize)]
590pub struct OptimizationSuggestion {
591 pub category: OptimizationCategory,
593 pub priority: SuggestionPriority,
595 pub description: String,
597 pub recommendation: String,
599 pub estimated_impact: String,
601}
602
603#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
605pub enum OptimizationCategory {
606 Memory,
608 Performance,
610 Lifetime,
612}
613
614#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
616pub enum SuggestionPriority {
617 Low,
619 Medium,
621 High,
623 Critical,
625}
626
627#[derive(Debug)]
629pub struct LifetimeGraph {
630 relationships: HashMap<usize, Vec<LifetimeRelationship>>,
632}
633
634impl Default for LifetimeGraph {
635 fn default() -> Self {
636 Self::new()
637 }
638}
639
640impl LifetimeGraph {
641 pub fn new() -> Self {
643 Self {
644 relationships: HashMap::new(),
645 }
646 }
647
648 pub fn add_closure_relationships(&mut self, closure_ptr: usize, captures: &[CaptureInfo]) {
650 let relationships: Vec<LifetimeRelationship> = captures
651 .iter()
652 .map(|capture| LifetimeRelationship {
653 captured_var_ptr: capture.var_ptr,
654 capture_mode: capture.mode.clone(),
655 relationship_type: self.classify_relationship(&capture.mode),
656 })
657 .collect();
658
659 self.relationships.insert(closure_ptr, relationships);
660 }
661
662 pub fn remove_closure(&mut self, closure_ptr: usize) {
664 self.relationships.remove(&closure_ptr);
665 }
666
667 fn classify_relationship(&self, mode: &CaptureMode) -> RelationshipType {
668 match mode {
669 CaptureMode::ByValue => RelationshipType::Ownership,
670 CaptureMode::ByReference => RelationshipType::SharedBorrow,
671 CaptureMode::ByMutableReference => RelationshipType::ExclusiveBorrow,
672 }
673 }
674
675 pub fn analyze_lifetimes(&self) -> LifetimeAnalysis {
677 let mut potential_issues = Vec::new();
678 let mut lifetime_patterns = Vec::new();
679
680 for (closure_ptr, relationships) in &self.relationships {
682 let has_value_captures = relationships
684 .iter()
685 .any(|r| r.capture_mode == CaptureMode::ByValue);
686 let has_ref_captures = relationships.iter().any(|r| {
687 matches!(
688 r.capture_mode,
689 CaptureMode::ByReference | CaptureMode::ByMutableReference
690 )
691 });
692
693 if has_value_captures && has_ref_captures {
694 potential_issues.push(LifetimeIssue {
695 closure_ptr: *closure_ptr,
696 issue_type: LifetimeIssueType::MixedCaptureMode,
697 description: "Closure mixes value and reference captures".to_string(),
698 severity: IssueSeverity::Medium,
699 suggestion: "Consider consistent capture strategy".to_string(),
700 });
701 }
702
703 if relationships.len() > 5 {
705 lifetime_patterns.push(LifetimePattern {
706 pattern_type: LifetimePatternType::ManyCaptures,
707 description: format!("Closure captures {} variables", relationships.len()),
708 impact: if relationships.len() > 10 {
709 PatternImpact::High
710 } else {
711 PatternImpact::Medium
712 },
713 });
714 }
715 }
716
717 LifetimeAnalysis {
718 total_relationships: self.relationships.len(),
719 potential_issues,
720 lifetime_patterns,
721 }
722 }
723}
724
725#[derive(Debug, Clone)]
727pub struct LifetimeRelationship {
728 pub captured_var_ptr: usize,
730 pub capture_mode: CaptureMode,
732 pub relationship_type: RelationshipType,
734}
735
736#[derive(Debug, Clone, PartialEq)]
738pub enum RelationshipType {
739 Ownership,
741 SharedBorrow,
743 ExclusiveBorrow,
745}
746
747#[derive(Debug, Clone, Default, Serialize, Deserialize)]
749pub struct LifetimeAnalysis {
750 pub total_relationships: usize,
752 pub potential_issues: Vec<LifetimeIssue>,
754 pub lifetime_patterns: Vec<LifetimePattern>,
756}
757
758#[derive(Debug, Clone, Serialize, Deserialize)]
760pub struct LifetimeIssue {
761 pub closure_ptr: usize,
763 pub issue_type: LifetimeIssueType,
765 pub description: String,
767 pub severity: IssueSeverity,
769 pub suggestion: String,
771}
772
773#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
775pub enum LifetimeIssueType {
776 MixedCaptureMode,
778 PotentialDanglingReference,
780 UnnecessaryCapture,
782}
783
784#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
786pub enum IssueSeverity {
787 Low,
789 Medium,
791 High,
793 Critical,
795}
796
797#[derive(Debug, Clone, Serialize, Deserialize)]
799pub struct LifetimePattern {
800 pub pattern_type: LifetimePatternType,
802 pub description: String,
804 pub impact: PatternImpact,
806}
807
808#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
810pub enum LifetimePatternType {
811 ManyCaptures,
813 LongLivedClosure,
815 FrequentCreation,
817}
818
819#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
821pub enum PatternImpact {
822 Low,
824 Medium,
826 High,
828}
829
830#[derive(Debug, Clone, Serialize, Deserialize)]
832pub struct ClosureAnalysisReport {
833 pub detected_closures: Vec<DetectedClosure>,
835 pub capture_statistics: CaptureStatistics,
837 pub optimization_suggestions: Vec<OptimizationSuggestion>,
839 pub lifetime_analysis: LifetimeAnalysis,
841 pub analysis_timestamp: u64,
843}
844
845fn current_timestamp() -> u64 {
849 std::time::SystemTime::now()
850 .duration_since(std::time::UNIX_EPOCH)
851 .unwrap_or_default()
852 .as_nanos() as u64
853}
854
855fn capture_call_site() -> String {
857 "<call_site_placeholder>".to_string()
859}
860
861impl Default for ClosureAnalyzer {
862 fn default() -> Self {
863 Self::new()
864 }
865}
866
867#[cfg(test)]
868mod tests {
869 use super::*;
870 use crate::core::types::AllocationInfo;
871
872 #[test]
873 fn test_closure_analyzer_creation() {
874 let analyzer = ClosureAnalyzer::new();
875
876 assert!(analyzer.closures.lock().unwrap().is_empty());
878 assert!(analyzer.capture_events.lock().unwrap().is_empty());
879 }
880
881 #[test]
882 fn test_closure_analyzer_singleton_behavior() {
883 let analyzer1 = ClosureAnalyzer::new();
885 let analyzer2 = ClosureAnalyzer::new();
886
887 assert!(analyzer1.closures.lock().unwrap().is_empty());
889 assert!(analyzer2.closures.lock().unwrap().is_empty());
890 }
891
892 #[test]
893 fn test_register_closure() {
894 let analyzer = ClosureAnalyzer::new();
895 let captures = vec![
896 CaptureInfo {
897 var_name: "x".to_string(),
898 var_ptr: 0x1000,
899 mode: CaptureMode::ByValue,
900 var_type: "i32".to_string(),
901 size: 4,
902 lifetime_bound: None,
903 },
904 CaptureInfo {
905 var_name: "y".to_string(),
906 var_ptr: 0x2000,
907 mode: CaptureMode::ByReference,
908 var_type: "&str".to_string(),
909 size: 8,
910 lifetime_bound: Some("'a".to_string()),
911 },
912 ];
913
914 analyzer.register_closure(0x5000, captures.clone());
915
916 let closures = analyzer.closures.lock().unwrap();
918 assert!(closures.contains_key(&0x5000));
919
920 let closure_info = &closures[&0x5000];
921 assert_eq!(closure_info.ptr, 0x5000);
922 assert_eq!(closure_info.captures.len(), 2);
923 assert_eq!(closure_info.memory_footprint.capture_count, 2);
924 assert_eq!(closure_info.memory_footprint.by_value_count, 1);
925 assert_eq!(closure_info.memory_footprint.by_ref_count, 1);
926
927 let events = analyzer.capture_events.lock().unwrap();
929 assert_eq!(events.len(), 2);
930 assert!(events.iter().all(|e| e.closure_ptr == 0x5000));
931 assert!(events
932 .iter()
933 .all(|e| e.event_type == CaptureEventType::Captured));
934 }
935
936 #[test]
937 fn test_track_closure_drop() {
938 let analyzer = ClosureAnalyzer::new();
939 let captures = vec![CaptureInfo {
940 var_name: "data".to_string(),
941 var_ptr: 0x3000,
942 mode: CaptureMode::ByValue,
943 var_type: "Vec<i32>".to_string(),
944 size: 24,
945 lifetime_bound: None,
946 }];
947
948 analyzer.register_closure(0x6000, captures);
949
950 assert!(analyzer.closures.lock().unwrap().contains_key(&0x6000));
952
953 analyzer.track_closure_drop(0x6000);
955
956 assert!(!analyzer.closures.lock().unwrap().contains_key(&0x6000));
958
959 let events = analyzer.capture_events.lock().unwrap();
961 let release_events: Vec<_> = events
962 .iter()
963 .filter(|e| e.event_type == CaptureEventType::Released)
964 .collect();
965 assert_eq!(release_events.len(), 1);
966 assert_eq!(release_events[0].closure_ptr, 0x6000);
967 }
968
969 #[test]
970 fn test_is_closure_type() {
971 let analyzer = ClosureAnalyzer::new();
972
973 assert!(analyzer.is_closure_type("closure"));
975 assert!(analyzer.is_closure_type("{{closure}}"));
976 assert!(analyzer.is_closure_type("fn()"));
977 assert!(analyzer.is_closure_type("fn(i32) -> bool"));
978 assert!(analyzer.is_closure_type("dyn Fn()"));
979 assert!(analyzer.is_closure_type("impl Fn(i32)"));
980 assert!(analyzer.is_closure_type("some_module::{{closure}}"));
981
982 assert!(!analyzer.is_closure_type("Vec<i32>"));
984 assert!(!analyzer.is_closure_type("String"));
985 assert!(!analyzer.is_closure_type("HashMap<K, V>"));
986 }
987
988 #[test]
989 fn test_estimate_captures_from_size() {
990 let analyzer = ClosureAnalyzer::new();
991
992 assert_eq!(analyzer.estimate_captures_from_size(0), 0);
993 assert_eq!(analyzer.estimate_captures_from_size(8), 0);
994 assert_eq!(analyzer.estimate_captures_from_size(16), 2);
995 assert_eq!(analyzer.estimate_captures_from_size(32), 2);
996 assert_eq!(analyzer.estimate_captures_from_size(64), 8);
997 assert_eq!(analyzer.estimate_captures_from_size(128), 8);
998 assert_eq!(analyzer.estimate_captures_from_size(256), 16);
999 }
1000
1001 #[test]
1002 fn test_classify_closure_type() {
1003 let analyzer = ClosureAnalyzer::new();
1004
1005 assert_eq!(
1006 analyzer.classify_closure_type("dyn FnOnce()"),
1007 ClosureType::FnOnce
1008 );
1009 assert_eq!(
1010 analyzer.classify_closure_type("impl FnMut(i32)"),
1011 ClosureType::FnMut
1012 );
1013 assert_eq!(
1014 analyzer.classify_closure_type("dyn Fn() -> bool"),
1015 ClosureType::Fn
1016 );
1017 assert_eq!(
1018 analyzer.classify_closure_type("{{closure}}"),
1019 ClosureType::Unknown
1020 );
1021 assert_eq!(
1022 analyzer.classify_closure_type("some_closure"),
1023 ClosureType::Unknown
1024 );
1025 }
1026
1027 #[test]
1028 fn test_assess_memory_impact() {
1029 let analyzer = ClosureAnalyzer::new();
1030
1031 assert_eq!(analyzer.assess_memory_impact(0), MemoryImpact::Minimal);
1032 assert_eq!(analyzer.assess_memory_impact(16), MemoryImpact::Minimal);
1033 assert_eq!(analyzer.assess_memory_impact(32), MemoryImpact::Low);
1034 assert_eq!(analyzer.assess_memory_impact(64), MemoryImpact::Low);
1035 assert_eq!(analyzer.assess_memory_impact(128), MemoryImpact::Medium);
1036 assert_eq!(analyzer.assess_memory_impact(256), MemoryImpact::Medium);
1037 assert_eq!(analyzer.assess_memory_impact(512), MemoryImpact::High);
1038 assert_eq!(analyzer.assess_memory_impact(1024), MemoryImpact::High);
1039 assert_eq!(analyzer.assess_memory_impact(2048), MemoryImpact::VeryHigh);
1040 }
1041
1042 #[test]
1043 fn test_is_heap_allocated_type() {
1044 let analyzer = ClosureAnalyzer::new();
1045
1046 assert!(analyzer.is_heap_allocated_type("Vec<i32>"));
1048 assert!(analyzer.is_heap_allocated_type("String"));
1049 assert!(analyzer.is_heap_allocated_type("HashMap<K, V>"));
1050 assert!(analyzer.is_heap_allocated_type("Box<dyn Trait>"));
1051 assert!(analyzer.is_heap_allocated_type("Arc<Mutex<T>>"));
1052 assert!(analyzer.is_heap_allocated_type("Rc<RefCell<T>>"));
1053
1054 assert!(!analyzer.is_heap_allocated_type("i32"));
1056 assert!(!analyzer.is_heap_allocated_type("&str"));
1057 assert!(!analyzer.is_heap_allocated_type("bool"));
1058 assert!(!analyzer.is_heap_allocated_type("[i32; 10]"));
1059 }
1060
1061 #[test]
1062 fn test_calculate_closure_footprint() {
1063 let analyzer = ClosureAnalyzer::new();
1064 let captures = vec![
1065 CaptureInfo {
1066 var_name: "a".to_string(),
1067 var_ptr: 0x1000,
1068 mode: CaptureMode::ByValue,
1069 var_type: "i32".to_string(),
1070 size: 4,
1071 lifetime_bound: None,
1072 },
1073 CaptureInfo {
1074 var_name: "b".to_string(),
1075 var_ptr: 0x2000,
1076 mode: CaptureMode::ByReference,
1077 var_type: "&str".to_string(),
1078 size: 8,
1079 lifetime_bound: None,
1080 },
1081 CaptureInfo {
1082 var_name: "c".to_string(),
1083 var_ptr: 0x3000,
1084 mode: CaptureMode::ByMutableReference,
1085 var_type: "&mut Vec<i32>".to_string(),
1086 size: 8,
1087 lifetime_bound: None,
1088 },
1089 ];
1090
1091 let footprint = analyzer.calculate_closure_footprint(&captures);
1092
1093 assert_eq!(footprint.total_size, 20);
1094 assert_eq!(footprint.capture_count, 3);
1095 assert_eq!(footprint.by_value_count, 1);
1096 assert_eq!(footprint.by_ref_count, 1);
1097 assert_eq!(footprint.by_mut_ref_count, 1);
1098 assert_eq!(footprint.estimated_heap_usage, 0); }
1100
1101 #[test]
1102 fn test_analyze_optimization_potential() {
1103 let analyzer = ClosureAnalyzer::new();
1104
1105 let large_captures = vec![CaptureInfo {
1107 var_name: "large_data".to_string(),
1108 var_ptr: 0x1000,
1109 mode: CaptureMode::ByValue,
1110 var_type: "[u8; 1024]".to_string(),
1111 size: 1024,
1112 lifetime_bound: None,
1113 }];
1114
1115 let potential = analyzer.analyze_optimization_potential(&large_captures);
1116 assert_eq!(potential.level, OptimizationLevel::High);
1117 assert_eq!(potential.potential_savings, 1024);
1118 assert!(!potential.suggestions.is_empty());
1119
1120 let small_captures = vec![CaptureInfo {
1122 var_name: "x".to_string(),
1123 var_ptr: 0x1000,
1124 mode: CaptureMode::ByValue,
1125 var_type: "i32".to_string(),
1126 size: 4,
1127 lifetime_bound: None,
1128 }];
1129
1130 let potential = analyzer.analyze_optimization_potential(&small_captures);
1131 assert_eq!(potential.level, OptimizationLevel::None);
1132 assert_eq!(potential.potential_savings, 0);
1133 }
1134
1135 #[test]
1136 fn test_analyze_closure_patterns() {
1137 let analyzer = ClosureAnalyzer::new();
1138
1139 let mut alloc1 = AllocationInfo::new(0x1000, 32);
1141 alloc1.type_name = Some("{{closure}}".to_string());
1142
1143 let mut alloc2 = AllocationInfo::new(0x2000, 64);
1144 alloc2.type_name = Some("dyn FnOnce()".to_string());
1145
1146 let mut alloc3 = AllocationInfo::new(0x3000, 16);
1147 alloc3.type_name = Some("Vec<i32>".to_string()); let allocations = vec![alloc1, alloc2, alloc3];
1150
1151 let report = analyzer.analyze_closure_patterns(&allocations);
1152
1153 assert_eq!(report.detected_closures.len(), 2);
1155
1156 let first_closure = &report.detected_closures[0];
1158 assert_eq!(first_closure.ptr, 0x1000);
1159 assert_eq!(first_closure.size, 32);
1160 assert_eq!(first_closure.estimated_captures, 2); assert_eq!(first_closure.closure_type, ClosureType::Unknown);
1162 assert_eq!(first_closure.memory_impact, MemoryImpact::Low);
1163
1164 let second_closure = &report.detected_closures[1];
1166 assert_eq!(second_closure.ptr, 0x2000);
1167 assert_eq!(second_closure.size, 64);
1168 assert_eq!(second_closure.estimated_captures, 8); assert_eq!(second_closure.closure_type, ClosureType::FnOnce);
1170 assert_eq!(second_closure.memory_impact, MemoryImpact::Low);
1171
1172 assert!(report.analysis_timestamp > 0);
1174 }
1175
1176 #[test]
1177 fn test_lifetime_graph() {
1178 let mut graph = LifetimeGraph::new();
1179
1180 let captures = vec![
1181 CaptureInfo {
1182 var_name: "x".to_string(),
1183 var_ptr: 0x1000,
1184 mode: CaptureMode::ByValue,
1185 var_type: "i32".to_string(),
1186 size: 4,
1187 lifetime_bound: None,
1188 },
1189 CaptureInfo {
1190 var_name: "y".to_string(),
1191 var_ptr: 0x2000,
1192 mode: CaptureMode::ByReference,
1193 var_type: "&str".to_string(),
1194 size: 8,
1195 lifetime_bound: None,
1196 },
1197 ];
1198
1199 graph.add_closure_relationships(0x5000, &captures);
1200
1201 assert!(graph.relationships.contains_key(&0x5000));
1203 let relationships = &graph.relationships[&0x5000];
1204 assert_eq!(relationships.len(), 2);
1205
1206 assert_eq!(
1208 relationships[0].relationship_type,
1209 RelationshipType::Ownership
1210 );
1211 assert_eq!(
1212 relationships[1].relationship_type,
1213 RelationshipType::SharedBorrow
1214 );
1215
1216 graph.remove_closure(0x5000);
1218 assert!(!graph.relationships.contains_key(&0x5000));
1219 }
1220
1221 #[test]
1222 fn test_lifetime_analysis() {
1223 let mut graph = LifetimeGraph::new();
1224
1225 let mixed_captures = vec![
1227 CaptureInfo {
1228 var_name: "owned".to_string(),
1229 var_ptr: 0x1000,
1230 mode: CaptureMode::ByValue,
1231 var_type: "String".to_string(),
1232 size: 24,
1233 lifetime_bound: None,
1234 },
1235 CaptureInfo {
1236 var_name: "borrowed".to_string(),
1237 var_ptr: 0x2000,
1238 mode: CaptureMode::ByReference,
1239 var_type: "&i32".to_string(),
1240 size: 8,
1241 lifetime_bound: None,
1242 },
1243 ];
1244
1245 graph.add_closure_relationships(0x5000, &mixed_captures);
1246
1247 let analysis = graph.analyze_lifetimes();
1248
1249 assert_eq!(analysis.total_relationships, 1);
1250 assert_eq!(analysis.potential_issues.len(), 1);
1251
1252 let issue = &analysis.potential_issues[0];
1253 assert_eq!(issue.closure_ptr, 0x5000);
1254 assert_eq!(issue.issue_type, LifetimeIssueType::MixedCaptureMode);
1255 assert_eq!(issue.severity, IssueSeverity::Medium);
1256 }
1257
1258 #[test]
1259 fn test_lifetime_analysis_many_captures() {
1260 let mut graph = LifetimeGraph::new();
1261
1262 let many_captures: Vec<CaptureInfo> = (0..8)
1264 .map(|i| CaptureInfo {
1265 var_name: format!("var_{i}"),
1266 var_ptr: 0x1000 + i * 8,
1267 mode: CaptureMode::ByValue,
1268 var_type: "i32".to_string(),
1269 size: 4,
1270 lifetime_bound: None,
1271 })
1272 .collect();
1273
1274 graph.add_closure_relationships(0x6000, &many_captures);
1275
1276 let analysis = graph.analyze_lifetimes();
1277
1278 assert_eq!(analysis.total_relationships, 1);
1279 assert_eq!(analysis.lifetime_patterns.len(), 1);
1280
1281 let pattern = &analysis.lifetime_patterns[0];
1282 assert_eq!(pattern.pattern_type, LifetimePatternType::ManyCaptures);
1283 assert_eq!(pattern.impact, PatternImpact::Medium);
1284 }
1285
1286 #[test]
1287 fn test_capture_mode_variants() {
1288 let modes = vec![
1289 CaptureMode::ByValue,
1290 CaptureMode::ByReference,
1291 CaptureMode::ByMutableReference,
1292 ];
1293
1294 for mode in modes {
1295 assert!(!format!("{mode:?}").is_empty());
1296 }
1297 }
1298
1299 #[test]
1300 fn test_optimization_level_variants() {
1301 let levels = vec![
1302 OptimizationLevel::None,
1303 OptimizationLevel::Low,
1304 OptimizationLevel::Medium,
1305 OptimizationLevel::High,
1306 ];
1307
1308 for level in levels {
1309 assert!(!format!("{level:?}").is_empty());
1310 }
1311 }
1312
1313 #[test]
1314 fn test_closure_type_variants() {
1315 let types = vec![
1316 ClosureType::Fn,
1317 ClosureType::FnMut,
1318 ClosureType::FnOnce,
1319 ClosureType::Unknown,
1320 ];
1321
1322 for closure_type in types {
1323 assert!(!format!("{closure_type:?}").is_empty());
1324 }
1325 }
1326
1327 #[test]
1328 fn test_memory_impact_variants() {
1329 let impacts = vec![
1330 MemoryImpact::Minimal,
1331 MemoryImpact::Low,
1332 MemoryImpact::Medium,
1333 MemoryImpact::High,
1334 MemoryImpact::VeryHigh,
1335 ];
1336
1337 for impact in impacts {
1338 assert!(!format!("{impact:?}").is_empty());
1339 }
1340 }
1341
1342 #[test]
1343 fn test_capture_statistics_default() {
1344 let stats = CaptureStatistics::default();
1345
1346 assert_eq!(stats.total_closures, 0);
1347 assert_eq!(stats.total_captures, 0);
1348 assert_eq!(stats.avg_captures_per_closure, 0.0);
1349 assert_eq!(stats.total_memory_usage, 0);
1350 assert!(stats.captures_by_mode.is_empty());
1351 assert!(stats.captures_by_type.is_empty());
1352 }
1353
1354 #[test]
1355 fn test_optimization_category_variants() {
1356 let categories = vec![
1357 OptimizationCategory::Memory,
1358 OptimizationCategory::Performance,
1359 OptimizationCategory::Lifetime,
1360 ];
1361
1362 for category in categories {
1363 assert!(!format!("{category:?}").is_empty());
1364 }
1365 }
1366
1367 #[test]
1368 fn test_suggestion_priority_variants() {
1369 let priorities = vec![
1370 SuggestionPriority::Low,
1371 SuggestionPriority::Medium,
1372 SuggestionPriority::High,
1373 SuggestionPriority::Critical,
1374 ];
1375
1376 for priority in priorities {
1377 assert!(!format!("{priority:?}").is_empty());
1378 }
1379 }
1380
1381 #[test]
1382 fn test_relationship_type_variants() {
1383 let types = vec![
1384 RelationshipType::Ownership,
1385 RelationshipType::SharedBorrow,
1386 RelationshipType::ExclusiveBorrow,
1387 ];
1388
1389 for rel_type in types {
1390 assert!(!format!("{rel_type:?}").is_empty());
1391 }
1392 }
1393
1394 #[test]
1395 fn test_lifetime_analysis_default() {
1396 let analysis = LifetimeAnalysis::default();
1397
1398 assert_eq!(analysis.total_relationships, 0);
1399 assert!(analysis.potential_issues.is_empty());
1400 assert!(analysis.lifetime_patterns.is_empty());
1401 }
1402
1403 #[test]
1404 fn test_lifetime_issue_type_variants() {
1405 let types = vec![
1406 LifetimeIssueType::MixedCaptureMode,
1407 LifetimeIssueType::PotentialDanglingReference,
1408 LifetimeIssueType::UnnecessaryCapture,
1409 ];
1410
1411 for issue_type in types {
1412 assert!(!format!("{issue_type:?}").is_empty());
1413 }
1414 }
1415
1416 #[test]
1417 fn test_issue_severity_variants() {
1418 let severities = vec![
1419 IssueSeverity::Low,
1420 IssueSeverity::Medium,
1421 IssueSeverity::High,
1422 IssueSeverity::Critical,
1423 ];
1424
1425 for severity in severities {
1426 assert!(!format!("{severity:?}").is_empty());
1427 }
1428 }
1429
1430 #[test]
1431 fn test_lifetime_pattern_type_variants() {
1432 let types = vec![
1433 LifetimePatternType::ManyCaptures,
1434 LifetimePatternType::LongLivedClosure,
1435 LifetimePatternType::FrequentCreation,
1436 ];
1437
1438 for pattern_type in types {
1439 assert!(!format!("{pattern_type:?}").is_empty());
1440 }
1441 }
1442
1443 #[test]
1444 fn test_pattern_impact_variants() {
1445 let impacts = vec![
1446 PatternImpact::Low,
1447 PatternImpact::Medium,
1448 PatternImpact::High,
1449 ];
1450
1451 for impact in impacts {
1452 assert!(!format!("{impact:?}").is_empty());
1453 }
1454 }
1455
1456 #[test]
1457 fn test_capture_event_type_variants() {
1458 let types = vec![CaptureEventType::Captured, CaptureEventType::Released];
1459
1460 for event_type in types {
1461 assert!(!format!("{event_type:?}").is_empty());
1462 }
1463 }
1464
1465 #[test]
1466 fn test_current_timestamp() {
1467 let timestamp1 = current_timestamp();
1468 std::thread::sleep(std::time::Duration::from_millis(1));
1469 let timestamp2 = current_timestamp();
1470
1471 assert!(timestamp2 > timestamp1);
1472 assert!(timestamp1 > 0);
1473 }
1474
1475 #[test]
1476 fn test_capture_call_site() {
1477 let call_site = capture_call_site();
1478 assert!(!call_site.is_empty());
1479 assert_eq!(call_site, "<call_site_placeholder>");
1480 }
1481
1482 #[test]
1483 fn test_complex_closure_analysis_scenario() {
1484 let analyzer = ClosureAnalyzer::new();
1485
1486 let small_captures = vec![CaptureInfo {
1488 var_name: "x".to_string(),
1489 var_ptr: 0x1000,
1490 mode: CaptureMode::ByValue,
1491 var_type: "i32".to_string(),
1492 size: 4,
1493 lifetime_bound: None,
1494 }];
1495
1496 let large_captures = vec![CaptureInfo {
1497 var_name: "data".to_string(),
1498 var_ptr: 0x2000,
1499 mode: CaptureMode::ByValue,
1500 var_type: "Vec<u8>".to_string(),
1501 size: 1024,
1502 lifetime_bound: None,
1503 }];
1504
1505 analyzer.register_closure(0x5000, small_captures);
1506 analyzer.register_closure(0x6000, large_captures);
1507
1508 let mut alloc1 = AllocationInfo::new(0x5000, 8);
1510 alloc1.type_name = Some("{{closure}}".to_string());
1511
1512 let mut alloc2 = AllocationInfo::new(0x6000, 1024);
1513 alloc2.type_name = Some("dyn FnOnce()".to_string());
1514
1515 let allocations = vec![alloc1, alloc2];
1516
1517 let report = analyzer.analyze_closure_patterns(&allocations);
1518
1519 assert_eq!(report.detected_closures.len(), 2);
1521 assert!(report.capture_statistics.total_closures > 0);
1522 assert!(!report.optimization_suggestions.is_empty());
1523 assert!(report.analysis_timestamp > 0);
1524
1525 let memory_suggestions: Vec<_> = report
1527 .optimization_suggestions
1528 .iter()
1529 .filter(|s| s.category == OptimizationCategory::Memory)
1530 .collect();
1531 assert!(!memory_suggestions.is_empty());
1532 }
1533
1534 #[test]
1535 fn test_thread_safety() {
1536 use std::sync::Arc;
1537 use std::thread;
1538
1539 let analyzer = Arc::new(ClosureAnalyzer::new());
1540 let mut handles = vec![];
1541
1542 for i in 0..4 {
1544 let analyzer_clone = analyzer.clone();
1545 let handle = thread::spawn(move || {
1546 let captures = vec![CaptureInfo {
1547 var_name: format!("var_{i}"),
1548 var_ptr: 0x1000 + i * 8,
1549 mode: CaptureMode::ByValue,
1550 var_type: "i32".to_string(),
1551 size: 4,
1552 lifetime_bound: None,
1553 }];
1554
1555 analyzer_clone.register_closure(0x5000 + i, captures);
1556 analyzer_clone.track_closure_drop(0x5000 + i);
1557 });
1558 handles.push(handle);
1559 }
1560
1561 for handle in handles {
1562 handle.join().unwrap();
1563 }
1564
1565 assert!(analyzer.closures.lock().unwrap().is_empty());
1567
1568 let events = analyzer.capture_events.lock().unwrap();
1570 assert_eq!(events.len(), 8); }
1572}